2 * io.c: shared file reading, writing, and probing code.
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
32 #ifndef APR_STATUS_IS_EPERM
35 #define APR_STATUS_IS_EPERM(s) ((s) == EPERM)
37 #define APR_STATUS_IS_EPERM(s) (0)
42 #include <apr_pools.h>
43 #include <apr_file_io.h>
44 #include <apr_file_info.h>
45 #include <apr_general.h>
46 #include <apr_strings.h>
47 #include <apr_portable.h>
51 #include <arch/win32/apr_arch_file_io.h>
55 #include "svn_types.h"
56 #include "svn_dirent_uri.h"
58 #include "svn_string.h"
59 #include "svn_error.h"
61 #include "svn_pools.h"
63 #include "svn_config.h"
64 #include "svn_private_config.h"
65 #include "svn_ctype.h"
67 #include "private/svn_atomic.h"
68 #include "private/svn_io_private.h"
70 #define SVN_SLEEP_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS"
73 Windows is 'aided' by a number of types of applications that
74 follow other applications around and open up files they have
75 changed for various reasons (the most intrusive are virus
76 scanners). So, if one of these other apps has glommed onto
77 our file we may get an 'access denied' error.
79 This retry loop does not completely solve the problem (who
80 knows how long the other app is going to hold onto it for), but
81 goes a long way towards minimizing it. It is not an infinite
82 loop because there might really be an error.
84 Another reason for retrying delete operations on Windows
85 is that they are asynchronous -- the file or directory is not
86 actually deleted until the last handle to it is closed. The
87 retry loop cannot completely solve this problem either, but can
90 #define RETRY_MAX_ATTEMPTS 100
91 #define RETRY_INITIAL_SLEEP 1000
92 #define RETRY_MAX_SLEEP 128000
94 #define RETRY_LOOP(err, expr, retry_test, sleep_test) \
97 apr_status_t os_err = APR_TO_OS_ERROR(err); \
98 int sleep_count = RETRY_INITIAL_SLEEP; \
101 retries < RETRY_MAX_ATTEMPTS && (retry_test); \
102 os_err = APR_TO_OS_ERROR(err)) \
107 apr_sleep(sleep_count); \
108 if (sleep_count < RETRY_MAX_SLEEP) \
116 #if defined(EDEADLK) && APR_HAS_THREADS
117 #define FILE_LOCK_RETRY_LOOP(err, expr) \
120 (APR_STATUS_IS_EINTR(err) || os_err == EDEADLK), \
121 (!APR_STATUS_IS_EINTR(err)))
123 #define FILE_LOCK_RETRY_LOOP(err, expr) \
126 (APR_STATUS_IS_EINTR(err)), \
130 #ifndef WIN32_RETRY_LOOP
131 #if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP)
132 #define WIN32_RETRY_LOOP(err, expr) \
133 RETRY_LOOP(err, expr, (os_err == ERROR_ACCESS_DENIED \
134 || os_err == ERROR_SHARING_VIOLATION \
135 || os_err == ERROR_DIR_NOT_EMPTY), \
138 #define WIN32_RETRY_LOOP(err, expr) ((void)0)
142 /* Forward declaration */
144 dir_is_empty(const char *dir, apr_pool_t *pool);
145 static APR_INLINE svn_error_t *
146 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
147 const char *msg, const char *msg_no_name,
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 */
153 cstring_to_utf8(const char **path_utf8,
154 const char *path_apr,
157 #if defined(WIN32) || defined(DARWIN)
158 *path_utf8 = path_apr;
161 return svn_path_cstring_to_utf8(path_utf8, path_apr, pool);
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 */
168 cstring_from_utf8(const char **path_apr,
169 const char *path_utf8,
172 #if defined(WIN32) || defined(DARWIN)
173 *path_apr = path_utf8;
176 return svn_path_cstring_from_utf8(path_apr, path_utf8, pool);
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.
185 * This is different from svn_io_file_name_get in that it uses a different
186 * signature style and will never fail.
189 try_utf8_from_internal_style(const char *path, apr_pool_t *pool)
192 const char *path_utf8;
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.
201 error = cstring_to_utf8(&path_utf8, path, pool);
204 /* fallback to best representation we have */
206 svn_error_clear(error);
210 /* Toggle (back-)slashes etc. as necessary.
212 return svn_dirent_local_style(path_utf8, pool);
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.
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.
228 * If there is any other error, just return that error directly.
230 * If there is any error, the effect on *NAME_P is undefined.
232 * *NAME_P and NAME may refer to the same storage.
235 entry_name_to_utf8(const char **name_p,
240 #if defined(WIN32) || defined(DARWIN)
241 *name_p = apr_pstrdup(pool, name);
244 svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool);
245 if (err && err->apr_err == APR_EINVAL)
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));
259 map_apr_finfo_to_node_kind(svn_node_kind_t *kind,
260 svn_boolean_t *is_special,
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)
272 *kind = svn_node_file;
275 *kind = svn_node_unknown;
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. */
282 io_check_path(const char *path,
283 svn_boolean_t resolve_symlinks,
284 svn_boolean_t *is_special_p,
285 svn_node_kind_t *kind,
290 apr_status_t apr_err;
291 const char *path_apr;
292 svn_boolean_t is_special = FALSE;
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));
301 flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK);
302 apr_err = apr_stat(&finfo, path_apr, flags, pool);
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;
309 return svn_error_wrap_apr(apr_err, _("Can't check path '%s'"),
310 svn_dirent_local_style(path, pool));
312 map_apr_finfo_to_node_kind(kind, &is_special, &finfo);
314 *is_special_p = is_special;
320 /* Wrapper for apr_file_open(), taking an APR-encoded filename. */
322 file_open(apr_file_t **f,
323 const char *fname_apr,
325 apr_fileperms_t perm,
326 svn_boolean_t retry_on_failure,
329 apr_status_t status = apr_file_open(f, fname_apr, flag, perm, pool);
331 if (retry_on_failure)
333 WIN32_RETRY_LOOP(status, apr_file_open(f, fname_apr, flag, perm, pool));
340 svn_io_check_resolved_path(const char *path,
341 svn_node_kind_t *kind,
344 svn_boolean_t ignored;
345 return io_check_path(path, TRUE, &ignored, kind, pool);
349 svn_io_check_path(const char *path,
350 svn_node_kind_t *kind,
353 svn_boolean_t ignored;
354 return io_check_path(path, FALSE, &ignored, kind, pool);
358 svn_io_check_special_path(const char *path,
359 svn_node_kind_t *kind,
360 svn_boolean_t *is_special,
363 return io_check_path(path, FALSE, is_special, kind, pool);
366 struct temp_file_cleanup_s
369 /* The (APR-encoded) full path of the file to be removed, or NULL if
371 const char *fname_apr;
376 temp_file_plain_cleanup_handler(void *baton)
378 struct temp_file_cleanup_s *b = baton;
379 apr_status_t apr_err = APR_SUCCESS;
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));
392 temp_file_child_cleanup_handler(void *baton)
394 struct temp_file_cleanup_s *b = baton;
396 apr_pool_cleanup_kill(b->pool, b,
397 temp_file_plain_cleanup_handler);
404 svn_io_open_uniquely_named(apr_file_t **file,
405 const char **unique_path,
407 const char *filename,
409 svn_io_file_del_t delete_when,
410 apr_pool_t *result_pool,
411 apr_pool_t *scratch_pool)
415 struct temp_file_cleanup_s *baton = NULL;
417 /* At the beginning, we don't know whether unique_path will need
419 svn_boolean_t needs_utf8_conversion = TRUE;
421 SVN_ERR_ASSERT(file || unique_path);
424 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
425 if (filename == NULL)
426 filename = "tempfile";
430 path = svn_dirent_join(dirpath, filename, scratch_pool);
432 if (delete_when == svn_io_file_del_on_pool_cleanup)
434 baton = apr_palloc(result_pool, sizeof(*baton));
436 baton->pool = result_pool;
437 baton->fname_apr = NULL;
439 /* Because cleanups are run LIFO, we need to make sure to register
440 our cleanup before the apr_file_close cleanup:
442 On Windows, you can't remove an open file.
444 apr_pool_cleanup_register(result_pool, baton,
445 temp_file_plain_cleanup_handler,
446 temp_file_child_cleanup_handler);
449 for (i = 1; i <= 99999; i++)
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);
458 if (delete_when == svn_io_file_del_on_close)
459 flag |= APR_DELONCLOSE;
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.)
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 :-). */
473 unique_name = apr_psprintf(scratch_pool, "%s%s", path, suffix);
475 unique_name = apr_psprintf(scratch_pool, "%s.%u%s", path, i, suffix);
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)
483 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name,
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);
495 unique_name_apr = unique_name;
497 apr_err = file_open(&try_file, unique_name_apr, flag,
498 APR_OS_DEFAULT, FALSE, result_pool);
500 if (APR_STATUS_IS_EEXIST(apr_err))
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))
510 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
511 APR_FINFO_TYPE, scratch_pool);
513 if (!apr_err_2 && finfo.filetype == APR_DIR)
517 apr_err_2 = APR_TO_OS_ERROR(apr_err);
519 if (apr_err_2 == ERROR_ACCESS_DENIED ||
520 apr_err_2 == ERROR_SHARING_VIOLATION)
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 */
530 /* Else fall through and return the original error. */
537 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
538 svn_dirent_local_style(unique_name,
543 if (delete_when == svn_io_file_del_on_pool_cleanup)
544 baton->fname_apr = apr_pstrdup(result_pool, unique_name_apr);
549 apr_file_close(try_file);
551 *unique_path = apr_pstrdup(result_pool, unique_name);
561 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
563 _("Unable to make name for '%s'"),
564 svn_dirent_local_style(path, scratch_pool));
568 svn_io_create_unique_link(const char **unique_name_p,
576 const char *unique_name;
577 const char *unique_name_apr;
578 const char *dest_apr;
581 SVN_ERR(cstring_from_utf8(&dest_apr, dest, pool));
582 for (i = 1; i <= 99999; i++)
584 apr_status_t apr_err;
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.)
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 :-). */
598 unique_name = apr_psprintf(pool, "%s%s", path, suffix);
600 unique_name = apr_psprintf(pool, "%s.%u%s", path, i, suffix);
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));
608 rv = symlink(dest_apr, unique_name_apr);
609 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
611 apr_err = apr_get_os_error();
613 if (rv == -1 && APR_STATUS_IS_EEXIST(apr_err))
615 else if (rv == -1 && apr_err)
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))
623 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
624 APR_FINFO_TYPE, pool);
627 && (finfo.filetype == APR_DIR))
630 /* Else ignore apr_err_2; better to fall through and
631 return the original error. */
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));
641 *unique_name_p = unique_name;
646 *unique_name_p = NULL;
647 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
649 _("Unable to make name for '%s'"),
650 svn_dirent_local_style(path, pool));
652 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
653 _("Symbolic links are not supported on this "
659 svn_io_read_link(svn_string_t **dest,
664 svn_string_t dest_apr;
665 const char *path_apr;
669 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
671 rv = readlink(path_apr, buf, sizeof(buf) - 1);
672 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
675 return svn_error_wrap_apr(apr_get_os_error(),
676 _("Can't read contents of link"));
682 /* ### Cast needed, one of these interfaces is wrong */
683 return svn_utf_string_to_utf8((const svn_string_t **)dest, &dest_apr, pool);
685 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
686 _("Symbolic links are not supported on this "
693 svn_io_copy_link(const char *src,
699 svn_string_t *link_dest;
702 /* Notice what the link is pointing at... */
703 SVN_ERR(svn_io_read_link(&link_dest, src, pool));
705 /* Make a tmp-link pointing at the same thing. */
706 SVN_ERR(svn_io_create_unique_link(&dst_tmp, dst, link_dest->data,
709 /* Move the tmp-link to link. */
710 return svn_io_file_rename(dst_tmp, dst, pool);
713 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
714 _("Symbolic links are not supported on this "
719 /* Temporary directory name cache for svn_io_temp_dir() */
720 static volatile svn_atomic_t temp_dir_init_state = 0;
721 static const char *temp_dir;
723 /* Helper function to initialize temp dir. Passed to svn_atomic__init_once */
725 init_temp_dir(void *baton, apr_pool_t *scratch_pool)
727 /* Global pool for the temp path */
728 apr_pool_t *global_pool = svn_pool_create(NULL);
731 apr_status_t apr_err = apr_temp_dir_get(&dir, scratch_pool);
734 return svn_error_wrap_apr(apr_err, _("Can't find a temporary directory"));
736 SVN_ERR(cstring_to_utf8(&dir, dir, scratch_pool));
738 dir = svn_dirent_internal_style(dir, scratch_pool);
740 SVN_ERR(svn_dirent_get_absolute(&temp_dir, dir, global_pool));
747 svn_io_temp_dir(const char **dir,
750 SVN_ERR(svn_atomic__init_once(&temp_dir_init_state,
751 init_temp_dir, NULL, pool));
753 *dir = apr_pstrdup(pool, temp_dir);
761 /*** Creating, copying and appending files. ***/
763 /* Transfer the contents of FROM_FILE to TO_FILE, using POOL for temporary
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.
773 copy_contents(apr_file_t *from_file,
777 /* Copy bytes till the cows come home. */
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;
786 read_err = apr_file_read(from_file, buf, &bytes_this_time);
787 if (read_err && !APR_STATUS_IS_EOF(read_err))
793 write_err = apr_file_write_full(to_file, buf, bytes_this_time, NULL);
799 if (read_err && APR_STATUS_IS_EOF(read_err))
801 /* Return the results of this close: an error, or success. */
810 svn_io_copy_file(const char *src,
812 svn_boolean_t copy_perms,
815 apr_file_t *from_file, *to_file;
816 apr_status_t apr_err;
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.
824 ### Note: specifically, this can happen during a switch when the desired
825 ### permissions for a file change from one branch to another. See
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.
833 #ifdef CHECK_FOR_SAME_FILE
834 if (strcmp(src, dst) == 0)
838 SVN_ERR(svn_io_file_open(&from_file, src, APR_READ,
839 APR_OS_DEFAULT, pool));
841 /* For atomicity, we copy to a tmp file and then rename the tmp
842 file over the real destination. */
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));
848 apr_err = copy_contents(from_file, to_file, pool);
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));
859 err = svn_error_compose_create(err,
860 svn_io_file_close(from_file, pool));
862 err = svn_error_compose_create(err,
863 svn_io_file_close(to_file, pool));
867 return svn_error_compose_create(
869 svn_io_remove_file2(dst_tmp, TRUE, pool));
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. */
877 SVN_ERR(svn_io_copy_perms(src, dst_tmp, pool));
879 return svn_error_trace(svn_io_file_rename(dst_tmp, dst, pool));
882 #if !defined(WIN32) && !defined(__OS2__)
883 /* Wrapper for apr_file_perms_set(), taking a UTF8-encoded filename. */
885 file_perms_set(const char *fname, apr_fileperms_t perms,
888 const char *fname_apr;
891 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
893 status = apr_file_perms_set(fname_apr, perms);
895 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
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.
906 file_perms_set2(apr_file_t* file, apr_fileperms_t perms, apr_pool_t *pool)
908 const char *fname_apr;
911 status = apr_file_name_get(&fname_apr, file);
913 return svn_error_wrap_apr(status, _("Can't get file name"));
915 status = apr_file_perms_set(fname_apr, perms);
917 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
918 try_utf8_from_internal_style(fname_apr, pool));
923 #endif /* !WIN32 && !__OS2__ */
926 svn_io_copy_perms(const char *src,
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. */
936 #if !defined(WIN32) && !defined(__OS2__)
939 svn_node_kind_t kind;
940 svn_boolean_t is_special;
943 /* If DST is a symlink, don't bother copying permissions. */
944 SVN_ERR(svn_io_check_special_path(dst, &kind, &is_special, pool));
948 SVN_ERR(svn_io_stat(&finfo, src, APR_FINFO_PROT, pool));
949 err = file_perms_set(dst, finfo.protection, pool);
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
957 if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
958 APR_STATUS_IS_ENOTIMPL(err->apr_err))
959 svn_error_clear(err);
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);
969 #endif /* !WIN32 && !__OS2__ */
976 svn_io_append_file(const char *src, const char *dst, apr_pool_t *pool)
978 apr_status_t apr_err;
979 const char *src_apr, *dst_apr;
981 SVN_ERR(cstring_from_utf8(&src_apr, src, pool));
982 SVN_ERR(cstring_from_utf8(&dst_apr, dst, pool));
984 apr_err = apr_file_append(src_apr, dst_apr, APR_OS_DEFAULT, pool);
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));
995 svn_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,
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;
1010 /* Make a subpool for recursion */
1011 apr_pool_t *subpool = svn_pool_create(pool);
1013 /* The 'dst_path' is simply dst_parent/dst_basename */
1014 dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
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));
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));
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));
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));
1040 /* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */
1041 SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
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))
1047 if ((this_entry.name[0] == '.')
1048 && ((this_entry.name[1] == '\0')
1049 || ((this_entry.name[1] == '.')
1050 && (this_entry.name[2] == '\0'))))
1056 const char *src_target, *entryname_utf8;
1059 SVN_ERR(cancel_func(cancel_baton));
1061 SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
1063 src_target = svn_dirent_join(src, entryname_utf8, subpool);
1065 if (this_entry.filetype == APR_REG) /* regular file */
1067 const char *dst_target = svn_dirent_join(dst_path,
1070 SVN_ERR(svn_io_copy_file(src_target, dst_target,
1071 copy_perms, subpool));
1073 else if (this_entry.filetype == APR_LNK) /* symlink */
1075 const char *dst_target = svn_dirent_join(dst_path,
1078 SVN_ERR(svn_io_copy_link(src_target, dst_target,
1081 else if (this_entry.filetype == APR_DIR) /* recurse */
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)
1089 SVN_ERR(svn_io_copy_dir_recursively
1098 /* ### support other APR node types someday?? */
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));
1107 status = apr_dir_close(this_dir);
1109 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
1110 svn_dirent_local_style(src, pool));
1112 /* Free any memory used by recursion */
1113 svn_pool_destroy(subpool);
1115 return SVN_NO_ERROR;
1120 svn_io_make_dir_recursively(const char *path, apr_pool_t *pool)
1122 const char *path_apr;
1123 apr_status_t apr_err;
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;
1130 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
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));
1137 return svn_error_wrap_apr(apr_err, _("Can't make directory '%s'"),
1138 svn_dirent_local_style(path, pool));
1140 return SVN_NO_ERROR;
1143 svn_error_t *svn_io_file_create(const char *file,
1144 const char *contents,
1149 svn_error_t *err = SVN_NO_ERROR;
1151 SVN_ERR(svn_io_file_open(&f, file,
1152 (APR_WRITE | APR_CREATE | APR_EXCL),
1155 if (contents && *contents)
1156 err = svn_io_file_write_full(f, contents, strlen(contents),
1160 return svn_error_trace(
1161 svn_error_compose_create(err,
1162 svn_io_file_close(f, pool)));
1165 svn_error_t *svn_io_dir_file_copy(const char *src_path,
1166 const char *dest_path,
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);
1173 return svn_io_copy_file(file_src_path, file_dest_path, TRUE, pool);
1177 /*** Modtime checking. ***/
1180 svn_io_file_affected_time(apr_time_t *apr_time,
1186 SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_MIN | APR_FINFO_LINK, pool));
1188 *apr_time = finfo.mtime;
1190 return SVN_NO_ERROR;
1195 svn_io_set_file_affected_time(apr_time_t apr_time,
1199 apr_status_t status;
1200 const char *native_path;
1202 SVN_ERR(cstring_from_utf8(&native_path, path, pool));
1203 status = apr_file_mtime_set(native_path, apr_time, pool);
1206 return svn_error_wrap_apr(status, _("Can't set access time of '%s'"),
1207 svn_dirent_local_style(path, pool));
1209 return SVN_NO_ERROR;
1214 svn_io_sleep_for_timestamps(const char *path, apr_pool_t *pool)
1216 apr_time_t now, then;
1218 char *sleep_env_var;
1220 sleep_env_var = getenv(SVN_SLEEP_ENV_VAR);
1222 if (sleep_env_var && apr_strnatcasecmp(sleep_env_var, "yes") == 0)
1223 return; /* Allow skipping for testing */
1225 now = apr_time_now();
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);
1230 /* Worst case is waiting one second, so we can use that time to determine
1231 if we can sleep shorter than that */
1236 err = svn_io_stat(&finfo, path, APR_FINFO_MTIME | APR_FINFO_LINK, pool);
1240 svn_error_clear(err); /* Fall back on original behavior */
1242 else if (finfo.mtime % APR_USEC_PER_SEC)
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.
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.
1254 But better to fail once in every thousand cases than every
1255 time, like we did before.
1257 Note for further research on algorithm:
1258 FAT32 has < 1 sec precision on ctime, but 2 sec on mtime.
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. */
1265 /* 10 milliseconds after now. */
1266 #ifndef SVN_HI_RES_SLEEP_MS
1267 #define SVN_HI_RES_SLEEP_MS 10
1269 then = now + apr_time_from_msec(SVN_HI_RES_SLEEP_MS);
1272 /* Remove time taken to do stat() from sleep. */
1273 now = apr_time_now();
1277 return; /* Passing negative values may suspend indefinitely (Windows) */
1279 /* (t < 1000 will be round to 0 in apr) */
1280 if (then - now < 1000)
1283 apr_sleep(then - now);
1288 svn_io_filesizes_different_p(svn_boolean_t *different_p,
1295 apr_status_t status;
1296 const char *file1_apr, *file2_apr;
1298 /* Not using svn_io_stat() because don't want to generate
1299 svn_error_t objects for non-error conditions. */
1301 SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool));
1302 SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool));
1304 /* Stat both files */
1305 status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool);
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;
1316 status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool);
1319 /* See previous comment. */
1320 *different_p = FALSE;
1321 return SVN_NO_ERROR;
1324 /* Examine file sizes */
1325 if (finfo1.size == finfo2.size)
1326 *different_p = FALSE;
1328 *different_p = TRUE;
1330 return SVN_NO_ERROR;
1335 svn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
1336 svn_boolean_t *different_p23,
1337 svn_boolean_t *different_p13,
1341 apr_pool_t *scratch_pool)
1343 apr_finfo_t finfo1, finfo2, finfo3;
1344 apr_status_t status1, status2, status3;
1345 const char *file1_apr, *file2_apr, *file3_apr;
1347 /* Not using svn_io_stat() because don't want to generate
1348 svn_error_t objects for non-error conditions. */
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));
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);
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;
1367 return SVN_NO_ERROR;
1372 svn_io_file_checksum2(svn_checksum_t **checksum,
1374 svn_checksum_kind_t kind,
1377 svn_stream_t *file_stream;
1378 svn_stream_t *checksum_stream;
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,
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);
1394 svn_io_file_checksum(unsigned char digest[],
1398 svn_checksum_t *checksum;
1400 SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool));
1401 memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE);
1403 return SVN_NO_ERROR;
1408 /*** Permissions and modes. ***/
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. */
1416 static svn_error_t *
1417 reown_file(const char *path,
1420 const char *unique_name;
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));
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. */
1437 static svn_error_t *
1438 get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool)
1440 /* the default permissions as read from the temp folder */
1441 static apr_fileperms_t default_perms = 0;
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.
1448 if (default_perms == 0)
1452 const char *fname_base, *fname;
1453 apr_uint32_t randomish;
1456 /* Get the perms for a newly created file to find out what bits
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.
1463 Using svn_io_open_uniquely_named() here because other tempfile
1464 creation functions tweak the permission bits of files they create.
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);
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,
1478 *perms = finfo.protection;
1479 default_perms = finfo.protection;
1482 *perms = default_perms;
1484 return SVN_NO_ERROR;
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. */
1490 static svn_error_t *
1491 merge_default_file_perms(apr_file_t *fd, apr_fileperms_t *perms,
1492 apr_pool_t *scratch_pool)
1495 apr_fileperms_t default_perms;
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));
1500 /* Glom the perms together. */
1501 *perms = default_perms | finfo.protection;
1502 return SVN_NO_ERROR;
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. */
1508 static svn_error_t *
1509 io_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,
1517 apr_status_t status;
1518 const char *path_apr;
1520 apr_fileperms_t perms_to_set;
1522 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
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);
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;
1540 if (finfo.filetype == APR_LNK)
1541 return SVN_NO_ERROR;
1543 perms_to_set = finfo.protection;
1544 if (change_readwrite)
1546 if (enable_write) /* Make read-write. */
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);
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;
1563 if (change_executable)
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;
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;
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;
1590 status = apr_file_perms_set(path_apr, perms_to_set);
1592 return SVN_NO_ERROR;
1594 if (APR_STATUS_IS_EPERM(status))
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
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);
1611 return SVN_NO_ERROR;
1613 if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1614 return SVN_NO_ERROR;
1615 else if (status == APR_ENOTIMPL)
1617 /* At least try to set the attributes. */
1618 apr_fileattrs_t attrs = 0;
1619 apr_fileattrs_t attrs_values = 0;
1621 if (change_readwrite)
1623 attrs = APR_FILE_ATTR_READONLY;
1625 attrs_values = APR_FILE_ATTR_READONLY;
1627 if (change_executable)
1629 attrs = APR_FILE_ATTR_EXECUTABLE;
1631 attrs_values = APR_FILE_ATTR_EXECUTABLE;
1633 status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
1636 return svn_error_wrap_apr(status,
1637 _("Can't change perms of file '%s'"),
1638 svn_dirent_local_style(path, pool));
1640 #endif /* !WIN32 && !__OS2__ */
1643 #if APR_HAS_UNICODE_FS
1644 /* copy of the apr function utf8_to_unicode_path since apr doesn't export this one */
1645 static apr_status_t io_utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen,
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.
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.
1657 * Note that the \\?\ form only works for local drive paths, and
1658 * \\?\UNC\ is needed UNC paths.
1660 apr_size_t srcremains = strlen(srcstr) + 1;
1661 apr_wchar_t *t = retstr;
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.
1672 * Note also this is shorter than MAX_PATH, as directory paths
1673 * are actually limited to 248 characters.
1675 * Note that a utf-8 name can never result in more wide chars
1676 * than the original number of utf-8 narrow chars.
1678 if (srcremains > 248) {
1679 if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
1680 wcscpy (retstr, L"\\\\?\\");
1684 else if ((srcstr[0] == '/' || srcstr[0] == '\\')
1685 && (srcstr[1] == '/' || srcstr[1] == '\\')
1686 && (srcstr[2] != '?')) {
1687 /* Skip the slashes */
1690 wcscpy (retstr, L"\\\\?\\UNC\\");
1696 if (rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen)) {
1697 return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
1700 return APR_ENAMETOOLONG;
1709 static apr_status_t io_win_file_attrs_set(const char *fname,
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 */
1720 #if APR_HAS_UNICODE_FS
1721 apr_wchar_t wfname[APR_PATH_MAX];
1724 #if APR_HAS_UNICODE_FS
1725 IF_WIN_OS_IS_UNICODE
1727 if (rv = io_utf8_to_unicode_path(wfname,
1728 sizeof(wfname) / sizeof(wfname[0]),
1731 flags = GetFileAttributesW(wfname);
1737 flags = GetFileAttributesA(fname);
1741 if (flags == 0xFFFFFFFF)
1742 return apr_get_os_error();
1744 flags &= ~attr_mask;
1745 flags |= (attributes & attr_mask);
1747 #if APR_HAS_UNICODE_FS
1748 IF_WIN_OS_IS_UNICODE
1750 rv = SetFileAttributesW(wfname, flags);
1756 rv = SetFileAttributesA(fname, flags);
1761 return apr_get_os_error();
1769 svn_io_set_file_read_write_carefully(const char *path,
1770 svn_boolean_t enable_write,
1771 svn_boolean_t ignore_enoent,
1775 return svn_io_set_file_read_write(path, ignore_enoent, pool);
1776 return svn_io_set_file_read_only(path, ignore_enoent, pool);
1780 svn_io_set_file_read_only(const char *path,
1781 svn_boolean_t ignore_enoent,
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);
1790 apr_status_t status;
1791 const char *path_apr;
1793 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1795 status = apr_file_attrs_set(path_apr,
1796 APR_FILE_ATTR_READONLY,
1797 APR_FILE_ATTR_READONLY,
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));
1806 return SVN_NO_ERROR;
1812 svn_io_set_file_read_write(const char *path,
1813 svn_boolean_t ignore_enoent,
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);
1822 apr_status_t status;
1823 const char *path_apr;
1825 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1827 status = apr_file_attrs_set(path_apr,
1829 APR_FILE_ATTR_READONLY,
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));
1838 return SVN_NO_ERROR;
1843 svn_io_set_file_executable(const char *path,
1844 svn_boolean_t executable,
1845 svn_boolean_t ignore_enoent,
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);
1854 return SVN_NO_ERROR;
1860 svn_io__is_finfo_read_only(svn_boolean_t *read_only,
1861 apr_finfo_t *file_info,
1864 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1865 apr_status_t apr_err;
1871 apr_err = apr_uid_current(&uid, &gid, pool);
1874 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
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);
1880 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1881 *read_only = !(file_info->protection & APR_GWRITE);
1884 *read_only = !(file_info->protection & APR_WWRITE);
1886 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1887 *read_only = (file_info->protection & APR_FREADONLY);
1890 return SVN_NO_ERROR;
1894 svn_io__is_finfo_executable(svn_boolean_t *executable,
1895 apr_finfo_t *file_info,
1898 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1899 apr_status_t apr_err;
1903 *executable = FALSE;
1905 apr_err = apr_uid_current(&uid, &gid, pool);
1908 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
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);
1914 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1915 *executable = (file_info->protection & APR_GEXECUTE);
1918 *executable = (file_info->protection & APR_WEXECUTE);
1920 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1921 *executable = FALSE;
1924 return SVN_NO_ERROR;
1928 svn_io_is_file_executable(svn_boolean_t *executable,
1932 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1933 apr_finfo_t file_info;
1935 SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
1937 SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
1939 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1940 *executable = FALSE;
1943 return SVN_NO_ERROR;
1947 /*** File locking. ***/
1948 #if !defined(WIN32) && !defined(__OS2__)
1949 /* Clear all outstanding locks on ARG, an open apr_file_t *. */
1951 file_clear_locks(void *arg)
1953 apr_status_t apr_err;
1954 apr_file_t *f = arg;
1957 apr_err = apr_file_unlock(f);
1966 svn_io_lock_open_file(apr_file_t *lockfile_handle,
1967 svn_boolean_t exclusive,
1968 svn_boolean_t nonblocking,
1971 int locktype = APR_FLOCK_SHARED;
1972 apr_status_t apr_err;
1976 locktype = APR_FLOCK_EXCLUSIVE;
1978 locktype |= APR_FLOCK_NONBLOCK;
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);
1984 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
1986 /* Get lock on the filehandle. */
1987 apr_err = apr_file_lock(lockfile_handle, locktype);
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:
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 ***
2001 Retry for a while for the deadlock to clear. */
2002 FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
2006 switch (locktype & APR_FLOCK_TYPEMASK)
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));
2017 SVN_ERR_MALFUNCTION();
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,
2026 apr_pool_cleanup_null);
2029 return SVN_NO_ERROR;
2033 svn_io_unlock_open_file(apr_file_t *lockfile_handle,
2037 apr_status_t apr_err;
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);
2043 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2045 /* The actual unlock attempt. */
2046 apr_err = apr_file_unlock(lockfile_handle);
2048 return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
2049 try_utf8_from_internal_style(fname, pool));
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);
2057 return SVN_NO_ERROR;
2061 svn_io_file_lock2(const char *lock_file,
2062 svn_boolean_t exclusive,
2063 svn_boolean_t nonblocking,
2066 int locktype = APR_FLOCK_SHARED;
2067 apr_file_t *lockfile_handle;
2071 locktype = APR_FLOCK_EXCLUSIVE;
2074 if (locktype == APR_FLOCK_EXCLUSIVE)
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
2081 locktype |= APR_FLOCK_NONBLOCK;
2084 SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
2088 /* Get lock on the filehandle. */
2089 return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
2094 /* Data consistency/coherency operations. */
2096 svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
2099 apr_os_file_t filehand;
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"),
2107 apr_os_file_get(&filehand, file);
2109 /* Call the operating system specific function to actually force the
2114 if (! FlushFileBuffers(filehand))
2115 return svn_error_wrap_apr(apr_get_os_error(),
2116 _("Can't flush file to disk"));
2122 rv = fsync(filehand);
2123 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
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;
2132 return svn_error_wrap_apr(apr_get_os_error(),
2133 _("Can't flush file to disk"));
2137 return SVN_NO_ERROR;
2142 /* TODO write test for these two functions, then refactor. */
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.) */
2151 static svn_error_t *
2152 stringbuf_from_aprfile(svn_stringbuf_t **result,
2153 const char *filename,
2155 svn_boolean_t check_size,
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);
2164 /* If our caller wants us to check the size of the file for
2165 efficient memory handling, we'll try to do so. */
2168 apr_status_t status;
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. */
2174 const char *filename_apr;
2175 if (! (status = apr_file_name_get(&filename_apr, file)))
2176 filename = filename_apr;
2179 /* If we now know the filename, try to stat(). If we succeed,
2180 we know how to allocate our stringbuf. */
2184 if (! (status = apr_stat(&finfo, filename, APR_FINFO_MIN, pool)))
2185 res_initial_len = (apr_size_t)finfo.size;
2190 /* XXX: We should check the incoming data for being of type binary. */
2192 res = svn_stringbuf_create_ensure(res_initial_len, pool);
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);
2200 svn_stringbuf_appendbytes(res, buf, len);
2201 len = SVN__STREAM_CHUNK_SIZE;
2202 err = svn_io_file_read(file, buf, &len, pool);
2205 /* Having read all the data we *expect* EOF */
2206 if (err && !APR_STATUS_IS_EOF(err->apr_err))
2208 svn_error_clear(err);
2211 return SVN_NO_ERROR;
2215 svn_stringbuf_from_file2(svn_stringbuf_t **result,
2216 const char *filename,
2221 if (filename[0] == '-' && filename[1] == '\0')
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));
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));
2233 return svn_io_file_close(f, pool);
2238 svn_stringbuf_from_file(svn_stringbuf_t **result,
2239 const char *filename,
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);
2250 svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
2254 return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
2262 svn_io_remove_file2(const char *path,
2263 svn_boolean_t ignore_enoent,
2264 apr_pool_t *scratch_pool)
2266 apr_status_t apr_err;
2267 const char *path_apr;
2269 SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
2271 apr_err = apr_file_remove(path_apr, scratch_pool);
2274 && (APR_STATUS_IS_ENOENT(apr_err)
2275 || SVN__APR_STATUS_IS_ENOTDIR(apr_err))))
2276 return SVN_NO_ERROR;
2279 /* If the target is read only NTFS reports EACCESS and FAT/FAT32
2281 if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
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);
2289 return SVN_NO_ERROR;
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)
2299 if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
2300 && finfo.filetype == APR_REG)
2302 WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr,
2307 /* Just return the delete error */
2312 return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
2313 svn_dirent_local_style(path, scratch_pool));
2315 return SVN_NO_ERROR;
2320 svn_io_remove_dir(const char *path, apr_pool_t *pool)
2322 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
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.
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.
2340 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
2341 http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
2344 /* Neither windows nor unix allows us to delete a non-empty
2347 This is a function to perform the equivalent of 'rm -rf'. */
2349 svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
2350 svn_cancel_func_t cancel_func, void *cancel_baton,
2354 apr_pool_t *subpool;
2355 apr_hash_t *dirents;
2356 apr_hash_index_t *hi;
2358 /* Check for pending cancellation request.
2359 If we need to bail out, do so early. */
2362 SVN_ERR((*cancel_func)(cancel_baton));
2364 subpool = svn_pool_create(pool);
2366 err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
2369 /* if the directory doesn't exist, our mission is accomplished */
2370 if (ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err))
2372 svn_error_clear(err);
2373 return SVN_NO_ERROR;
2375 return svn_error_trace(err);
2378 for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
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;
2384 fullpath = svn_dirent_join(path, name, subpool);
2385 if (dirent->kind == svn_node_dir)
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));
2394 SVN_ERR((*cancel_func)(cancel_baton));
2396 err = svn_io_remove_file2(fullpath, FALSE, subpool);
2398 return svn_error_createf
2399 (err->apr_err, err, _("Can't remove '%s'"),
2400 svn_dirent_local_style(fullpath, subpool));
2404 svn_pool_destroy(subpool);
2406 return svn_io_dir_remove_nonrecursive(path, pool);
2410 svn_io_get_dir_filenames(apr_hash_t **dirents,
2414 return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
2419 svn_io_dirent2_create(apr_pool_t *result_pool)
2421 svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
2423 /*dirent->kind = svn_node_none;
2424 dirent->special = FALSE;*/
2425 dirent->filesize = SVN_INVALID_FILESIZE;
2426 /*dirent->mtime = 0;*/
2432 svn_io_dirent2_dup(const svn_io_dirent2_t *item,
2433 apr_pool_t *result_pool)
2435 return apr_pmemdup(result_pool,
2441 svn_io_get_dirents3(apr_hash_t **dirents,
2443 svn_boolean_t only_check_type,
2444 apr_pool_t *result_pool,
2445 apr_pool_t *scratch_pool)
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;
2452 if (!only_check_type)
2453 flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
2455 *dirents = apr_hash_make(result_pool);
2457 SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
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))
2463 if ((this_entry.name[0] == '.')
2464 && ((this_entry.name[1] == '\0')
2465 || ((this_entry.name[1] == '.')
2466 && (this_entry.name[2] == '\0'))))
2473 svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
2475 SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
2477 map_apr_finfo_to_node_kind(&(dirent->kind),
2481 if (!only_check_type)
2483 dirent->filesize = this_entry.size;
2484 dirent->mtime = this_entry.mtime;
2487 svn_hash_sets(*dirents, name, dirent);
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));
2495 status = apr_dir_close(this_dir);
2497 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
2498 svn_dirent_local_style(path, scratch_pool));
2500 return SVN_NO_ERROR;
2504 svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
2506 svn_boolean_t verify_truename,
2507 svn_boolean_t ignore_enoent,
2508 apr_pool_t *result_pool,
2509 apr_pool_t *scratch_pool)
2512 svn_io_dirent2_t *dirent;
2514 apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
2515 | APR_FINFO_SIZE | APR_FINFO_MTIME;
2517 #if defined(WIN32) || defined(__OS2__)
2518 if (verify_truename)
2519 wanted |= APR_FINFO_NAME;
2522 err = svn_io_stat(&finfo, path, wanted, scratch_pool);
2524 if (err && ignore_enoent &&
2525 (APR_STATUS_IS_ENOENT(err->apr_err)
2526 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2528 svn_error_clear(err);
2529 dirent = svn_io_dirent2_create(result_pool);
2530 SVN_ERR_ASSERT(dirent->kind == svn_node_none);
2533 return SVN_NO_ERROR;
2537 #if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
2538 if (verify_truename)
2540 const char *requested_name = svn_dirent_basename(path, NULL);
2542 if (requested_name[0] == '\0')
2544 /* No parent directory. No need to stat/verify */
2546 #if defined(WIN32) || defined(__OS2__)
2547 else if (finfo.name)
2549 const char *name_on_disk;
2550 SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
2553 if (strcmp(name_on_disk, requested_name) /* != 0 */)
2557 *dirent_p = svn_io_dirent2_create(result_pool);
2558 return SVN_NO_ERROR;
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),
2567 #elif defined(DARWIN)
2568 /* Currently apr doesn't set finfo.name on DARWIN, returning
2570 ### Can we optimize this in another way? */
2573 apr_hash_t *dirents;
2575 err = svn_io_get_dirents3(&dirents,
2576 svn_dirent_dirname(path, scratch_pool),
2577 TRUE /* only_check_type */,
2578 scratch_pool, scratch_pool);
2580 if (err && ignore_enoent
2581 && (APR_STATUS_IS_ENOENT(err->apr_err)
2582 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2584 svn_error_clear(err);
2586 *dirent_p = svn_io_dirent2_create(result_pool);
2587 return SVN_NO_ERROR;
2592 if (! svn_hash_gets(dirents, requested_name))
2596 *dirent_p = svn_io_dirent2_create(result_pool);
2597 return SVN_NO_ERROR;
2600 return svn_error_createf(APR_ENOENT, NULL,
2601 _("Path '%s' not found"),
2602 svn_dirent_local_style(path, scratch_pool));
2609 dirent = svn_io_dirent2_create(result_pool);
2610 map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
2612 dirent->filesize = finfo.size;
2613 dirent->mtime = finfo.mtime;
2617 return SVN_NO_ERROR;
2620 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2621 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2623 /* Handle an error from the child process (before command execution) by
2624 printing DESC and the error string corresponding to STATUS to stderr. */
2626 handle_child_process_error(apr_pool_t *pool, apr_status_t status,
2630 apr_file_t *errfile;
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))
2639 /* What we get from APR is in native encoding. */
2640 apr_file_printf(errfile, "%s: %s",
2641 desc, apr_strerror(status, errbuf,
2647 svn_io_start_cmd3(apr_proc_t *cmd_proc,
2650 const char *const *args,
2651 const char *const *env,
2652 svn_boolean_t inherit,
2653 svn_boolean_t infile_pipe,
2655 svn_boolean_t outfile_pipe,
2656 apr_file_t *outfile,
2657 svn_boolean_t errfile_pipe,
2658 apr_file_t *errfile,
2661 apr_status_t apr_err;
2662 apr_procattr_t *cmdproc_attr;
2664 const char **args_native;
2665 const char *cmd_apr;
2667 SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
2668 SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
2669 SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
2671 /* Create the process attributes. */
2672 apr_err = apr_procattr_create(&cmdproc_attr, pool);
2674 return svn_error_wrap_apr(apr_err,
2675 _("Can't create process '%s' attributes"),
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);
2682 return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
2685 /* Set the process's working directory. */
2688 const char *path_apr;
2690 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2691 apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
2693 return svn_error_wrap_apr(apr_err,
2694 _("Can't set process '%s' directory"),
2698 /* Use requested inputs and outputs.
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. */
2705 apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
2707 return svn_error_wrap_apr(apr_err,
2708 _("Can't set process '%s' child input"),
2713 apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
2715 return svn_error_wrap_apr(apr_err,
2716 _("Can't set process '%s' child outfile"),
2721 apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
2723 return svn_error_wrap_apr(apr_err,
2724 _("Can't set process '%s' child errfile"),
2728 /* Forward request for pipes to APR. */
2729 if (infile_pipe || outfile_pipe || errfile_pipe)
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);
2737 return svn_error_wrap_apr(apr_err,
2738 _("Can't set process '%s' stdio pipes"),
2742 /* Have the child print any problems executing its program to errfile. */
2743 apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
2745 return svn_error_wrap_apr(apr_err,
2746 _("Can't set process '%s' child errfile for "
2749 apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
2750 handle_child_process_error);
2752 return svn_error_wrap_apr(apr_err,
2753 _("Can't set process '%s' error handler"),
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++)
2760 args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
2761 args_native[num_args] = NULL;
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));
2772 /* Start the cmd command. */
2773 apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
2774 inherit ? NULL : env, cmdproc_attr, pool);
2776 return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
2778 return SVN_NO_ERROR;
2784 svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
2787 apr_exit_why_e *exitwhy,
2790 apr_status_t apr_err;
2791 apr_exit_why_e exitwhy_val;
2794 /* The Win32 apr_proc_wait doesn't set this... */
2795 exitwhy_val = APR_PROC_EXIT;
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'"),
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)"),
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)"),
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);
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);
2830 return SVN_NO_ERROR;
2835 svn_io_run_cmd(const char *path,
2837 const char *const *args,
2839 apr_exit_why_e *exitwhy,
2840 svn_boolean_t inherit,
2842 apr_file_t *outfile,
2843 apr_file_t *errfile,
2846 apr_proc_t cmd_proc;
2848 SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
2849 FALSE, infile, FALSE, outfile, FALSE, errfile,
2852 return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
2857 svn_io_run_diff2(const char *dir,
2858 const char *const *user_args,
2865 apr_file_t *outfile,
2866 apr_file_t *errfile,
2867 const char *diff_cmd,
2873 int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
2874 apr_pool_t *subpool = svn_pool_create(pool);
2876 if (pexitcode == NULL)
2877 pexitcode = &exitcode;
2879 if (user_args != NULL)
2880 nargs += num_user_args;
2882 nargs += 1; /* -u */
2885 nargs += 2; /* the -L and the label itself */
2887 nargs += 2; /* the -L and the label itself */
2889 args = apr_palloc(subpool, nargs * sizeof(char *));
2892 args[i++] = diff_cmd;
2894 if (user_args != NULL)
2897 for (j = 0; j < num_user_args; ++j)
2898 args[i++] = user_args[j];
2901 args[i++] = "-u"; /* assume -u if the user didn't give us any args */
2914 args[i++] = svn_dirent_local_style(from, subpool);
2915 args[i++] = svn_dirent_local_style(to, subpool);
2918 SVN_ERR_ASSERT(i == nargs);
2920 SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
2921 NULL, outfile, errfile, subpool));
2923 /* The man page for (GNU) diff describes the return value as:
2925 "An exit status of 0 means no differences were found, 1 means
2926 some differences were found, and 2 means trouble."
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
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),
2939 svn_pool_destroy(subpool);
2941 return SVN_NO_ERROR;
2946 svn_io_run_diff3_3(int *exitcode,
2951 const char *mine_label,
2952 const char *older_label,
2953 const char *yours_label,
2955 const char *diff3_cmd,
2956 const apr_array_header_t *user_args,
2959 const char **args = apr_palloc(pool,
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";
2977 /* Set up diff3 command line. */
2978 args[i++] = diff3_cmd;
2982 for (j = 0; j < user_args->nelts; ++j)
2983 args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
2985 nargs += user_args->nelts;
2990 args[i++] = "-E"; /* We tried "-A" here, but that caused
2991 overlapping identical changes to
2992 conflict. See issue #682. */
2999 args[i++] = mine_label;
3001 args[i++] = older_label; /* note: this label is ignored if
3002 using 2-part markers, which is the
3005 args[i++] = yours_label;
3006 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
3008 svn_boolean_t has_arg;
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? */
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,
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);
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);
3040 SVN_ERR_ASSERT(i == nargs);
3043 /* Run diff3, output the merged text into the scratch file. */
3044 SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
3046 TRUE, /* keep environment */
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
3053 if ((*exitcode != 0) && (*exitcode != 1))
3054 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3055 _("Error running '%s': exitcode was %d, "
3057 "\nin directory '%s', basenames:\n%s\n%s\n%s"),
3058 svn_dirent_local_style(diff3_cmd, pool),
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);
3067 return SVN_NO_ERROR;
3071 /* Canonicalize a string for hashing. Modifies KEY in place. */
3072 static APR_INLINE char *
3073 fileext_tolower(char *key)
3076 for (p = key; *p != 0; ++p)
3077 *p = (char)apr_tolower(*p);
3083 svn_io_parse_mimetypes_file(apr_hash_t **type_map,
3084 const char *mimetypes_file,
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;
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);
3101 apr_array_header_t *tokens;
3104 svn_pool_clear(subpool);
3107 if ((err = svn_stream_readline(mimetypes_stream, &buf,
3108 APR_EOL_STR, &eof, subpool)))
3111 /* Only pay attention to non-empty, non-comment lines. */
3116 if (buf->data[0] == '#')
3119 /* Tokenize (into our return pool). */
3120 tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
3121 if (tokens->nelts < 2)
3124 /* The first token in a multi-token line is the media type.
3125 Subsequent tokens are filename extensions associated with
3127 type = APR_ARRAY_IDX(tokens, 0, const char *);
3128 for (i = 1; i < tokens->nelts; i++)
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);
3140 svn_pool_destroy(subpool);
3142 /* If there was an error above, close the file (ignoring any error
3143 from *that*) and return the originally error. */
3146 svn_error_clear(svn_stream_close(mimetypes_stream));
3150 /* Close the stream (which closes the underlying file, too). */
3151 SVN_ERR(svn_stream_close(mimetypes_stream));
3154 return SVN_NO_ERROR;
3159 svn_io_detect_mimetype2(const char **mimetype,
3161 apr_hash_t *mimetype_map,
3164 static const char * const generic_binary = "application/octet-stream";
3166 svn_node_kind_t kind;
3169 unsigned char block[1024];
3170 apr_size_t amt_read = sizeof(block);
3172 /* Default return value is NULL. */
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
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)))
3187 *mimetype = type_from_map;
3188 return SVN_NO_ERROR;
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));
3199 SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
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))
3205 svn_error_clear(err);
3207 /* Now close the file. No use keeping it open any more. */
3208 SVN_ERR(svn_io_file_close(fh, pool));
3210 if (svn_io_is_binary_data(block, amt_read))
3211 *mimetype = generic_binary;
3213 return SVN_NO_ERROR;
3218 svn_io_is_binary_data(const void *data, apr_size_t len)
3220 const unsigned char *buf = data;
3222 if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
3224 /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
3225 * Treat it as plain text. */
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.
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 */
3242 apr_size_t binary_count = 0;
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++)
3255 || ((buf[i] > 0x0D) && (buf[i] < 0x20))
3262 return (((binary_count * 1000) / len) > 850);
3270 svn_io_detect_mimetype(const char **mimetype,
3274 return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
3279 svn_io_file_open(apr_file_t **new_file, const char *fname,
3280 apr_int32_t flag, apr_fileperms_t perm,
3283 const char *fname_apr;
3284 apr_status_t status;
3286 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3287 status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
3291 return svn_error_wrap_apr(status, _("Can't open file '%s'"),
3292 svn_dirent_local_style(fname, pool));
3294 return SVN_NO_ERROR;
3298 static APR_INLINE svn_error_t *
3299 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
3300 const char *msg, const char *msg_no_name,
3307 return SVN_NO_ERROR;
3309 err = svn_io_file_name_get(&name, file, pool);
3312 svn_error_clear(err);
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);
3320 return svn_error_wrap_apr(status, _(msg),
3321 try_utf8_from_internal_style(name, pool));
3323 return svn_error_wrap_apr(status, "%s", _(msg_no_name));
3328 svn_io_file_close(apr_file_t *file, apr_pool_t *pool)
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"),
3338 svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
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"),
3348 svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
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"),
3358 svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
3359 apr_file_t *file, apr_pool_t *pool)
3361 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3362 wanted &= ~SVN__APR_FINFO_MASK_OUT;
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"),
3373 svn_io_file_read(apr_file_t *file, void *buf,
3374 apr_size_t *nbytes, apr_pool_t *pool)
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"),
3384 svn_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,
3389 apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
3392 if (APR_STATUS_IS_EOF(status))
3395 return SVN_NO_ERROR;
3401 return do_io_file_wrapper_cleanup(file, status,
3402 N_("Can't read file '%s'"),
3403 N_("Can't read stream"),
3409 svn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
3410 apr_off_t *offset, apr_pool_t *pool)
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"),
3421 svn_io_file_write(apr_file_t *file, const void *buf,
3422 apr_size_t *nbytes, apr_pool_t *pool)
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"),
3433 svn_io_file_write_full(apr_file_t *file, const void *buf,
3434 apr_size_t nbytes, apr_size_t *bytes_written,
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. */
3442 #define MAXBUFSIZE 30*1024
3443 apr_size_t bw = nbytes;
3444 apr_size_t to_write = nbytes;
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;
3451 /* if the OS cannot handle that, use smaller chunks */
3452 if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
3453 && nbytes > MAXBUFSIZE)
3456 bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
3457 rv = apr_file_write(file, buf, &bw);
3458 buf = (char *)buf + bw;
3460 } while (rv == APR_SUCCESS && to_write > 0);
3463 /* bytes_written may actually be NULL */
3465 *bytes_written = nbytes - to_write;
3468 apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
3471 return svn_error_trace(do_io_file_wrapper_cleanup(
3473 N_("Can't write to file '%s'"),
3474 N_("Can't write to stream"),
3480 svn_io_write_unique(const char **tmp_path,
3481 const char *dirpath,
3484 svn_io_file_del_t delete_when,
3487 apr_file_t *new_file;
3490 SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
3491 delete_when, pool, pool));
3493 err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
3496 err = svn_io_file_flush_to_disk(new_file, pool);
3498 return svn_error_trace(
3499 svn_error_compose_create(err,
3500 svn_io_file_close(new_file, pool)));
3505 svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
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"),
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"),
3523 svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
3527 apr_size_t total_read = 0;
3528 svn_boolean_t eof = FALSE;
3531 apr_size_t buf_size = *limit;
3533 while (buf_size > 0)
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.
3539 apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
3540 apr_size_t bytes_read = 0;
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));
3550 /* look or a newline char */
3551 buf[bytes_read] = 0;
3552 eol = strchr(buf, '\n');
3555 apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
3558 *limit = total_read + (eol - buf);
3560 /* correct the file pointer:
3561 * appear as though we just had read the newline char
3563 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
3565 return SVN_NO_ERROR;
3569 /* no EOL found but we hit the end of the file.
3570 * Generate a nice EOF error object and return it.
3573 SVN_ERR(svn_io_file_getc(&dummy, file, pool));
3576 /* next data chunk */
3577 buf_size -= bytes_read;
3579 total_read += bytes_read;
3582 /* buffer limit has been exceeded without finding the EOL */
3583 err = svn_io_file_name_get(&name, file, pool);
3586 svn_error_clear(err);
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));
3593 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
3594 _("Can't read length line in stream"));
3599 svn_io_stat(apr_finfo_t *finfo, const char *fname,
3600 apr_int32_t wanted, apr_pool_t *pool)
3602 apr_status_t status;
3603 const char *fname_apr;
3605 /* APR doesn't like "" directories */
3606 if (fname[0] == '\0')
3609 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3611 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3612 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3614 status = apr_stat(finfo, fname_apr, wanted, pool);
3616 return svn_error_wrap_apr(status, _("Can't stat '%s'"),
3617 svn_dirent_local_style(fname, pool));
3619 return SVN_NO_ERROR;
3624 svn_io_file_rename(const char *from_path, const char *to_path,
3627 apr_status_t status = APR_SUCCESS;
3628 const char *from_path_apr, *to_path_apr;
3630 SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
3631 SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
3633 status = apr_file_rename(from_path_apr, to_path_apr, pool);
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))
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));
3645 status = apr_file_rename(from_path_apr, to_path_apr, pool);
3647 WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool));
3648 #endif /* WIN32 || __OS2__ */
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));
3655 return SVN_NO_ERROR;
3660 svn_io_file_move(const char *from_path, const char *to_path,
3663 svn_error_t *err = svn_io_file_rename(from_path, to_path, pool);
3665 if (err && APR_STATUS_IS_EXDEV(err->apr_err))
3667 const char *tmp_to_path;
3669 svn_error_clear(err);
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,
3676 err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool);
3680 err = svn_io_file_rename(tmp_to_path, to_path, pool);
3684 err = svn_io_remove_file2(from_path, FALSE, pool);
3686 return SVN_NO_ERROR;
3688 svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool));
3693 svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool));
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. */
3702 static svn_error_t *
3703 dir_make(const char *path, apr_fileperms_t perm,
3704 svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
3706 apr_status_t status;
3707 const char *path_apr;
3709 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
3711 /* APR doesn't like "" directories */
3712 if (path_apr[0] == '\0')
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);
3728 status = apr_dir_make(path_apr, perm, pool);
3729 WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
3732 return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
3733 svn_dirent_local_style(path, pool));
3735 #ifdef APR_FILE_ATTR_HIDDEN
3739 status = apr_file_attrs_set(path_apr,
3740 APR_FILE_ATTR_HIDDEN,
3741 APR_FILE_ATTR_HIDDEN,
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,
3755 return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
3756 svn_dirent_local_style(path, pool));
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'. */
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);
3773 apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
3777 return SVN_NO_ERROR;
3781 svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
3783 return dir_make(path, perm, FALSE, FALSE, pool);
3787 svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
3790 return dir_make(path, perm, TRUE, FALSE, pool);
3794 svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
3797 return dir_make(path, perm, FALSE, TRUE, pool);
3802 svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
3804 apr_status_t status;
3805 const char *dirname_apr;
3807 /* APR doesn't like "" directories */
3808 if (dirname[0] == '\0')
3811 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3813 status = apr_dir_open(new_dir, dirname_apr, pool);
3815 return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
3816 svn_dirent_local_style(dirname, pool));
3818 return SVN_NO_ERROR;
3822 svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
3824 apr_status_t status;
3825 const char *dirname_apr;
3827 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3829 status = apr_dir_remove(dirname_apr, pool);
3833 svn_boolean_t retry = TRUE;
3835 if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY)
3837 apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
3839 if (APR_STATUS_IS_ENOTEMPTY(empty_status))
3845 WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
3850 return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
3851 svn_dirent_local_style(dirname, pool));
3853 return SVN_NO_ERROR;
3858 svn_io_dir_read(apr_finfo_t *finfo,
3863 apr_status_t status;
3865 status = apr_dir_read(finfo, wanted, thedir);
3868 return svn_error_wrap_apr(status, _("Can't read directory"));
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
3875 SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
3878 SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
3880 return SVN_NO_ERROR;
3884 svn_io_dir_close(apr_dir_t *thedir)
3886 apr_status_t apr_err = apr_dir_close(thedir);
3888 return svn_error_wrap_apr(apr_err, _("Error closing directory"));
3890 return SVN_NO_ERROR;
3894 svn_io_dir_walk2(const char *dirname,
3896 svn_io_walk_func_t walk_func,
3900 apr_status_t apr_err;
3902 apr_pool_t *subpool;
3903 const char *dirname_apr;
3906 wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
3908 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3909 wanted &= ~SVN__APR_FINFO_MASK_OUT;
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
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
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),
3927 finfo.valid |= APR_FINFO_NAME;
3928 SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
3930 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3932 /* APR doesn't like "" directories */
3933 if (dirname_apr[0] == '\0')
3936 apr_err = apr_dir_open(&handle, dirname_apr, pool);
3938 return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
3939 svn_dirent_local_style(dirname, pool));
3941 /* iteration subpool */
3942 subpool = svn_pool_create(pool);
3946 const char *name_utf8;
3947 const char *full_path;
3949 svn_pool_clear(subpool);
3951 apr_err = apr_dir_read(&finfo, wanted, handle);
3952 if (APR_STATUS_IS_ENOENT(apr_err))
3956 return svn_error_wrap_apr(apr_err,
3957 _("Can't read directory entry in '%s'"),
3958 svn_dirent_local_style(dirname, pool));
3961 if (finfo.filetype == APR_DIR)
3963 if (finfo.name[0] == '.'
3964 && (finfo.name[1] == '\0'
3965 || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
3966 /* skip "." and ".." */
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,
3973 full_path = svn_dirent_join(dirname, name_utf8, subpool);
3974 SVN_ERR(svn_io_dir_walk2(full_path,
3980 else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
3982 /* some other directory. pass it to the callback. */
3983 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
3985 full_path = svn_dirent_join(dirname, name_utf8, subpool);
3986 SVN_ERR((*walk_func)(walk_baton,
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.
3998 svn_pool_destroy(subpool);
4000 apr_err = apr_dir_close(handle);
4002 return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
4003 svn_dirent_local_style(dirname, pool));
4005 return SVN_NO_ERROR;
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.
4018 * (This function is written in APR style, in anticipation of
4019 * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
4022 dir_is_empty(const char *dir, apr_pool_t *pool)
4024 apr_status_t apr_err;
4025 apr_dir_t *dir_handle;
4027 apr_status_t retval = APR_SUCCESS;
4029 /* APR doesn't like "" directories */
4033 apr_err = apr_dir_open(&dir_handle, dir, pool);
4034 if (apr_err != APR_SUCCESS)
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))
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'))))
4049 retval = APR_ENOTEMPTY;
4054 /* Make sure we broke out of the loop for the right reason. */
4055 if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
4058 apr_err = apr_dir_close(dir_handle);
4059 if (apr_err != APR_SUCCESS)
4067 svn_io_dir_empty(svn_boolean_t *is_empty_p,
4071 apr_status_t status;
4072 const char *path_apr;
4074 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4076 status = dir_is_empty(path_apr, pool);
4080 else if (APR_STATUS_IS_ENOTEMPTY(status))
4081 *is_empty_p = FALSE;
4083 return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
4084 svn_dirent_local_style(path, pool));
4086 return SVN_NO_ERROR;
4091 /*** Version/format files ***/
4094 svn_io_write_version_file(const char *path,
4098 const char *path_tmp;
4099 const char *format_contents = apr_psprintf(pool, "%d\n", version);
4101 SVN_ERR_ASSERT(version >= 0);
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));
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__ */
4114 /* rename the temp file as the real destination */
4115 SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
4117 /* And finally remove the perms to make it read only */
4118 return svn_io_set_file_read_only(path, FALSE, pool);
4123 svn_io_read_version_file(int *version,
4127 apr_file_t *format_file;
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));
4136 err = svn_io_file_read(format_file, buf, &len, pool);
4138 /* Close the file. */
4139 SVN_ERR(svn_error_compose_create(err,
4140 svn_io_file_close(format_file, pool)));
4142 /* If there was no data in PATH, return an error. */
4144 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
4146 svn_dirent_local_style(path, pool));
4148 /* Check that the first line contains only digits. */
4152 for (i = 0; i < len; ++i)
4156 if (i > 0 && (c == '\r' || c == '\n'))
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));
4169 /* Convert to integer. */
4170 SVN_ERR(svn_cstring_atoi(version, buf));
4172 return SVN_NO_ERROR;
4177 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
4178 static svn_error_t *
4179 contents_identical_p(svn_boolean_t *identical_p,
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;
4193 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4196 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4200 return svn_error_trace(
4201 svn_error_compose_create(err,
4202 svn_io_file_close(file1_h, pool)));
4204 *identical_p = TRUE; /* assume TRUE, until disproved below */
4205 while (!err && !eof1 && !eof2)
4207 err = svn_io_file_read_full2(file1_h, buf1,
4208 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4213 err = svn_io_file_read_full2(file2_h, buf2,
4214 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4219 if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
4221 *identical_p = FALSE;
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;
4231 return svn_error_trace(
4232 svn_error_compose_create(
4234 svn_error_compose_create(svn_io_file_close(file1_h, pool),
4235 svn_io_file_close(file2_h, pool))));
4240 /* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
4241 static svn_error_t *
4242 contents_three_identical_p(svn_boolean_t *identical_p12,
4243 svn_boolean_t *identical_p23,
4244 svn_boolean_t *identical_p13,
4248 apr_pool_t *scratch_pool)
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;
4263 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4266 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4270 return svn_error_trace(
4271 svn_error_compose_create(err,
4272 svn_io_file_close(file1_h, scratch_pool)));
4274 err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
4278 return svn_error_trace(
4279 svn_error_compose_create(
4281 svn_error_compose_create(svn_io_file_close(file1_h,
4283 svn_io_file_close(file2_h,
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 */
4291 && ((*identical_p12 && !eof1 && !eof2)
4292 || (*identical_p23 && !eof2 && !eof3)
4293 || (*identical_p13 && !eof1 && !eof3)))
4295 read_1 = read_2 = read_3 = FALSE;
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))
4301 err = svn_io_file_read_full2(file1_h, buf1,
4302 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4303 &eof1, scratch_pool);
4309 if (!eof2 && (*identical_p12 || *identical_p23))
4311 err = svn_io_file_read_full2(file2_h, buf2,
4312 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4313 &eof2, scratch_pool);
4319 if (!eof3 && (*identical_p13 || *identical_p23))
4321 err = svn_io_file_read_full2(file3_h, buf3,
4322 SVN__STREAM_CHUNK_SIZE, &bytes_read3,
4323 &eof3, scratch_pool);
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. */
4333 && (read_1 || read_2)
4335 || (bytes_read1 != bytes_read2)
4336 || memcmp(buf1, buf2, bytes_read1)))
4338 *identical_p12 = FALSE;
4342 && (read_2 || read_3)
4344 || (bytes_read2 != bytes_read3)
4345 || memcmp(buf2, buf3, bytes_read2)))
4347 *identical_p23 = FALSE;
4351 && (read_1 || read_3)
4353 || (bytes_read1 != bytes_read3)
4354 || memcmp(buf1, buf3, bytes_read3)))
4356 *identical_p13 = FALSE;
4360 return svn_error_trace(
4361 svn_error_compose_create(
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)))));
4373 svn_io_files_contents_same_p(svn_boolean_t *same,
4380 SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
4385 return SVN_NO_ERROR;
4388 SVN_ERR(contents_identical_p(&q, file1, file2, pool));
4395 return SVN_NO_ERROR;
4399 svn_io_files_contents_three_same_p(svn_boolean_t *same12,
4400 svn_boolean_t *same23,
4401 svn_boolean_t *same13,
4405 apr_pool_t *scratch_pool)
4407 svn_boolean_t diff_size12, diff_size23, diff_size13;
4409 SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
4417 if (diff_size12 && diff_size23 && diff_size13)
4419 *same12 = *same23 = *same13 = FALSE;
4421 else if (diff_size12 && diff_size23)
4423 *same12 = *same23 = FALSE;
4424 SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
4426 else if (diff_size23 && diff_size13)
4428 *same23 = *same13 = FALSE;
4429 SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
4431 else if (diff_size12 && diff_size13)
4433 *same12 = *same13 = FALSE;
4434 SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
4438 SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
4439 SVN_ERR(contents_three_identical_p(same12, same23, same13,
4440 file1, file2, file3,
4444 return SVN_NO_ERROR;
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
4451 static volatile apr_uint32_t tempname_counter = 0;
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
4458 static svn_error_t *
4459 temp_file_create(apr_file_t **new_file,
4460 const char **new_file_name,
4461 const char *directory,
4463 apr_pool_t *result_pool,
4464 apr_pool_t *scratch_pool)
4467 const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
4468 const char *templ_apr;
4469 apr_status_t status;
4471 SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
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);
4479 return svn_error_wrap_apr(status, _("Can't create temporary file from "
4480 "template '%s'"), templ);
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,
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. */
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. */
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();
4501 /* ### Maybe use an iterpool? */
4502 for (i = 0; i <= 99999; i++)
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;
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
4514 unique_nr = baseNr + 3 * i;
4516 unique_name = svn_dirent_join(directory,
4517 apr_psprintf(scratch_pool, "svn-%X",
4521 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
4523 apr_err = file_open(&try_file, unique_name_apr, flags,
4524 APR_OS_DEFAULT, FALSE, scratch_pool);
4526 if (APR_STATUS_IS_EEXIST(apr_err))
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. */
4534 if (APR_STATUS_IS_EACCES(apr_err))
4537 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
4538 APR_FINFO_TYPE, scratch_pool);
4540 if (!apr_err_2 && finfo.filetype == APR_DIR)
4543 apr_err_2 = APR_TO_OS_ERROR(apr_err);
4545 if (apr_err_2 == ERROR_ACCESS_DENIED ||
4546 apr_err_2 == ERROR_SHARING_VIOLATION)
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 */
4555 /* Else fall through and return the original error. */
4558 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
4559 svn_dirent_local_style(unique_name,
4564 /* Move file to the right pool */
4565 apr_err = apr_file_setaside(new_file, try_file, result_pool);
4568 return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
4569 svn_dirent_local_style(unique_name,
4572 *new_file_name = apr_pstrdup(result_pool, unique_name);
4574 return SVN_NO_ERROR;
4578 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
4580 _("Unable to make name in '%s'"),
4581 svn_dirent_local_style(directory, scratch_pool));
4585 /* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
4587 svn_io_file_name_get(const char **filename,
4591 const char *fname_apr;
4592 apr_status_t status;
4594 status = apr_file_name_get(&fname_apr, file);
4596 return svn_error_wrap_apr(status, _("Can't get file name"));
4599 SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
4603 return SVN_NO_ERROR;
4608 svn_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)
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;
4625 SVN_ERR_ASSERT(file || unique_path);
4629 *unique_path = NULL;
4631 if (dirpath == NULL)
4633 #if !defined(WIN32) && !defined(__OS2__)
4634 using_system_temp_dir = TRUE;
4636 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
4639 switch (delete_when)
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;
4646 /* Because cleanups are run LIFO, we need to make sure to register
4647 our cleanup before the apr_file_close cleanup:
4649 On Windows, you can't remove an open file.
4651 apr_pool_cleanup_register(result_pool, baton,
4652 temp_file_plain_cleanup_handler,
4653 temp_file_child_cleanup_handler);
4656 case svn_io_file_del_on_close:
4657 flags |= APR_DELONCLOSE;
4663 SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
4664 result_pool, scratch_pool));
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)
4678 SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
4679 SVN_ERR(file_perms_set2(tempfile, perms, scratch_pool));
4686 SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
4689 *unique_path = tempname; /* Was allocated in result_pool */
4692 SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
4694 return SVN_NO_ERROR;
4698 svn_io_file_readline(apr_file_t *file,
4699 svn_stringbuf_t **stringbuf,
4703 apr_pool_t *result_pool,
4704 apr_pool_t *scratch_pool)
4706 svn_stringbuf_t *str;
4707 const char *eol_str;
4708 apr_size_t numbytes;
4711 svn_boolean_t found_eof;
4713 str = svn_stringbuf_create_ensure(80, result_pool);
4715 /* Read bytes into STR up to and including, but not storing,
4716 * the next EOL sequence. */
4724 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4725 &found_eof, scratch_pool));
4727 if (numbytes != 1 || len > max_len)
4741 if (!found_eof && len < max_len)
4745 /* Check for "\r\n" by peeking at the next byte. */
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')
4757 /* Pretend we never peeked. */
4758 SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
4765 svn_stringbuf_appendbyte(str, c);
4777 return SVN_NO_ERROR;