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 <= millisecond precision.
1248 ## Perhaps find a better algorithm here. This will fail once
1249 in every 1000 cases on a millisecond precision filesystem.
1251 But better to fail once in every thousand cases than every
1252 time, like we did before.
1253 (All tested filesystems I know have at least microsecond precision.)
1255 Note for further research on algorithm:
1256 FAT32 has < 1 sec precision on ctime, but 2 sec on mtime */
1258 /* Sleep for at least 1 millisecond.
1259 (t < 1000 will be round to 0 in apr) */
1265 now = apr_time_now(); /* Extract the time used for the path stat */
1268 return; /* Passing negative values may suspend indefinitely (Windows) */
1271 apr_sleep(then - now);
1276 svn_io_filesizes_different_p(svn_boolean_t *different_p,
1283 apr_status_t status;
1284 const char *file1_apr, *file2_apr;
1286 /* Not using svn_io_stat() because don't want to generate
1287 svn_error_t objects for non-error conditions. */
1289 SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool));
1290 SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool));
1292 /* Stat both files */
1293 status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool);
1296 /* If we got an error stat'ing a file, it could be because the
1297 file was removed... or who knows. Whatever the case, we
1298 don't know if the filesizes are definitely different, so
1299 assume that they're not. */
1300 *different_p = FALSE;
1301 return SVN_NO_ERROR;
1304 status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool);
1307 /* See previous comment. */
1308 *different_p = FALSE;
1309 return SVN_NO_ERROR;
1312 /* Examine file sizes */
1313 if (finfo1.size == finfo2.size)
1314 *different_p = FALSE;
1316 *different_p = TRUE;
1318 return SVN_NO_ERROR;
1323 svn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
1324 svn_boolean_t *different_p23,
1325 svn_boolean_t *different_p13,
1329 apr_pool_t *scratch_pool)
1331 apr_finfo_t finfo1, finfo2, finfo3;
1332 apr_status_t status1, status2, status3;
1333 const char *file1_apr, *file2_apr, *file3_apr;
1335 /* Not using svn_io_stat() because don't want to generate
1336 svn_error_t objects for non-error conditions. */
1338 SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool));
1339 SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool));
1340 SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool));
1342 /* Stat all three files */
1343 status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool);
1344 status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool);
1345 status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool);
1347 /* If we got an error stat'ing a file, it could be because the
1348 file was removed... or who knows. Whatever the case, we
1349 don't know if the filesizes are definitely different, so
1350 assume that they're not. */
1351 *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size;
1352 *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size;
1353 *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size;
1355 return SVN_NO_ERROR;
1360 svn_io_file_checksum2(svn_checksum_t **checksum,
1362 svn_checksum_kind_t kind,
1365 svn_stream_t *file_stream;
1366 svn_stream_t *checksum_stream;
1369 SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool));
1370 file_stream = svn_stream_from_aprfile2(f, FALSE, pool);
1371 checksum_stream = svn_stream_checksummed2(file_stream, checksum, NULL, kind,
1374 /* Because the checksummed stream will force the reading (and
1375 checksumming) of all the file's bytes, we can just close the stream
1376 and let its magic work. */
1377 return svn_stream_close(checksum_stream);
1382 svn_io_file_checksum(unsigned char digest[],
1386 svn_checksum_t *checksum;
1388 SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool));
1389 memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE);
1391 return SVN_NO_ERROR;
1396 /*** Permissions and modes. ***/
1398 #if !defined(WIN32) && !defined(__OS2__)
1399 /* Given the file specified by PATH, attempt to create an
1400 identical version of it owned by the current user. This is done by
1401 moving it to a temporary location, copying the file back to its old
1402 path, then deleting the temporarily moved version. All temporary
1403 allocations are done in POOL. */
1404 static svn_error_t *
1405 reown_file(const char *path,
1408 const char *unique_name;
1410 SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name,
1411 svn_dirent_dirname(path, pool),
1412 svn_io_file_del_none, pool, pool));
1413 SVN_ERR(svn_io_file_rename(path, unique_name, pool));
1414 SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool));
1415 return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool));
1418 /* Determine what the PERMS for a new file should be by looking at the
1419 permissions of a temporary file that we create.
1420 Unfortunately, umask() as defined in POSIX provides no thread-safe way
1421 to get at the current value of the umask, so what we're doing here is
1422 the only way we have to determine which combination of write bits
1423 (User/Group/World) should be set by default.
1424 Make temporary allocations in SCRATCH_POOL. */
1425 static svn_error_t *
1426 get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool)
1428 /* the default permissions as read from the temp folder */
1429 static apr_fileperms_t default_perms = 0;
1431 /* Technically, this "racy": Multiple threads may use enter here and
1432 try to figure out the default permission concurrently. That's fine
1433 since they will end up with the same results. Even more technical,
1434 apr_fileperms_t is an atomic type on 32+ bit machines.
1436 if (default_perms == 0)
1440 const char *fname_base, *fname;
1441 apr_uint32_t randomish;
1444 /* Get the perms for a newly created file to find out what bits
1447 Explictly delete the file because we want this file to be as
1448 short-lived as possible since its presence means other
1449 processes may have to try multiple names.
1451 Using svn_io_open_uniquely_named() here because other tempfile
1452 creation functions tweak the permission bits of files they create.
1454 randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool
1455 + (apr_uint32_t)apr_time_now());
1456 fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish);
1458 SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, NULL, fname_base,
1459 NULL, svn_io_file_del_none,
1460 scratch_pool, scratch_pool));
1461 err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool);
1462 err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool));
1463 err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE,
1466 *perms = finfo.protection;
1467 default_perms = finfo.protection;
1470 *perms = default_perms;
1472 return SVN_NO_ERROR;
1475 /* OR together permission bits of the file FD and the default permissions
1476 of a file as determined by get_default_file_perms(). Do temporary
1477 allocations in SCRATCH_POOL. */
1478 static svn_error_t *
1479 merge_default_file_perms(apr_file_t *fd, apr_fileperms_t *perms,
1480 apr_pool_t *scratch_pool)
1483 apr_fileperms_t default_perms;
1485 SVN_ERR(get_default_file_perms(&default_perms, scratch_pool));
1486 SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool));
1488 /* Glom the perms together. */
1489 *perms = default_perms | finfo.protection;
1490 return SVN_NO_ERROR;
1493 /* This is a helper function for the svn_io_set_file_read* functions
1494 that attempts to honor the users umask when dealing with
1495 permission changes. It is a no-op when invoked on a symlink. */
1496 static svn_error_t *
1497 io_set_file_perms(const char *path,
1498 svn_boolean_t change_readwrite,
1499 svn_boolean_t enable_write,
1500 svn_boolean_t change_executable,
1501 svn_boolean_t executable,
1502 svn_boolean_t ignore_enoent,
1505 apr_status_t status;
1506 const char *path_apr;
1508 apr_fileperms_t perms_to_set;
1510 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1512 /* Try to change only a minimal amount of the perms first
1513 by getting the current perms and adding bits
1514 only on where read perms are granted. If this fails
1515 fall through to just setting file attributes. */
1516 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool);
1519 if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1520 return SVN_NO_ERROR;
1521 else if (status != APR_ENOTIMPL)
1522 return svn_error_wrap_apr(status,
1523 _("Can't change perms of file '%s'"),
1524 svn_dirent_local_style(path, pool));
1525 return SVN_NO_ERROR;
1528 if (finfo.filetype == APR_LNK)
1529 return SVN_NO_ERROR;
1531 perms_to_set = finfo.protection;
1532 if (change_readwrite)
1534 if (enable_write) /* Make read-write. */
1536 /* Tweak the owner bits only. The group/other bits aren't safe to
1537 * touch because we may end up setting them in undesired ways. */
1538 perms_to_set |= (APR_UREAD|APR_UWRITE);
1542 if (finfo.protection & APR_UREAD)
1543 perms_to_set &= ~APR_UWRITE;
1544 if (finfo.protection & APR_GREAD)
1545 perms_to_set &= ~APR_GWRITE;
1546 if (finfo.protection & APR_WREAD)
1547 perms_to_set &= ~APR_WWRITE;
1551 if (change_executable)
1555 if (finfo.protection & APR_UREAD)
1556 perms_to_set |= APR_UEXECUTE;
1557 if (finfo.protection & APR_GREAD)
1558 perms_to_set |= APR_GEXECUTE;
1559 if (finfo.protection & APR_WREAD)
1560 perms_to_set |= APR_WEXECUTE;
1564 if (finfo.protection & APR_UREAD)
1565 perms_to_set &= ~APR_UEXECUTE;
1566 if (finfo.protection & APR_GREAD)
1567 perms_to_set &= ~APR_GEXECUTE;
1568 if (finfo.protection & APR_WREAD)
1569 perms_to_set &= ~APR_WEXECUTE;
1573 /* If we aren't changing anything then just return, this saves
1574 some system calls and helps with shared working copies */
1575 if (perms_to_set == finfo.protection)
1576 return SVN_NO_ERROR;
1578 status = apr_file_perms_set(path_apr, perms_to_set);
1580 return SVN_NO_ERROR;
1582 if (APR_STATUS_IS_EPERM(status))
1584 /* We don't have permissions to change the
1585 permissions! Try a move, copy, and delete
1586 workaround to see if we can get the file owned by
1587 us. If these succeed, try the permissions set
1590 Note that we only attempt this in the
1591 stat-available path. This assumes that the
1592 move-copy workaround will only be helpful on
1593 platforms that implement apr_stat. */
1594 SVN_ERR(reown_file(path, pool));
1595 status = apr_file_perms_set(path_apr, perms_to_set);
1599 return SVN_NO_ERROR;
1601 if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1602 return SVN_NO_ERROR;
1603 else if (status == APR_ENOTIMPL)
1605 /* At least try to set the attributes. */
1606 apr_fileattrs_t attrs = 0;
1607 apr_fileattrs_t attrs_values = 0;
1609 if (change_readwrite)
1611 attrs = APR_FILE_ATTR_READONLY;
1613 attrs_values = APR_FILE_ATTR_READONLY;
1615 if (change_executable)
1617 attrs = APR_FILE_ATTR_EXECUTABLE;
1619 attrs_values = APR_FILE_ATTR_EXECUTABLE;
1621 status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
1624 return svn_error_wrap_apr(status,
1625 _("Can't change perms of file '%s'"),
1626 svn_dirent_local_style(path, pool));
1628 #endif /* !WIN32 && !__OS2__ */
1631 #if APR_HAS_UNICODE_FS
1632 /* copy of the apr function utf8_to_unicode_path since apr doesn't export this one */
1633 static apr_status_t io_utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen,
1636 /* TODO: The computations could preconvert the string to determine
1637 * the true size of the retstr, but that's a memory over speed
1638 * tradeoff that isn't appropriate this early in development.
1640 * Allocate the maximum string length based on leading 4
1641 * characters of \\?\ (allowing nearly unlimited path lengths)
1642 * plus the trailing null, then transform /'s into \\'s since
1643 * the \\?\ form doesn't allow '/' path separators.
1645 * Note that the \\?\ form only works for local drive paths, and
1646 * \\?\UNC\ is needed UNC paths.
1648 apr_size_t srcremains = strlen(srcstr) + 1;
1649 apr_wchar_t *t = retstr;
1652 /* This is correct, we don't twist the filename if it will
1653 * definitely be shorter than 248 characters. It merits some
1654 * performance testing to see if this has any effect, but there
1655 * seem to be applications that get confused by the resulting
1656 * Unicode \\?\ style file names, especially if they use argv[0]
1657 * or call the Win32 API functions such as GetModuleName, etc.
1658 * Not every application is prepared to handle such names.
1660 * Note also this is shorter than MAX_PATH, as directory paths
1661 * are actually limited to 248 characters.
1663 * Note that a utf-8 name can never result in more wide chars
1664 * than the original number of utf-8 narrow chars.
1666 if (srcremains > 248) {
1667 if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
1668 wcscpy (retstr, L"\\\\?\\");
1672 else if ((srcstr[0] == '/' || srcstr[0] == '\\')
1673 && (srcstr[1] == '/' || srcstr[1] == '\\')
1674 && (srcstr[2] != '?')) {
1675 /* Skip the slashes */
1678 wcscpy (retstr, L"\\\\?\\UNC\\");
1684 if (rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen)) {
1685 return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
1688 return APR_ENAMETOOLONG;
1697 static apr_status_t io_win_file_attrs_set(const char *fname,
1702 /* this is an implementation of apr_file_attrs_set() but one
1703 that uses the proper Windows attributes instead of the apr
1704 attributes. This way, we can apply any Windows file and
1705 folder attributes even if apr doesn't implement them */
1708 #if APR_HAS_UNICODE_FS
1709 apr_wchar_t wfname[APR_PATH_MAX];
1712 #if APR_HAS_UNICODE_FS
1713 IF_WIN_OS_IS_UNICODE
1715 if (rv = io_utf8_to_unicode_path(wfname,
1716 sizeof(wfname) / sizeof(wfname[0]),
1719 flags = GetFileAttributesW(wfname);
1725 flags = GetFileAttributesA(fname);
1729 if (flags == 0xFFFFFFFF)
1730 return apr_get_os_error();
1732 flags &= ~attr_mask;
1733 flags |= (attributes & attr_mask);
1735 #if APR_HAS_UNICODE_FS
1736 IF_WIN_OS_IS_UNICODE
1738 rv = SetFileAttributesW(wfname, flags);
1744 rv = SetFileAttributesA(fname, flags);
1749 return apr_get_os_error();
1757 svn_io_set_file_read_write_carefully(const char *path,
1758 svn_boolean_t enable_write,
1759 svn_boolean_t ignore_enoent,
1763 return svn_io_set_file_read_write(path, ignore_enoent, pool);
1764 return svn_io_set_file_read_only(path, ignore_enoent, pool);
1768 svn_io_set_file_read_only(const char *path,
1769 svn_boolean_t ignore_enoent,
1772 /* On Windows and OS/2, just set the file attributes -- on unix call
1773 our internal function which attempts to honor the umask. */
1774 #if !defined(WIN32) && !defined(__OS2__)
1775 return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE,
1776 ignore_enoent, pool);
1778 apr_status_t status;
1779 const char *path_apr;
1781 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1783 status = apr_file_attrs_set(path_apr,
1784 APR_FILE_ATTR_READONLY,
1785 APR_FILE_ATTR_READONLY,
1788 if (status && status != APR_ENOTIMPL)
1789 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
1790 return svn_error_wrap_apr(status,
1791 _("Can't set file '%s' read-only"),
1792 svn_dirent_local_style(path, pool));
1794 return SVN_NO_ERROR;
1800 svn_io_set_file_read_write(const char *path,
1801 svn_boolean_t ignore_enoent,
1804 /* On Windows and OS/2, just set the file attributes -- on unix call
1805 our internal function which attempts to honor the umask. */
1806 #if !defined(WIN32) && !defined(__OS2__)
1807 return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE,
1808 ignore_enoent, pool);
1810 apr_status_t status;
1811 const char *path_apr;
1813 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1815 status = apr_file_attrs_set(path_apr,
1817 APR_FILE_ATTR_READONLY,
1820 if (status && status != APR_ENOTIMPL)
1821 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
1822 return svn_error_wrap_apr(status,
1823 _("Can't set file '%s' read-write"),
1824 svn_dirent_local_style(path, pool));
1826 return SVN_NO_ERROR;
1831 svn_io_set_file_executable(const char *path,
1832 svn_boolean_t executable,
1833 svn_boolean_t ignore_enoent,
1836 /* On Windows and OS/2, just exit -- on unix call our internal function
1837 which attempts to honor the umask. */
1838 #if (!defined(WIN32) && !defined(__OS2__))
1839 return io_set_file_perms(path, FALSE, FALSE, TRUE, executable,
1840 ignore_enoent, pool);
1842 return SVN_NO_ERROR;
1848 svn_io__is_finfo_read_only(svn_boolean_t *read_only,
1849 apr_finfo_t *file_info,
1852 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1853 apr_status_t apr_err;
1859 apr_err = apr_uid_current(&uid, &gid, pool);
1862 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
1864 /* Check write bit for current user. */
1865 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
1866 *read_only = !(file_info->protection & APR_UWRITE);
1868 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1869 *read_only = !(file_info->protection & APR_GWRITE);
1872 *read_only = !(file_info->protection & APR_WWRITE);
1874 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1875 *read_only = (file_info->protection & APR_FREADONLY);
1878 return SVN_NO_ERROR;
1882 svn_io__is_finfo_executable(svn_boolean_t *executable,
1883 apr_finfo_t *file_info,
1886 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1887 apr_status_t apr_err;
1891 *executable = FALSE;
1893 apr_err = apr_uid_current(&uid, &gid, pool);
1896 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
1898 /* Check executable bit for current user. */
1899 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
1900 *executable = (file_info->protection & APR_UEXECUTE);
1902 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1903 *executable = (file_info->protection & APR_GEXECUTE);
1906 *executable = (file_info->protection & APR_WEXECUTE);
1908 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1909 *executable = FALSE;
1912 return SVN_NO_ERROR;
1916 svn_io_is_file_executable(svn_boolean_t *executable,
1920 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1921 apr_finfo_t file_info;
1923 SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
1925 SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
1927 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1928 *executable = FALSE;
1931 return SVN_NO_ERROR;
1935 /*** File locking. ***/
1936 #if !defined(WIN32) && !defined(__OS2__)
1937 /* Clear all outstanding locks on ARG, an open apr_file_t *. */
1939 file_clear_locks(void *arg)
1941 apr_status_t apr_err;
1942 apr_file_t *f = arg;
1945 apr_err = apr_file_unlock(f);
1954 svn_io_lock_open_file(apr_file_t *lockfile_handle,
1955 svn_boolean_t exclusive,
1956 svn_boolean_t nonblocking,
1959 int locktype = APR_FLOCK_SHARED;
1960 apr_status_t apr_err;
1964 locktype = APR_FLOCK_EXCLUSIVE;
1966 locktype |= APR_FLOCK_NONBLOCK;
1968 /* We need this only in case of an error but this is cheap to get -
1969 * so we do it here for clarity. */
1970 apr_err = apr_file_name_get(&fname, lockfile_handle);
1972 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
1974 /* Get lock on the filehandle. */
1975 apr_err = apr_file_lock(lockfile_handle, locktype);
1977 /* In deployments with two or more multithreaded servers running on
1978 the same system serving two or more fsfs repositories it is
1979 possible for a deadlock to occur when getting a write lock on
1980 db/txn-current-lock:
1984 thread 1: get lock in repos A
1985 thread 1: get lock in repos B
1986 thread 2: block getting lock in repos A
1987 thread 2: try to get lock in B *** deadlock ***
1989 Retry for a while for the deadlock to clear. */
1990 FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
1994 switch (locktype & APR_FLOCK_TYPEMASK)
1996 case APR_FLOCK_SHARED:
1997 return svn_error_wrap_apr(apr_err,
1998 _("Can't get shared lock on file '%s'"),
1999 try_utf8_from_internal_style(fname, pool));
2000 case APR_FLOCK_EXCLUSIVE:
2001 return svn_error_wrap_apr(apr_err,
2002 _("Can't get exclusive lock on file '%s'"),
2003 try_utf8_from_internal_style(fname, pool));
2005 SVN_ERR_MALFUNCTION();
2009 /* On Windows and OS/2 file locks are automatically released when
2010 the file handle closes */
2011 #if !defined(WIN32) && !defined(__OS2__)
2012 apr_pool_cleanup_register(pool, lockfile_handle,
2014 apr_pool_cleanup_null);
2017 return SVN_NO_ERROR;
2021 svn_io_unlock_open_file(apr_file_t *lockfile_handle,
2025 apr_status_t apr_err;
2027 /* We need this only in case of an error but this is cheap to get -
2028 * so we do it here for clarity. */
2029 apr_err = apr_file_name_get(&fname, lockfile_handle);
2031 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2033 /* The actual unlock attempt. */
2034 apr_err = apr_file_unlock(lockfile_handle);
2036 return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
2037 try_utf8_from_internal_style(fname, pool));
2039 /* On Windows and OS/2 file locks are automatically released when
2040 the file handle closes */
2041 #if !defined(WIN32) && !defined(__OS2__)
2042 apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks);
2045 return SVN_NO_ERROR;
2049 svn_io_file_lock2(const char *lock_file,
2050 svn_boolean_t exclusive,
2051 svn_boolean_t nonblocking,
2054 int locktype = APR_FLOCK_SHARED;
2055 apr_file_t *lockfile_handle;
2059 locktype = APR_FLOCK_EXCLUSIVE;
2062 if (locktype == APR_FLOCK_EXCLUSIVE)
2065 /* locktype is never read after this block, so we don't need to bother
2066 setting it. If that were to ever change, uncomment the following
2069 locktype |= APR_FLOCK_NONBLOCK;
2072 SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
2076 /* Get lock on the filehandle. */
2077 return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
2082 /* Data consistency/coherency operations. */
2084 svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
2087 apr_os_file_t filehand;
2089 /* First make sure that any user-space buffered data is flushed. */
2090 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
2091 N_("Can't flush file '%s'"),
2092 N_("Can't flush stream"),
2095 apr_os_file_get(&filehand, file);
2097 /* Call the operating system specific function to actually force the
2102 if (! FlushFileBuffers(filehand))
2103 return svn_error_wrap_apr(apr_get_os_error(),
2104 _("Can't flush file to disk"));
2110 rv = fsync(filehand);
2111 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
2113 /* If the file is in a memory filesystem, fsync() may return
2114 EINVAL. Presumably the user knows the risks, and we can just
2115 ignore the error. */
2116 if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
2117 return SVN_NO_ERROR;
2120 return svn_error_wrap_apr(apr_get_os_error(),
2121 _("Can't flush file to disk"));
2125 return SVN_NO_ERROR;
2130 /* TODO write test for these two functions, then refactor. */
2132 /* Set RESULT to an svn_stringbuf_t containing the contents of FILE.
2133 FILENAME is the FILE's on-disk APR-safe name, or NULL if that name
2134 isn't known. If CHECK_SIZE is TRUE, the function will attempt to
2135 first stat() the file to determine it's size before sucking its
2136 contents into the stringbuf. (Doing so can prevent unnecessary
2137 memory usage, an unwanted side effect of the stringbuf growth and
2138 reallocation mechanism.) */
2139 static svn_error_t *
2140 stringbuf_from_aprfile(svn_stringbuf_t **result,
2141 const char *filename,
2143 svn_boolean_t check_size,
2148 svn_stringbuf_t *res = NULL;
2149 apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE;
2150 char *buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
2152 /* If our caller wants us to check the size of the file for
2153 efficient memory handling, we'll try to do so. */
2156 apr_status_t status;
2158 /* If our caller didn't tell us the file's name, we'll ask APR
2159 if it knows the name. No problem if we can't figure it out. */
2162 const char *filename_apr;
2163 if (! (status = apr_file_name_get(&filename_apr, file)))
2164 filename = filename_apr;
2167 /* If we now know the filename, try to stat(). If we succeed,
2168 we know how to allocate our stringbuf. */
2172 if (! (status = apr_stat(&finfo, filename, APR_FINFO_MIN, pool)))
2173 res_initial_len = (apr_size_t)finfo.size;
2178 /* XXX: We should check the incoming data for being of type binary. */
2180 res = svn_stringbuf_create_ensure(res_initial_len, pool);
2182 /* apr_file_read will not return data and eof in the same call. So this loop
2183 * is safe from missing read data. */
2184 len = SVN__STREAM_CHUNK_SIZE;
2185 err = svn_io_file_read(file, buf, &len, pool);
2188 svn_stringbuf_appendbytes(res, buf, len);
2189 len = SVN__STREAM_CHUNK_SIZE;
2190 err = svn_io_file_read(file, buf, &len, pool);
2193 /* Having read all the data we *expect* EOF */
2194 if (err && !APR_STATUS_IS_EOF(err->apr_err))
2196 svn_error_clear(err);
2199 return SVN_NO_ERROR;
2203 svn_stringbuf_from_file2(svn_stringbuf_t **result,
2204 const char *filename,
2209 if (filename[0] == '-' && filename[1] == '\0')
2211 apr_status_t apr_err;
2212 if ((apr_err = apr_file_open_stdin(&f, pool)))
2213 return svn_error_wrap_apr(apr_err, _("Can't open stdin"));
2214 SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool));
2218 SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool));
2219 SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool));
2221 return svn_io_file_close(f, pool);
2226 svn_stringbuf_from_file(svn_stringbuf_t **result,
2227 const char *filename,
2230 if (filename[0] == '-' && filename[1] == '\0')
2231 return svn_error_create
2232 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2233 _("Reading from stdin is disallowed"));
2234 return svn_stringbuf_from_file2(result, filename, pool);
2238 svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
2242 return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
2250 svn_io_remove_file2(const char *path,
2251 svn_boolean_t ignore_enoent,
2252 apr_pool_t *scratch_pool)
2254 apr_status_t apr_err;
2255 const char *path_apr;
2257 SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
2259 apr_err = apr_file_remove(path_apr, scratch_pool);
2262 && (APR_STATUS_IS_ENOENT(apr_err)
2263 || SVN__APR_STATUS_IS_ENOTDIR(apr_err))))
2264 return SVN_NO_ERROR;
2267 /* If the target is read only NTFS reports EACCESS and FAT/FAT32
2269 if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
2271 /* Set the destination file writable because Windows will not
2272 allow us to delete when path is read-only */
2273 SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool));
2274 apr_err = apr_file_remove(path_apr, scratch_pool);
2277 return SVN_NO_ERROR;
2281 apr_status_t os_err = APR_TO_OS_ERROR(apr_err);
2282 /* Check to make sure we aren't trying to delete a directory */
2283 if (os_err == ERROR_ACCESS_DENIED || os_err == ERROR_SHARING_VIOLATION)
2287 if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
2288 && finfo.filetype == APR_REG)
2290 WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr,
2295 /* Just return the delete error */
2300 return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
2301 svn_dirent_local_style(path, scratch_pool));
2303 return SVN_NO_ERROR;
2308 svn_io_remove_dir(const char *path, apr_pool_t *pool)
2310 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
2314 Mac OS X has a bug where if you're reading the contents of a
2315 directory via readdir in a loop, and you remove one of the entries in
2316 the directory and the directory has 338 or more files in it you will
2317 skip over some of the entries in the directory. Needless to say,
2318 this causes problems if you are using this kind of loop inside a
2319 function that is recursively deleting a directory, because when you
2320 get around to removing the directory it will still have something in
2321 it. A similar problem has been observed in other BSDs. This bug has
2322 since been fixed. See http://www.vnode.ch/fixing_seekdir for details.
2324 The workaround is to delete the files only _after_ the initial
2325 directory scan. A previous workaround involving rewinddir is
2326 problematic on Win32 and some NFS clients, notably NetBSD.
2328 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
2329 http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
2332 /* Neither windows nor unix allows us to delete a non-empty
2335 This is a function to perform the equivalent of 'rm -rf'. */
2337 svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
2338 svn_cancel_func_t cancel_func, void *cancel_baton,
2342 apr_pool_t *subpool;
2343 apr_hash_t *dirents;
2344 apr_hash_index_t *hi;
2346 /* Check for pending cancellation request.
2347 If we need to bail out, do so early. */
2350 SVN_ERR((*cancel_func)(cancel_baton));
2352 subpool = svn_pool_create(pool);
2354 err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
2357 /* if the directory doesn't exist, our mission is accomplished */
2358 if (ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err))
2360 svn_error_clear(err);
2361 return SVN_NO_ERROR;
2363 return svn_error_trace(err);
2366 for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
2368 const char *name = svn__apr_hash_index_key(hi);
2369 const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
2370 const char *fullpath;
2372 fullpath = svn_dirent_join(path, name, subpool);
2373 if (dirent->kind == svn_node_dir)
2375 /* Don't check for cancellation, the callee will immediately do so */
2376 SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func,
2377 cancel_baton, subpool));
2382 SVN_ERR((*cancel_func)(cancel_baton));
2384 err = svn_io_remove_file2(fullpath, FALSE, subpool);
2386 return svn_error_createf
2387 (err->apr_err, err, _("Can't remove '%s'"),
2388 svn_dirent_local_style(fullpath, subpool));
2392 svn_pool_destroy(subpool);
2394 return svn_io_dir_remove_nonrecursive(path, pool);
2398 svn_io_get_dir_filenames(apr_hash_t **dirents,
2402 return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
2407 svn_io_dirent2_create(apr_pool_t *result_pool)
2409 svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
2411 /*dirent->kind = svn_node_none;
2412 dirent->special = FALSE;*/
2413 dirent->filesize = SVN_INVALID_FILESIZE;
2414 /*dirent->mtime = 0;*/
2420 svn_io_dirent2_dup(const svn_io_dirent2_t *item,
2421 apr_pool_t *result_pool)
2423 return apr_pmemdup(result_pool,
2429 svn_io_get_dirents3(apr_hash_t **dirents,
2431 svn_boolean_t only_check_type,
2432 apr_pool_t *result_pool,
2433 apr_pool_t *scratch_pool)
2435 apr_status_t status;
2436 apr_dir_t *this_dir;
2437 apr_finfo_t this_entry;
2438 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
2440 if (!only_check_type)
2441 flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
2443 *dirents = apr_hash_make(result_pool);
2445 SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
2447 for (status = apr_dir_read(&this_entry, flags, this_dir);
2448 status == APR_SUCCESS;
2449 status = apr_dir_read(&this_entry, flags, this_dir))
2451 if ((this_entry.name[0] == '.')
2452 && ((this_entry.name[1] == '\0')
2453 || ((this_entry.name[1] == '.')
2454 && (this_entry.name[2] == '\0'))))
2461 svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
2463 SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
2465 map_apr_finfo_to_node_kind(&(dirent->kind),
2469 if (!only_check_type)
2471 dirent->filesize = this_entry.size;
2472 dirent->mtime = this_entry.mtime;
2475 svn_hash_sets(*dirents, name, dirent);
2479 if (! (APR_STATUS_IS_ENOENT(status)))
2480 return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
2481 svn_dirent_local_style(path, scratch_pool));
2483 status = apr_dir_close(this_dir);
2485 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
2486 svn_dirent_local_style(path, scratch_pool));
2488 return SVN_NO_ERROR;
2492 svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
2494 svn_boolean_t verify_truename,
2495 svn_boolean_t ignore_enoent,
2496 apr_pool_t *result_pool,
2497 apr_pool_t *scratch_pool)
2500 svn_io_dirent2_t *dirent;
2502 apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
2503 | APR_FINFO_SIZE | APR_FINFO_MTIME;
2505 #if defined(WIN32) || defined(__OS2__)
2506 if (verify_truename)
2507 wanted |= APR_FINFO_NAME;
2510 err = svn_io_stat(&finfo, path, wanted, scratch_pool);
2512 if (err && ignore_enoent &&
2513 (APR_STATUS_IS_ENOENT(err->apr_err)
2514 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2516 svn_error_clear(err);
2517 dirent = svn_io_dirent2_create(result_pool);
2518 SVN_ERR_ASSERT(dirent->kind == svn_node_none);
2521 return SVN_NO_ERROR;
2525 #if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
2526 if (verify_truename)
2528 const char *requested_name = svn_dirent_basename(path, NULL);
2530 if (requested_name[0] == '\0')
2532 /* No parent directory. No need to stat/verify */
2534 #if defined(WIN32) || defined(__OS2__)
2535 else if (finfo.name)
2537 const char *name_on_disk;
2538 SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
2541 if (strcmp(name_on_disk, requested_name) /* != 0 */)
2545 *dirent_p = svn_io_dirent2_create(result_pool);
2546 return SVN_NO_ERROR;
2549 return svn_error_createf(APR_ENOENT, NULL,
2550 _("Path '%s' not found, case obstructed by '%s'"),
2551 svn_dirent_local_style(path, scratch_pool),
2555 #elif defined(DARWIN)
2556 /* Currently apr doesn't set finfo.name on DARWIN, returning
2558 ### Can we optimize this in another way? */
2561 apr_hash_t *dirents;
2563 err = svn_io_get_dirents3(&dirents,
2564 svn_dirent_dirname(path, scratch_pool),
2565 TRUE /* only_check_type */,
2566 scratch_pool, scratch_pool);
2568 if (err && ignore_enoent
2569 && (APR_STATUS_IS_ENOENT(err->apr_err)
2570 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2572 svn_error_clear(err);
2574 *dirent_p = svn_io_dirent2_create(result_pool);
2575 return SVN_NO_ERROR;
2580 if (! svn_hash_gets(dirents, requested_name))
2584 *dirent_p = svn_io_dirent2_create(result_pool);
2585 return SVN_NO_ERROR;
2588 return svn_error_createf(APR_ENOENT, NULL,
2589 _("Path '%s' not found"),
2590 svn_dirent_local_style(path, scratch_pool));
2597 dirent = svn_io_dirent2_create(result_pool);
2598 map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
2600 dirent->filesize = finfo.size;
2601 dirent->mtime = finfo.mtime;
2605 return SVN_NO_ERROR;
2608 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2609 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2611 /* Handle an error from the child process (before command execution) by
2612 printing DESC and the error string corresponding to STATUS to stderr. */
2614 handle_child_process_error(apr_pool_t *pool, apr_status_t status,
2618 apr_file_t *errfile;
2621 /* We can't do anything if we get an error here, so just return. */
2622 if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool))
2627 /* What we get from APR is in native encoding. */
2628 apr_file_printf(errfile, "%s: %s",
2629 desc, apr_strerror(status, errbuf,
2635 svn_io_start_cmd3(apr_proc_t *cmd_proc,
2638 const char *const *args,
2639 const char *const *env,
2640 svn_boolean_t inherit,
2641 svn_boolean_t infile_pipe,
2643 svn_boolean_t outfile_pipe,
2644 apr_file_t *outfile,
2645 svn_boolean_t errfile_pipe,
2646 apr_file_t *errfile,
2649 apr_status_t apr_err;
2650 apr_procattr_t *cmdproc_attr;
2652 const char **args_native;
2653 const char *cmd_apr;
2655 SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
2656 SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
2657 SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
2659 /* Create the process attributes. */
2660 apr_err = apr_procattr_create(&cmdproc_attr, pool);
2662 return svn_error_wrap_apr(apr_err,
2663 _("Can't create process '%s' attributes"),
2666 /* Make sure we invoke cmd directly, not through a shell. */
2667 apr_err = apr_procattr_cmdtype_set(cmdproc_attr,
2668 inherit ? APR_PROGRAM_PATH : APR_PROGRAM);
2670 return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
2673 /* Set the process's working directory. */
2676 const char *path_apr;
2678 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2679 apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
2681 return svn_error_wrap_apr(apr_err,
2682 _("Can't set process '%s' directory"),
2686 /* Use requested inputs and outputs.
2688 ### Unfortunately each of these apr functions creates a pipe and then
2689 overwrites the pipe file descriptor with the descriptor we pass
2690 in. The pipes can then never be closed. This is an APR bug. */
2693 apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
2695 return svn_error_wrap_apr(apr_err,
2696 _("Can't set process '%s' child input"),
2701 apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
2703 return svn_error_wrap_apr(apr_err,
2704 _("Can't set process '%s' child outfile"),
2709 apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
2711 return svn_error_wrap_apr(apr_err,
2712 _("Can't set process '%s' child errfile"),
2716 /* Forward request for pipes to APR. */
2717 if (infile_pipe || outfile_pipe || errfile_pipe)
2719 apr_err = apr_procattr_io_set(cmdproc_attr,
2720 infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
2721 outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
2722 errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE);
2725 return svn_error_wrap_apr(apr_err,
2726 _("Can't set process '%s' stdio pipes"),
2730 /* Have the child print any problems executing its program to errfile. */
2731 apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
2733 return svn_error_wrap_apr(apr_err,
2734 _("Can't set process '%s' child errfile for "
2737 apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
2738 handle_child_process_error);
2740 return svn_error_wrap_apr(apr_err,
2741 _("Can't set process '%s' error handler"),
2744 /* Convert cmd and args from UTF-8 */
2745 SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool));
2746 for (num_args = 0; args[num_args]; num_args++)
2748 args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
2749 args_native[num_args] = NULL;
2752 /* ### Well, it turns out that on APR on Windows expects all
2753 program args to be in UTF-8. Callers of svn_io_run_cmd
2754 should be aware of that. */
2755 SVN_ERR(cstring_from_utf8(&args_native[num_args],
2756 args[num_args], pool));
2760 /* Start the cmd command. */
2761 apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
2762 inherit ? NULL : env, cmdproc_attr, pool);
2764 return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
2766 return SVN_NO_ERROR;
2772 svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
2775 apr_exit_why_e *exitwhy,
2778 apr_status_t apr_err;
2779 apr_exit_why_e exitwhy_val;
2782 /* The Win32 apr_proc_wait doesn't set this... */
2783 exitwhy_val = APR_PROC_EXIT;
2785 /* Wait for the cmd command to finish. */
2786 apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT);
2787 if (!APR_STATUS_IS_CHILD_DONE(apr_err))
2788 return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"),
2792 *exitwhy = exitwhy_val;
2793 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)
2794 && APR_PROC_CHECK_CORE_DUMP(exitwhy_val))
2795 return svn_error_createf
2796 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2797 _("Process '%s' failed (signal %d, core dumped)"),
2799 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val))
2800 return svn_error_createf
2801 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2802 _("Process '%s' failed (signal %d)"),
2804 else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
2805 /* Don't really know what happened here. */
2806 return svn_error_createf
2807 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2808 _("Process '%s' failed (exitwhy %d, exitcode %d)"),
2809 cmd, exitwhy_val, exitcode_val);
2812 *exitcode = exitcode_val;
2813 else if (exitcode_val != 0)
2814 return svn_error_createf
2815 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2816 _("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
2818 return SVN_NO_ERROR;
2823 svn_io_run_cmd(const char *path,
2825 const char *const *args,
2827 apr_exit_why_e *exitwhy,
2828 svn_boolean_t inherit,
2830 apr_file_t *outfile,
2831 apr_file_t *errfile,
2834 apr_proc_t cmd_proc;
2836 SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
2837 FALSE, infile, FALSE, outfile, FALSE, errfile,
2840 return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
2845 svn_io_run_diff2(const char *dir,
2846 const char *const *user_args,
2853 apr_file_t *outfile,
2854 apr_file_t *errfile,
2855 const char *diff_cmd,
2861 int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
2862 apr_pool_t *subpool = svn_pool_create(pool);
2864 if (pexitcode == NULL)
2865 pexitcode = &exitcode;
2867 if (user_args != NULL)
2868 nargs += num_user_args;
2870 nargs += 1; /* -u */
2873 nargs += 2; /* the -L and the label itself */
2875 nargs += 2; /* the -L and the label itself */
2877 args = apr_palloc(subpool, nargs * sizeof(char *));
2880 args[i++] = diff_cmd;
2882 if (user_args != NULL)
2885 for (j = 0; j < num_user_args; ++j)
2886 args[i++] = user_args[j];
2889 args[i++] = "-u"; /* assume -u if the user didn't give us any args */
2902 args[i++] = svn_dirent_local_style(from, subpool);
2903 args[i++] = svn_dirent_local_style(to, subpool);
2906 SVN_ERR_ASSERT(i == nargs);
2908 SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
2909 NULL, outfile, errfile, subpool));
2911 /* The man page for (GNU) diff describes the return value as:
2913 "An exit status of 0 means no differences were found, 1 means
2914 some differences were found, and 2 means trouble."
2916 A return value of 2 typically occurs when diff cannot read its input
2917 or write to its output, but in any case we probably ought to return an
2918 error for anything other than 0 or 1 as the output is likely to be
2921 if (*pexitcode != 0 && *pexitcode != 1)
2922 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
2923 _("'%s' returned %d"),
2924 svn_dirent_local_style(diff_cmd, pool),
2927 svn_pool_destroy(subpool);
2929 return SVN_NO_ERROR;
2934 svn_io_run_diff3_3(int *exitcode,
2939 const char *mine_label,
2940 const char *older_label,
2941 const char *yours_label,
2943 const char *diff3_cmd,
2944 const apr_array_header_t *user_args,
2947 const char **args = apr_palloc(pool,
2957 /* Labels fall back to sensible defaults if not specified. */
2958 if (mine_label == NULL)
2959 mine_label = ".working";
2960 if (older_label == NULL)
2961 older_label = ".old";
2962 if (yours_label == NULL)
2963 yours_label = ".new";
2965 /* Set up diff3 command line. */
2966 args[i++] = diff3_cmd;
2970 for (j = 0; j < user_args->nelts; ++j)
2971 args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
2973 nargs += user_args->nelts;
2978 args[i++] = "-E"; /* We tried "-A" here, but that caused
2979 overlapping identical changes to
2980 conflict. See issue #682. */
2987 args[i++] = mine_label;
2989 args[i++] = older_label; /* note: this label is ignored if
2990 using 2-part markers, which is the
2993 args[i++] = yours_label;
2994 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
2996 svn_boolean_t has_arg;
2998 /* ### FIXME: we really shouldn't be reading the config here;
2999 instead, the necessary bits should be passed in by the caller.
3000 But should we add another parameter to this function, when the
3001 whole external diff3 thing might eventually go away? */
3005 SVN_ERR(svn_config_get_config(&config, pool));
3006 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
3007 SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS,
3008 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG,
3012 const char *diff_cmd, *diff_utf8;
3013 svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
3014 SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF);
3015 SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool));
3016 args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8, NULL);
3023 args[i++] = svn_dirent_local_style(mine, pool);
3024 args[i++] = svn_dirent_local_style(older, pool);
3025 args[i++] = svn_dirent_local_style(yours, pool);
3028 SVN_ERR_ASSERT(i == nargs);
3031 /* Run diff3, output the merged text into the scratch file. */
3032 SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
3034 TRUE, /* keep environment */
3038 /* According to the diff3 docs, a '0' means the merge was clean, and
3039 '1' means conflict markers were found. Anything else is real
3041 if ((*exitcode != 0) && (*exitcode != 1))
3042 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3043 _("Error running '%s': exitcode was %d, "
3045 "\nin directory '%s', basenames:\n%s\n%s\n%s"),
3046 svn_dirent_local_style(diff3_cmd, pool),
3048 svn_dirent_local_style(dir, pool),
3049 /* Don't call svn_path_local_style() on
3050 the basenames. We don't want them to
3051 be absolute, and we don't need the
3052 separator conversion. */
3053 mine, older, yours);
3055 return SVN_NO_ERROR;
3059 /* Canonicalize a string for hashing. Modifies KEY in place. */
3060 static APR_INLINE char *
3061 fileext_tolower(char *key)
3064 for (p = key; *p != 0; ++p)
3065 *p = (char)apr_tolower(*p);
3071 svn_io_parse_mimetypes_file(apr_hash_t **type_map,
3072 const char *mimetypes_file,
3075 svn_error_t *err = SVN_NO_ERROR;
3076 apr_hash_t *types = apr_hash_make(pool);
3077 svn_boolean_t eof = FALSE;
3078 svn_stringbuf_t *buf;
3079 apr_pool_t *subpool = svn_pool_create(pool);
3080 apr_file_t *types_file;
3081 svn_stream_t *mimetypes_stream;
3083 SVN_ERR(svn_io_file_open(&types_file, mimetypes_file,
3084 APR_READ, APR_OS_DEFAULT, pool));
3085 mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool);
3089 apr_array_header_t *tokens;
3092 svn_pool_clear(subpool);
3095 if ((err = svn_stream_readline(mimetypes_stream, &buf,
3096 APR_EOL_STR, &eof, subpool)))
3099 /* Only pay attention to non-empty, non-comment lines. */
3104 if (buf->data[0] == '#')
3107 /* Tokenize (into our return pool). */
3108 tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
3109 if (tokens->nelts < 2)
3112 /* The first token in a multi-token line is the media type.
3113 Subsequent tokens are filename extensions associated with
3115 type = APR_ARRAY_IDX(tokens, 0, const char *);
3116 for (i = 1; i < tokens->nelts; i++)
3118 /* We can safely address 'ext' as a non-const string because
3119 * we know svn_cstring_split() allocated it in 'pool' for us. */
3120 char *ext = APR_ARRAY_IDX(tokens, i, char *);
3121 fileext_tolower(ext);
3122 svn_hash_sets(types, ext, type);
3128 svn_pool_destroy(subpool);
3130 /* If there was an error above, close the file (ignoring any error
3131 from *that*) and return the originally error. */
3134 svn_error_clear(svn_stream_close(mimetypes_stream));
3138 /* Close the stream (which closes the underlying file, too). */
3139 SVN_ERR(svn_stream_close(mimetypes_stream));
3142 return SVN_NO_ERROR;
3147 svn_io_detect_mimetype2(const char **mimetype,
3149 apr_hash_t *mimetype_map,
3152 static const char * const generic_binary = "application/octet-stream";
3154 svn_node_kind_t kind;
3157 unsigned char block[1024];
3158 apr_size_t amt_read = sizeof(block);
3160 /* Default return value is NULL. */
3163 /* If there is a mimetype_map provided, we'll first try to look up
3164 our file's extension in the map. Failing that, we'll run the
3168 const char *type_from_map;
3169 char *path_ext; /* Can point to physical const memory but only when
3170 svn_path_splitext sets it to "". */
3171 svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
3172 fileext_tolower(path_ext);
3173 if ((type_from_map = svn_hash_gets(mimetype_map, path_ext)))
3175 *mimetype = type_from_map;
3176 return SVN_NO_ERROR;
3180 /* See if this file even exists, and make sure it really is a file. */
3181 SVN_ERR(svn_io_check_path(file, &kind, pool));
3182 if (kind != svn_node_file)
3183 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
3184 _("Can't detect MIME type of non-file '%s'"),
3185 svn_dirent_local_style(file, pool));
3187 SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
3189 /* Read a block of data from FILE. */
3190 err = svn_io_file_read(fh, block, &amt_read, pool);
3191 if (err && ! APR_STATUS_IS_EOF(err->apr_err))
3193 svn_error_clear(err);
3195 /* Now close the file. No use keeping it open any more. */
3196 SVN_ERR(svn_io_file_close(fh, pool));
3198 if (svn_io_is_binary_data(block, amt_read))
3199 *mimetype = generic_binary;
3201 return SVN_NO_ERROR;
3206 svn_io_is_binary_data(const void *data, apr_size_t len)
3208 const unsigned char *buf = data;
3210 if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
3212 /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
3213 * Treat it as plain text. */
3217 /* Right now, this function is going to be really stupid. It's
3218 going to examine the block of data, and make sure that 15%
3219 of the bytes are such that their value is in the ranges 0x07-0x0D
3220 or 0x20-0x7F, and that none of those bytes is 0x00. If those
3221 criteria are not met, we're calling it binary.
3223 NOTE: Originally, I intended to target 85% of the bytes being in
3224 the specified ranges, but I flubbed the condition. At any rate,
3225 folks aren't complaining, so I'm not sure that it's worth
3226 adjusting this retroactively now. --cmpilato */
3230 apr_size_t binary_count = 0;
3232 /* Run through the data we've read, counting the 'binary-ish'
3233 bytes. HINT: If we see a 0x00 byte, we'll set our count to its
3234 max and stop reading the file. */
3235 for (i = 0; i < len; i++)
3243 || ((buf[i] > 0x0D) && (buf[i] < 0x20))
3250 return (((binary_count * 1000) / len) > 850);
3258 svn_io_detect_mimetype(const char **mimetype,
3262 return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
3267 svn_io_file_open(apr_file_t **new_file, const char *fname,
3268 apr_int32_t flag, apr_fileperms_t perm,
3271 const char *fname_apr;
3272 apr_status_t status;
3274 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3275 status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
3279 return svn_error_wrap_apr(status, _("Can't open file '%s'"),
3280 svn_dirent_local_style(fname, pool));
3282 return SVN_NO_ERROR;
3286 static APR_INLINE svn_error_t *
3287 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
3288 const char *msg, const char *msg_no_name,
3295 return SVN_NO_ERROR;
3297 err = svn_io_file_name_get(&name, file, pool);
3300 svn_error_clear(err);
3302 /* ### Issue #3014: Return a specific error for broken pipes,
3303 * ### with a single element in the error chain. */
3304 if (SVN__APR_STATUS_IS_EPIPE(status))
3305 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
3308 return svn_error_wrap_apr(status, _(msg),
3309 try_utf8_from_internal_style(name, pool));
3311 return svn_error_wrap_apr(status, "%s", _(msg_no_name));
3316 svn_io_file_close(apr_file_t *file, apr_pool_t *pool)
3318 return do_io_file_wrapper_cleanup(file, apr_file_close(file),
3319 N_("Can't close file '%s'"),
3320 N_("Can't close stream"),
3326 svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
3328 return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file),
3329 N_("Can't read file '%s'"),
3330 N_("Can't read stream"),
3336 svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
3338 return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file),
3339 N_("Can't write file '%s'"),
3340 N_("Can't write stream"),
3346 svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
3347 apr_file_t *file, apr_pool_t *pool)
3349 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3350 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3352 return do_io_file_wrapper_cleanup(
3353 file, apr_file_info_get(finfo, wanted, file),
3354 N_("Can't get attribute information from file '%s'"),
3355 N_("Can't get attribute information from stream"),
3361 svn_io_file_read(apr_file_t *file, void *buf,
3362 apr_size_t *nbytes, apr_pool_t *pool)
3364 return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes),
3365 N_("Can't read file '%s'"),
3366 N_("Can't read stream"),
3372 svn_io_file_read_full2(apr_file_t *file, void *buf,
3373 apr_size_t nbytes, apr_size_t *bytes_read,
3374 svn_boolean_t *hit_eof,
3377 apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
3380 if (APR_STATUS_IS_EOF(status))
3383 return SVN_NO_ERROR;
3389 return do_io_file_wrapper_cleanup(file, status,
3390 N_("Can't read file '%s'"),
3391 N_("Can't read stream"),
3397 svn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
3398 apr_off_t *offset, apr_pool_t *pool)
3400 return do_io_file_wrapper_cleanup(
3401 file, apr_file_seek(file, where, offset),
3402 N_("Can't set position pointer in file '%s'"),
3403 N_("Can't set position pointer in stream"),
3409 svn_io_file_write(apr_file_t *file, const void *buf,
3410 apr_size_t *nbytes, apr_pool_t *pool)
3412 return svn_error_trace(do_io_file_wrapper_cleanup(
3413 file, apr_file_write(file, buf, nbytes),
3414 N_("Can't write to file '%s'"),
3415 N_("Can't write to stream"),
3421 svn_io_file_write_full(apr_file_t *file, const void *buf,
3422 apr_size_t nbytes, apr_size_t *bytes_written,
3425 /* We cannot simply call apr_file_write_full on Win32 as it may fail
3426 for larger values of NBYTES. In that case, we have to emulate the
3427 "_full" part here. Thus, always call apr_file_write directly on
3428 Win32 as this minimizes overhead for small data buffers. */
3430 #define MAXBUFSIZE 30*1024
3431 apr_size_t bw = nbytes;
3432 apr_size_t to_write = nbytes;
3434 /* try a simple "write everything at once" first */
3435 apr_status_t rv = apr_file_write(file, buf, &bw);
3436 buf = (char *)buf + bw;
3439 /* if the OS cannot handle that, use smaller chunks */
3440 if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
3441 && nbytes > MAXBUFSIZE)
3444 bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
3445 rv = apr_file_write(file, buf, &bw);
3446 buf = (char *)buf + bw;
3448 } while (rv == APR_SUCCESS && to_write > 0);
3451 /* bytes_written may actually be NULL */
3453 *bytes_written = nbytes - to_write;
3456 apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
3459 return svn_error_trace(do_io_file_wrapper_cleanup(
3461 N_("Can't write to file '%s'"),
3462 N_("Can't write to stream"),
3468 svn_io_write_unique(const char **tmp_path,
3469 const char *dirpath,
3472 svn_io_file_del_t delete_when,
3475 apr_file_t *new_file;
3478 SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
3479 delete_when, pool, pool));
3481 err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
3484 err = svn_io_file_flush_to_disk(new_file, pool);
3486 return svn_error_trace(
3487 svn_error_compose_create(err,
3488 svn_io_file_close(new_file, pool)));
3493 svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
3495 /* This is a work-around. APR would flush the write buffer
3496 _after_ truncating the file causing now invalid buffered
3497 data to be written behind OFFSET. */
3498 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
3499 N_("Can't flush file '%s'"),
3500 N_("Can't flush stream"),
3503 return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset),
3504 N_("Can't truncate file '%s'"),
3505 N_("Can't truncate stream"),
3511 svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
3515 apr_size_t total_read = 0;
3516 svn_boolean_t eof = FALSE;
3519 apr_size_t buf_size = *limit;
3521 while (buf_size > 0)
3523 /* read a fair chunk of data at once. But don't get too ambitious
3524 * as that would result in too much waste. Also make sure we can
3525 * put a NUL after the last byte read.
3527 apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
3528 apr_size_t bytes_read = 0;
3534 /* read data block (or just a part of it) */
3535 SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
3536 &bytes_read, &eof, pool));
3538 /* look or a newline char */
3539 buf[bytes_read] = 0;
3540 eol = strchr(buf, '\n');
3543 apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
3546 *limit = total_read + (eol - buf);
3548 /* correct the file pointer:
3549 * appear as though we just had read the newline char
3551 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
3553 return SVN_NO_ERROR;
3557 /* no EOL found but we hit the end of the file.
3558 * Generate a nice EOF error object and return it.
3561 SVN_ERR(svn_io_file_getc(&dummy, file, pool));
3564 /* next data chunk */
3565 buf_size -= bytes_read;
3567 total_read += bytes_read;
3570 /* buffer limit has been exceeded without finding the EOL */
3571 err = svn_io_file_name_get(&name, file, pool);
3574 svn_error_clear(err);
3577 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
3578 _("Can't read length line in file '%s'"),
3579 svn_dirent_local_style(name, pool));
3581 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
3582 _("Can't read length line in stream"));
3587 svn_io_stat(apr_finfo_t *finfo, const char *fname,
3588 apr_int32_t wanted, apr_pool_t *pool)
3590 apr_status_t status;
3591 const char *fname_apr;
3593 /* APR doesn't like "" directories */
3594 if (fname[0] == '\0')
3597 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3599 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3600 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3602 status = apr_stat(finfo, fname_apr, wanted, pool);
3604 return svn_error_wrap_apr(status, _("Can't stat '%s'"),
3605 svn_dirent_local_style(fname, pool));
3607 return SVN_NO_ERROR;
3612 svn_io_file_rename(const char *from_path, const char *to_path,
3615 apr_status_t status = APR_SUCCESS;
3616 const char *from_path_apr, *to_path_apr;
3618 SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
3619 SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
3621 status = apr_file_rename(from_path_apr, to_path_apr, pool);
3623 #if defined(WIN32) || defined(__OS2__)
3624 /* If the target file is read only NTFS reports EACCESS and
3625 FAT/FAT32 reports EEXIST */
3626 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
3628 /* Set the destination file writable because Windows will not
3629 allow us to rename when to_path is read-only, but will
3630 allow renaming when from_path is read only. */
3631 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
3633 status = apr_file_rename(from_path_apr, to_path_apr, pool);
3635 WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool));
3636 #endif /* WIN32 || __OS2__ */
3639 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
3640 svn_dirent_local_style(from_path, pool),
3641 svn_dirent_local_style(to_path, pool));
3643 return SVN_NO_ERROR;
3648 svn_io_file_move(const char *from_path, const char *to_path,
3651 svn_error_t *err = svn_io_file_rename(from_path, to_path, pool);
3653 if (err && APR_STATUS_IS_EXDEV(err->apr_err))
3655 const char *tmp_to_path;
3657 svn_error_clear(err);
3659 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_to_path,
3660 svn_dirent_dirname(to_path, pool),
3661 svn_io_file_del_none,
3664 err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool);
3668 err = svn_io_file_rename(tmp_to_path, to_path, pool);
3672 err = svn_io_remove_file2(from_path, FALSE, pool);
3674 return SVN_NO_ERROR;
3676 svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool));
3681 svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool));
3687 /* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
3688 HIDDEN determines if the hidden attribute
3689 should be set on the newly created directory. */
3690 static svn_error_t *
3691 dir_make(const char *path, apr_fileperms_t perm,
3692 svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
3694 apr_status_t status;
3695 const char *path_apr;
3697 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
3699 /* APR doesn't like "" directories */
3700 if (path_apr[0] == '\0')
3703 #if (APR_OS_DEFAULT & APR_WSTICKY)
3704 /* The APR shipped with httpd 2.0.50 contains a bug where
3705 APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
3706 There is a special case for file creation, but not directory
3707 creation, so directories wind up getting created with the sticky
3708 bit set. (There is no such thing as a setuid directory, and the
3709 setgid bit is apparently ignored at mkdir() time.) If we detect
3710 this problem, work around it by unsetting those bits if we are
3711 passed APR_OS_DEFAULT. */
3712 if (perm == APR_OS_DEFAULT)
3713 perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY);
3716 status = apr_dir_make(path_apr, perm, pool);
3717 WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
3720 return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
3721 svn_dirent_local_style(path, pool));
3723 #ifdef APR_FILE_ATTR_HIDDEN
3727 status = apr_file_attrs_set(path_apr,
3728 APR_FILE_ATTR_HIDDEN,
3729 APR_FILE_ATTR_HIDDEN,
3732 /* on Windows, use our wrapper so we can also set the
3733 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */
3734 status = io_win_file_attrs_set(path_apr,
3735 FILE_ATTRIBUTE_HIDDEN |
3736 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
3737 FILE_ATTRIBUTE_HIDDEN |
3738 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
3743 return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
3744 svn_dirent_local_style(path, pool));
3748 /* Windows does not implement sgid. Skip here because retrieving
3749 the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented
3750 to be 'incredibly expensive'. */
3756 /* Per our contract, don't do error-checking. Some filesystems
3757 * don't support the sgid bit, and that's okay. */
3758 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool);
3761 apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
3765 return SVN_NO_ERROR;
3769 svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
3771 return dir_make(path, perm, FALSE, FALSE, pool);
3775 svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
3778 return dir_make(path, perm, TRUE, FALSE, pool);
3782 svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
3785 return dir_make(path, perm, FALSE, TRUE, pool);
3790 svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
3792 apr_status_t status;
3793 const char *dirname_apr;
3795 /* APR doesn't like "" directories */
3796 if (dirname[0] == '\0')
3799 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3801 status = apr_dir_open(new_dir, dirname_apr, pool);
3803 return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
3804 svn_dirent_local_style(dirname, pool));
3806 return SVN_NO_ERROR;
3810 svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
3812 apr_status_t status;
3813 const char *dirname_apr;
3815 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3817 status = apr_dir_remove(dirname_apr, pool);
3821 svn_boolean_t retry = TRUE;
3823 if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY)
3825 apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
3827 if (APR_STATUS_IS_ENOTEMPTY(empty_status))
3833 WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
3838 return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
3839 svn_dirent_local_style(dirname, pool));
3841 return SVN_NO_ERROR;
3846 svn_io_dir_read(apr_finfo_t *finfo,
3851 apr_status_t status;
3853 status = apr_dir_read(finfo, wanted, thedir);
3856 return svn_error_wrap_apr(status, _("Can't read directory"));
3858 /* It would be nice to use entry_name_to_utf8() below, but can we
3859 get the dir's path out of an apr_dir_t? I don't see a reliable
3863 SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
3866 SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
3868 return SVN_NO_ERROR;
3872 svn_io_dir_close(apr_dir_t *thedir)
3874 apr_status_t apr_err = apr_dir_close(thedir);
3876 return svn_error_wrap_apr(apr_err, _("Error closing directory"));
3878 return SVN_NO_ERROR;
3882 svn_io_dir_walk2(const char *dirname,
3884 svn_io_walk_func_t walk_func,
3888 apr_status_t apr_err;
3890 apr_pool_t *subpool;
3891 const char *dirname_apr;
3894 wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
3896 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3897 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3899 /* The documentation for apr_dir_read used to state that "." and ".."
3900 will be returned as the first two files, but it doesn't
3901 work that way in practice, in particular ext3 on Linux-2.6 doesn't
3902 follow the rules. For details see
3903 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
3905 If APR ever does implement "dot-first" then it would be possible to
3906 remove the svn_io_stat and walk_func calls and use the walk_func
3909 Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
3910 documented to provide it, so we have to do a bit extra. */
3911 SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool));
3912 SVN_ERR(cstring_from_utf8(&finfo.name,
3913 svn_dirent_basename(dirname, pool),
3915 finfo.valid |= APR_FINFO_NAME;
3916 SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
3918 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3920 /* APR doesn't like "" directories */
3921 if (dirname_apr[0] == '\0')
3924 apr_err = apr_dir_open(&handle, dirname_apr, pool);
3926 return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
3927 svn_dirent_local_style(dirname, pool));
3929 /* iteration subpool */
3930 subpool = svn_pool_create(pool);
3934 const char *name_utf8;
3935 const char *full_path;
3937 svn_pool_clear(subpool);
3939 apr_err = apr_dir_read(&finfo, wanted, handle);
3940 if (APR_STATUS_IS_ENOENT(apr_err))
3944 return svn_error_wrap_apr(apr_err,
3945 _("Can't read directory entry in '%s'"),
3946 svn_dirent_local_style(dirname, pool));
3949 if (finfo.filetype == APR_DIR)
3951 if (finfo.name[0] == '.'
3952 && (finfo.name[1] == '\0'
3953 || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
3954 /* skip "." and ".." */
3957 /* some other directory. recurse. it will be passed to the
3958 callback inside the recursion. */
3959 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
3961 full_path = svn_dirent_join(dirname, name_utf8, subpool);
3962 SVN_ERR(svn_io_dir_walk2(full_path,
3968 else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
3970 /* some other directory. pass it to the callback. */
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((*walk_func)(walk_baton,
3980 Some other type of file; skip it for now. We've reserved the
3981 right to expand our coverage here in the future, though,
3982 without revving this API.
3986 svn_pool_destroy(subpool);
3988 apr_err = apr_dir_close(handle);
3990 return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
3991 svn_dirent_local_style(dirname, pool));
3993 return SVN_NO_ERROR;
3999 * Determine if a directory is empty or not.
4000 * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
4001 * @param path The directory.
4002 * @param pool Used for temporary allocation.
4003 * @remark If path is not a directory, or some other error occurs,
4004 * then return the appropriate apr status code.
4006 * (This function is written in APR style, in anticipation of
4007 * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
4010 dir_is_empty(const char *dir, apr_pool_t *pool)
4012 apr_status_t apr_err;
4013 apr_dir_t *dir_handle;
4015 apr_status_t retval = APR_SUCCESS;
4017 /* APR doesn't like "" directories */
4021 apr_err = apr_dir_open(&dir_handle, dir, pool);
4022 if (apr_err != APR_SUCCESS)
4025 for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle);
4026 apr_err == APR_SUCCESS;
4027 apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle))
4029 /* Ignore entries for this dir and its parent, robustly.
4030 (APR promises that they'll come first, so technically
4031 this guard could be moved outside the loop. But Ryan Bloom
4032 says he doesn't believe it, and I believe him. */
4033 if (! (finfo.name[0] == '.'
4034 && (finfo.name[1] == '\0'
4035 || (finfo.name[1] == '.' && finfo.name[2] == '\0'))))
4037 retval = APR_ENOTEMPTY;
4042 /* Make sure we broke out of the loop for the right reason. */
4043 if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
4046 apr_err = apr_dir_close(dir_handle);
4047 if (apr_err != APR_SUCCESS)
4055 svn_io_dir_empty(svn_boolean_t *is_empty_p,
4059 apr_status_t status;
4060 const char *path_apr;
4062 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4064 status = dir_is_empty(path_apr, pool);
4068 else if (APR_STATUS_IS_ENOTEMPTY(status))
4069 *is_empty_p = FALSE;
4071 return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
4072 svn_dirent_local_style(path, pool));
4074 return SVN_NO_ERROR;
4079 /*** Version/format files ***/
4082 svn_io_write_version_file(const char *path,
4086 const char *path_tmp;
4087 const char *format_contents = apr_psprintf(pool, "%d\n", version);
4089 SVN_ERR_ASSERT(version >= 0);
4091 SVN_ERR(svn_io_write_unique(&path_tmp,
4092 svn_dirent_dirname(path, pool),
4093 format_contents, strlen(format_contents),
4094 svn_io_file_del_none, pool));
4096 #if defined(WIN32) || defined(__OS2__)
4097 /* make the destination writable, but only on Windows, because
4098 Windows does not let us replace read-only files. */
4099 SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
4100 #endif /* WIN32 || __OS2__ */
4102 /* rename the temp file as the real destination */
4103 SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
4105 /* And finally remove the perms to make it read only */
4106 return svn_io_set_file_read_only(path, FALSE, pool);
4111 svn_io_read_version_file(int *version,
4115 apr_file_t *format_file;
4120 /* Read a chunk of data from PATH */
4121 SVN_ERR(svn_io_file_open(&format_file, path, APR_READ,
4122 APR_OS_DEFAULT, pool));
4124 err = svn_io_file_read(format_file, buf, &len, pool);
4126 /* Close the file. */
4127 SVN_ERR(svn_error_compose_create(err,
4128 svn_io_file_close(format_file, pool)));
4130 /* If there was no data in PATH, return an error. */
4132 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
4134 svn_dirent_local_style(path, pool));
4136 /* Check that the first line contains only digits. */
4140 for (i = 0; i < len; ++i)
4144 if (i > 0 && (c == '\r' || c == '\n'))
4149 if (! svn_ctype_isdigit(c))
4150 return svn_error_createf
4151 (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
4152 _("First line of '%s' contains non-digit"),
4153 svn_dirent_local_style(path, pool));
4157 /* Convert to integer. */
4158 SVN_ERR(svn_cstring_atoi(version, buf));
4160 return SVN_NO_ERROR;
4165 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
4166 static svn_error_t *
4167 contents_identical_p(svn_boolean_t *identical_p,
4173 apr_size_t bytes_read1, bytes_read2;
4174 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4175 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4176 apr_file_t *file1_h;
4177 apr_file_t *file2_h;
4178 svn_boolean_t eof1 = FALSE;
4179 svn_boolean_t eof2 = FALSE;
4181 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4184 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4188 return svn_error_trace(
4189 svn_error_compose_create(err,
4190 svn_io_file_close(file1_h, pool)));
4192 *identical_p = TRUE; /* assume TRUE, until disproved below */
4193 while (!err && !eof1 && !eof2)
4195 err = svn_io_file_read_full2(file1_h, buf1,
4196 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4201 err = svn_io_file_read_full2(file2_h, buf2,
4202 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4207 if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
4209 *identical_p = FALSE;
4214 /* Special case: one file being a prefix of the other and the shorter
4215 * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */
4216 if (!err && (eof1 != eof2))
4217 *identical_p = FALSE;
4219 return svn_error_trace(
4220 svn_error_compose_create(
4222 svn_error_compose_create(svn_io_file_close(file1_h, pool),
4223 svn_io_file_close(file2_h, pool))));
4228 /* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
4229 static svn_error_t *
4230 contents_three_identical_p(svn_boolean_t *identical_p12,
4231 svn_boolean_t *identical_p23,
4232 svn_boolean_t *identical_p13,
4236 apr_pool_t *scratch_pool)
4239 apr_size_t bytes_read1, bytes_read2, bytes_read3;
4240 char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4241 char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4242 char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4243 apr_file_t *file1_h;
4244 apr_file_t *file2_h;
4245 apr_file_t *file3_h;
4246 svn_boolean_t eof1 = FALSE;
4247 svn_boolean_t eof2 = FALSE;
4248 svn_boolean_t eof3 = FALSE;
4249 svn_boolean_t read_1, read_2, read_3;
4251 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4254 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4258 return svn_error_trace(
4259 svn_error_compose_create(err,
4260 svn_io_file_close(file1_h, scratch_pool)));
4262 err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
4266 return svn_error_trace(
4267 svn_error_compose_create(
4269 svn_error_compose_create(svn_io_file_close(file1_h,
4271 svn_io_file_close(file2_h,
4274 /* assume TRUE, until disproved below */
4275 *identical_p12 = *identical_p23 = *identical_p13 = TRUE;
4276 /* We need to read as long as no error occurs, and as long as one of the
4277 * flags could still change due to a read operation */
4279 && ((*identical_p12 && !eof1 && !eof2)
4280 || (*identical_p23 && !eof2 && !eof3)
4281 || (*identical_p13 && !eof1 && !eof3)))
4283 read_1 = read_2 = read_3 = FALSE;
4285 /* As long as a file is not at the end yet, and it is still
4286 * potentially identical to another file, we read the next chunk.*/
4287 if (!eof1 && (*identical_p12 || *identical_p13))
4289 err = svn_io_file_read_full2(file1_h, buf1,
4290 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4291 &eof1, scratch_pool);
4297 if (!eof2 && (*identical_p12 || *identical_p23))
4299 err = svn_io_file_read_full2(file2_h, buf2,
4300 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4301 &eof2, scratch_pool);
4307 if (!eof3 && (*identical_p13 || *identical_p23))
4309 err = svn_io_file_read_full2(file3_h, buf3,
4310 SVN__STREAM_CHUNK_SIZE, &bytes_read3,
4311 &eof3, scratch_pool);
4317 /* If the files are still marked identical, and at least one of them
4318 * is not at the end of file, we check whether they differ, and set
4319 * their flag to false then. */
4321 && (read_1 || read_2)
4323 || (bytes_read1 != bytes_read2)
4324 || memcmp(buf1, buf2, bytes_read1)))
4326 *identical_p12 = FALSE;
4330 && (read_2 || read_3)
4332 || (bytes_read2 != bytes_read3)
4333 || memcmp(buf2, buf3, bytes_read2)))
4335 *identical_p23 = FALSE;
4339 && (read_1 || read_3)
4341 || (bytes_read1 != bytes_read3)
4342 || memcmp(buf1, buf3, bytes_read3)))
4344 *identical_p13 = FALSE;
4348 return svn_error_trace(
4349 svn_error_compose_create(
4351 svn_error_compose_create(
4352 svn_io_file_close(file1_h, scratch_pool),
4353 svn_error_compose_create(
4354 svn_io_file_close(file2_h, scratch_pool),
4355 svn_io_file_close(file3_h, scratch_pool)))));
4361 svn_io_files_contents_same_p(svn_boolean_t *same,
4368 SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
4373 return SVN_NO_ERROR;
4376 SVN_ERR(contents_identical_p(&q, file1, file2, pool));
4383 return SVN_NO_ERROR;
4387 svn_io_files_contents_three_same_p(svn_boolean_t *same12,
4388 svn_boolean_t *same23,
4389 svn_boolean_t *same13,
4393 apr_pool_t *scratch_pool)
4395 svn_boolean_t diff_size12, diff_size23, diff_size13;
4397 SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
4405 if (diff_size12 && diff_size23 && diff_size13)
4407 *same12 = *same23 = *same13 = FALSE;
4409 else if (diff_size12 && diff_size23)
4411 *same12 = *same23 = FALSE;
4412 SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
4414 else if (diff_size23 && diff_size13)
4416 *same23 = *same13 = FALSE;
4417 SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
4419 else if (diff_size12 && diff_size13)
4421 *same12 = *same13 = FALSE;
4422 SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
4426 SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
4427 SVN_ERR(contents_three_identical_p(same12, same23, same13,
4428 file1, file2, file3,
4432 return SVN_NO_ERROR;
4436 /* Counter value of file_mktemp request (used in a threadsafe way), to make
4437 sure that a single process normally never generates the same tempname
4439 static volatile apr_uint32_t tempname_counter = 0;
4442 /* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
4443 Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name.
4444 Perform temporary allocations in SCRATCH_POOL and the result in
4446 static svn_error_t *
4447 temp_file_create(apr_file_t **new_file,
4448 const char **new_file_name,
4449 const char *directory,
4451 apr_pool_t *result_pool,
4452 apr_pool_t *scratch_pool)
4455 const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
4456 const char *templ_apr;
4457 apr_status_t status;
4459 SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
4461 /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
4462 data available in POOL and we need a non-const pointer here,
4463 as apr changes the template to return the new filename. */
4464 status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool);
4467 return svn_error_wrap_apr(status, _("Can't create temporary file from "
4468 "template '%s'"), templ);
4470 /* Translate the returned path back to utf-8 before returning it */
4471 return svn_error_trace(svn_path_cstring_to_utf8(new_file_name,
4475 /* The Windows implementation of apr_file_mktemp doesn't handle access
4476 denied errors correctly. Therefore we implement our own temp file
4477 creation function here. */
4479 /* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
4480 ### the function we used before. But we try to guess a more unique
4481 ### name before trying if it exists. */
4483 /* Offset by some time value and a unique request nr to make the number
4484 +- unique for both this process and on the computer */
4485 int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter)
4486 + GetCurrentProcessId();
4489 /* ### Maybe use an iterpool? */
4490 for (i = 0; i <= 99999; i++)
4492 apr_uint32_t unique_nr;
4493 const char *unique_name;
4494 const char *unique_name_apr;
4495 apr_file_t *try_file;
4496 apr_status_t apr_err;
4498 /* Generate a number that should be unique for this application and
4499 usually for the entire computer to reduce the number of cycles
4500 through this loop. (A bit of calculation is much cheaper then
4502 unique_nr = baseNr + 3 * i;
4504 unique_name = svn_dirent_join(directory,
4505 apr_psprintf(scratch_pool, "svn-%X",
4509 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
4511 apr_err = file_open(&try_file, unique_name_apr, flags,
4512 APR_OS_DEFAULT, FALSE, scratch_pool);
4514 if (APR_STATUS_IS_EEXIST(apr_err))
4518 /* On Win32, CreateFile fails with an "Access Denied" error
4519 code, rather than "File Already Exists", if the colliding
4520 name belongs to a directory. */
4522 if (APR_STATUS_IS_EACCES(apr_err))
4525 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
4526 APR_FINFO_TYPE, scratch_pool);
4528 if (!apr_err_2 && finfo.filetype == APR_DIR)
4531 apr_err_2 = APR_TO_OS_ERROR(apr_err);
4533 if (apr_err_2 == ERROR_ACCESS_DENIED ||
4534 apr_err_2 == ERROR_SHARING_VIOLATION)
4536 /* The file is in use by another process or is hidden;
4537 create a new name, but don't do this 99999 times in
4538 case the folder is not writable */
4543 /* Else fall through and return the original error. */
4546 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
4547 svn_dirent_local_style(unique_name,
4552 /* Move file to the right pool */
4553 apr_err = apr_file_setaside(new_file, try_file, result_pool);
4556 return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
4557 svn_dirent_local_style(unique_name,
4560 *new_file_name = apr_pstrdup(result_pool, unique_name);
4562 return SVN_NO_ERROR;
4566 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
4568 _("Unable to make name in '%s'"),
4569 svn_dirent_local_style(directory, scratch_pool));
4573 /* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
4575 svn_io_file_name_get(const char **filename,
4579 const char *fname_apr;
4580 apr_status_t status;
4582 status = apr_file_name_get(&fname_apr, file);
4584 return svn_error_wrap_apr(status, _("Can't get file name"));
4587 SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
4591 return SVN_NO_ERROR;
4596 svn_io_open_unique_file3(apr_file_t **file,
4597 const char **unique_path,
4598 const char *dirpath,
4599 svn_io_file_del_t delete_when,
4600 apr_pool_t *result_pool,
4601 apr_pool_t *scratch_pool)
4603 apr_file_t *tempfile;
4604 const char *tempname;
4605 struct temp_file_cleanup_s *baton = NULL;
4606 apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
4607 APR_BUFFERED | APR_BINARY);
4608 #if !defined(WIN32) && !defined(__OS2__)
4609 apr_fileperms_t perms;
4610 svn_boolean_t using_system_temp_dir = FALSE;
4613 SVN_ERR_ASSERT(file || unique_path);
4617 *unique_path = NULL;
4619 if (dirpath == NULL)
4621 #if !defined(WIN32) && !defined(__OS2__)
4622 using_system_temp_dir = TRUE;
4624 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
4627 switch (delete_when)
4629 case svn_io_file_del_on_pool_cleanup:
4630 baton = apr_palloc(result_pool, sizeof(*baton));
4631 baton->pool = result_pool;
4632 baton->fname_apr = NULL;
4634 /* Because cleanups are run LIFO, we need to make sure to register
4635 our cleanup before the apr_file_close cleanup:
4637 On Windows, you can't remove an open file.
4639 apr_pool_cleanup_register(result_pool, baton,
4640 temp_file_plain_cleanup_handler,
4641 temp_file_child_cleanup_handler);
4644 case svn_io_file_del_on_close:
4645 flags |= APR_DELONCLOSE;
4651 SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
4652 result_pool, scratch_pool));
4654 #if !defined(WIN32) && !defined(__OS2__)
4655 /* apr_file_mktemp() creates files with mode 0600.
4656 * This is appropriate if we're using a system temp dir since we don't
4657 * want to leak sensitive data into temp files other users can read.
4658 * If we're not using a system temp dir we're probably using the
4659 * .svn/tmp area and it's likely that the tempfile will end up being
4660 * copied or renamed into the working copy.
4661 * This would cause working files having mode 0600 while users might
4662 * expect to see 0644 or 0664. So we tweak perms of the tempfile in this
4663 * case, but only if the umask allows it. */
4664 if (!using_system_temp_dir)
4666 SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
4667 SVN_ERR(file_perms_set2(tempfile, perms, scratch_pool));
4674 SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
4677 *unique_path = tempname; /* Was allocated in result_pool */
4680 SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
4682 return SVN_NO_ERROR;
4686 svn_io_file_readline(apr_file_t *file,
4687 svn_stringbuf_t **stringbuf,
4691 apr_pool_t *result_pool,
4692 apr_pool_t *scratch_pool)
4694 svn_stringbuf_t *str;
4695 const char *eol_str;
4696 apr_size_t numbytes;
4699 svn_boolean_t found_eof;
4701 str = svn_stringbuf_create_ensure(80, result_pool);
4703 /* Read bytes into STR up to and including, but not storing,
4704 * the next EOL sequence. */
4712 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4713 &found_eof, scratch_pool));
4715 if (numbytes != 1 || len > max_len)
4729 if (!found_eof && len < max_len)
4733 /* Check for "\r\n" by peeking at the next byte. */
4735 SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool));
4736 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4737 &found_eof, scratch_pool));
4738 if (numbytes == 1 && c == '\n')
4745 /* Pretend we never peeked. */
4746 SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
4753 svn_stringbuf_appendbyte(str, c);
4765 return SVN_NO_ERROR;