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. */
1538 /* Get the perms for the original file so we'll have any other bits
1539 * that were already set (like the execute bits, for example). */
1540 SVN_ERR(svn_io_file_open(&fd, path, APR_READ,
1541 APR_OS_DEFAULT, pool));
1542 SVN_ERR(merge_default_file_perms(fd, &perms_to_set, pool));
1543 SVN_ERR(svn_io_file_close(fd, pool));
1547 if (finfo.protection & APR_UREAD)
1548 perms_to_set &= ~APR_UWRITE;
1549 if (finfo.protection & APR_GREAD)
1550 perms_to_set &= ~APR_GWRITE;
1551 if (finfo.protection & APR_WREAD)
1552 perms_to_set &= ~APR_WWRITE;
1556 if (change_executable)
1560 if (finfo.protection & APR_UREAD)
1561 perms_to_set |= APR_UEXECUTE;
1562 if (finfo.protection & APR_GREAD)
1563 perms_to_set |= APR_GEXECUTE;
1564 if (finfo.protection & APR_WREAD)
1565 perms_to_set |= APR_WEXECUTE;
1569 if (finfo.protection & APR_UREAD)
1570 perms_to_set &= ~APR_UEXECUTE;
1571 if (finfo.protection & APR_GREAD)
1572 perms_to_set &= ~APR_GEXECUTE;
1573 if (finfo.protection & APR_WREAD)
1574 perms_to_set &= ~APR_WEXECUTE;
1578 /* If we aren't changing anything then just return, this saves
1579 some system calls and helps with shared working copies */
1580 if (perms_to_set == finfo.protection)
1581 return SVN_NO_ERROR;
1583 status = apr_file_perms_set(path_apr, perms_to_set);
1585 return SVN_NO_ERROR;
1587 if (APR_STATUS_IS_EPERM(status))
1589 /* We don't have permissions to change the
1590 permissions! Try a move, copy, and delete
1591 workaround to see if we can get the file owned by
1592 us. If these succeed, try the permissions set
1595 Note that we only attempt this in the
1596 stat-available path. This assumes that the
1597 move-copy workaround will only be helpful on
1598 platforms that implement apr_stat. */
1599 SVN_ERR(reown_file(path, pool));
1600 status = apr_file_perms_set(path_apr, perms_to_set);
1604 return SVN_NO_ERROR;
1606 if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1607 return SVN_NO_ERROR;
1608 else if (status == APR_ENOTIMPL)
1610 /* At least try to set the attributes. */
1611 apr_fileattrs_t attrs = 0;
1612 apr_fileattrs_t attrs_values = 0;
1614 if (change_readwrite)
1616 attrs = APR_FILE_ATTR_READONLY;
1618 attrs_values = APR_FILE_ATTR_READONLY;
1620 if (change_executable)
1622 attrs = APR_FILE_ATTR_EXECUTABLE;
1624 attrs_values = APR_FILE_ATTR_EXECUTABLE;
1626 status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
1629 return svn_error_wrap_apr(status,
1630 _("Can't change perms of file '%s'"),
1631 svn_dirent_local_style(path, pool));
1633 #endif /* !WIN32 && !__OS2__ */
1636 #if APR_HAS_UNICODE_FS
1637 /* copy of the apr function utf8_to_unicode_path since apr doesn't export this one */
1638 static apr_status_t io_utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen,
1641 /* TODO: The computations could preconvert the string to determine
1642 * the true size of the retstr, but that's a memory over speed
1643 * tradeoff that isn't appropriate this early in development.
1645 * Allocate the maximum string length based on leading 4
1646 * characters of \\?\ (allowing nearly unlimited path lengths)
1647 * plus the trailing null, then transform /'s into \\'s since
1648 * the \\?\ form doesn't allow '/' path separators.
1650 * Note that the \\?\ form only works for local drive paths, and
1651 * \\?\UNC\ is needed UNC paths.
1653 apr_size_t srcremains = strlen(srcstr) + 1;
1654 apr_wchar_t *t = retstr;
1657 /* This is correct, we don't twist the filename if it will
1658 * definitely be shorter than 248 characters. It merits some
1659 * performance testing to see if this has any effect, but there
1660 * seem to be applications that get confused by the resulting
1661 * Unicode \\?\ style file names, especially if they use argv[0]
1662 * or call the Win32 API functions such as GetModuleName, etc.
1663 * Not every application is prepared to handle such names.
1665 * Note also this is shorter than MAX_PATH, as directory paths
1666 * are actually limited to 248 characters.
1668 * Note that a utf-8 name can never result in more wide chars
1669 * than the original number of utf-8 narrow chars.
1671 if (srcremains > 248) {
1672 if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
1673 wcscpy (retstr, L"\\\\?\\");
1677 else if ((srcstr[0] == '/' || srcstr[0] == '\\')
1678 && (srcstr[1] == '/' || srcstr[1] == '\\')
1679 && (srcstr[2] != '?')) {
1680 /* Skip the slashes */
1683 wcscpy (retstr, L"\\\\?\\UNC\\");
1689 if (rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen)) {
1690 return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
1693 return APR_ENAMETOOLONG;
1702 static apr_status_t io_win_file_attrs_set(const char *fname,
1707 /* this is an implementation of apr_file_attrs_set() but one
1708 that uses the proper Windows attributes instead of the apr
1709 attributes. This way, we can apply any Windows file and
1710 folder attributes even if apr doesn't implement them */
1713 #if APR_HAS_UNICODE_FS
1714 apr_wchar_t wfname[APR_PATH_MAX];
1717 #if APR_HAS_UNICODE_FS
1718 IF_WIN_OS_IS_UNICODE
1720 if (rv = io_utf8_to_unicode_path(wfname,
1721 sizeof(wfname) / sizeof(wfname[0]),
1724 flags = GetFileAttributesW(wfname);
1730 flags = GetFileAttributesA(fname);
1734 if (flags == 0xFFFFFFFF)
1735 return apr_get_os_error();
1737 flags &= ~attr_mask;
1738 flags |= (attributes & attr_mask);
1740 #if APR_HAS_UNICODE_FS
1741 IF_WIN_OS_IS_UNICODE
1743 rv = SetFileAttributesW(wfname, flags);
1749 rv = SetFileAttributesA(fname, flags);
1754 return apr_get_os_error();
1762 svn_io_set_file_read_write_carefully(const char *path,
1763 svn_boolean_t enable_write,
1764 svn_boolean_t ignore_enoent,
1768 return svn_io_set_file_read_write(path, ignore_enoent, pool);
1769 return svn_io_set_file_read_only(path, ignore_enoent, pool);
1773 svn_io_set_file_read_only(const char *path,
1774 svn_boolean_t ignore_enoent,
1777 /* On Windows and OS/2, just set the file attributes -- on unix call
1778 our internal function which attempts to honor the umask. */
1779 #if !defined(WIN32) && !defined(__OS2__)
1780 return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE,
1781 ignore_enoent, pool);
1783 apr_status_t status;
1784 const char *path_apr;
1786 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1788 status = apr_file_attrs_set(path_apr,
1789 APR_FILE_ATTR_READONLY,
1790 APR_FILE_ATTR_READONLY,
1793 if (status && status != APR_ENOTIMPL)
1794 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
1795 return svn_error_wrap_apr(status,
1796 _("Can't set file '%s' read-only"),
1797 svn_dirent_local_style(path, pool));
1799 return SVN_NO_ERROR;
1805 svn_io_set_file_read_write(const char *path,
1806 svn_boolean_t ignore_enoent,
1809 /* On Windows and OS/2, just set the file attributes -- on unix call
1810 our internal function which attempts to honor the umask. */
1811 #if !defined(WIN32) && !defined(__OS2__)
1812 return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE,
1813 ignore_enoent, pool);
1815 apr_status_t status;
1816 const char *path_apr;
1818 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1820 status = apr_file_attrs_set(path_apr,
1822 APR_FILE_ATTR_READONLY,
1825 if (status && status != APR_ENOTIMPL)
1826 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
1827 return svn_error_wrap_apr(status,
1828 _("Can't set file '%s' read-write"),
1829 svn_dirent_local_style(path, pool));
1831 return SVN_NO_ERROR;
1836 svn_io_set_file_executable(const char *path,
1837 svn_boolean_t executable,
1838 svn_boolean_t ignore_enoent,
1841 /* On Windows and OS/2, just exit -- on unix call our internal function
1842 which attempts to honor the umask. */
1843 #if (!defined(WIN32) && !defined(__OS2__))
1844 return io_set_file_perms(path, FALSE, FALSE, TRUE, executable,
1845 ignore_enoent, pool);
1847 return SVN_NO_ERROR;
1853 svn_io__is_finfo_read_only(svn_boolean_t *read_only,
1854 apr_finfo_t *file_info,
1857 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1858 apr_status_t apr_err;
1864 apr_err = apr_uid_current(&uid, &gid, pool);
1867 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
1869 /* Check write bit for current user. */
1870 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
1871 *read_only = !(file_info->protection & APR_UWRITE);
1873 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1874 *read_only = !(file_info->protection & APR_GWRITE);
1877 *read_only = !(file_info->protection & APR_WWRITE);
1879 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1880 *read_only = (file_info->protection & APR_FREADONLY);
1883 return SVN_NO_ERROR;
1887 svn_io__is_finfo_executable(svn_boolean_t *executable,
1888 apr_finfo_t *file_info,
1891 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1892 apr_status_t apr_err;
1896 *executable = FALSE;
1898 apr_err = apr_uid_current(&uid, &gid, pool);
1901 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
1903 /* Check executable bit for current user. */
1904 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
1905 *executable = (file_info->protection & APR_UEXECUTE);
1907 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1908 *executable = (file_info->protection & APR_GEXECUTE);
1911 *executable = (file_info->protection & APR_WEXECUTE);
1913 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1914 *executable = FALSE;
1917 return SVN_NO_ERROR;
1921 svn_io_is_file_executable(svn_boolean_t *executable,
1925 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1926 apr_finfo_t file_info;
1928 SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
1930 SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
1932 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1933 *executable = FALSE;
1936 return SVN_NO_ERROR;
1940 /*** File locking. ***/
1941 #if !defined(WIN32) && !defined(__OS2__)
1942 /* Clear all outstanding locks on ARG, an open apr_file_t *. */
1944 file_clear_locks(void *arg)
1946 apr_status_t apr_err;
1947 apr_file_t *f = arg;
1950 apr_err = apr_file_unlock(f);
1959 svn_io_lock_open_file(apr_file_t *lockfile_handle,
1960 svn_boolean_t exclusive,
1961 svn_boolean_t nonblocking,
1964 int locktype = APR_FLOCK_SHARED;
1965 apr_status_t apr_err;
1969 locktype = APR_FLOCK_EXCLUSIVE;
1971 locktype |= APR_FLOCK_NONBLOCK;
1973 /* We need this only in case of an error but this is cheap to get -
1974 * so we do it here for clarity. */
1975 apr_err = apr_file_name_get(&fname, lockfile_handle);
1977 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
1979 /* Get lock on the filehandle. */
1980 apr_err = apr_file_lock(lockfile_handle, locktype);
1982 /* In deployments with two or more multithreaded servers running on
1983 the same system serving two or more fsfs repositories it is
1984 possible for a deadlock to occur when getting a write lock on
1985 db/txn-current-lock:
1989 thread 1: get lock in repos A
1990 thread 1: get lock in repos B
1991 thread 2: block getting lock in repos A
1992 thread 2: try to get lock in B *** deadlock ***
1994 Retry for a while for the deadlock to clear. */
1995 FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
1999 switch (locktype & APR_FLOCK_TYPEMASK)
2001 case APR_FLOCK_SHARED:
2002 return svn_error_wrap_apr(apr_err,
2003 _("Can't get shared lock on file '%s'"),
2004 try_utf8_from_internal_style(fname, pool));
2005 case APR_FLOCK_EXCLUSIVE:
2006 return svn_error_wrap_apr(apr_err,
2007 _("Can't get exclusive lock on file '%s'"),
2008 try_utf8_from_internal_style(fname, pool));
2010 SVN_ERR_MALFUNCTION();
2014 /* On Windows and OS/2 file locks are automatically released when
2015 the file handle closes */
2016 #if !defined(WIN32) && !defined(__OS2__)
2017 apr_pool_cleanup_register(pool, lockfile_handle,
2019 apr_pool_cleanup_null);
2022 return SVN_NO_ERROR;
2026 svn_io_unlock_open_file(apr_file_t *lockfile_handle,
2030 apr_status_t apr_err;
2032 /* We need this only in case of an error but this is cheap to get -
2033 * so we do it here for clarity. */
2034 apr_err = apr_file_name_get(&fname, lockfile_handle);
2036 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2038 /* The actual unlock attempt. */
2039 apr_err = apr_file_unlock(lockfile_handle);
2041 return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
2042 try_utf8_from_internal_style(fname, pool));
2044 /* On Windows and OS/2 file locks are automatically released when
2045 the file handle closes */
2046 #if !defined(WIN32) && !defined(__OS2__)
2047 apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks);
2050 return SVN_NO_ERROR;
2054 svn_io_file_lock2(const char *lock_file,
2055 svn_boolean_t exclusive,
2056 svn_boolean_t nonblocking,
2059 int locktype = APR_FLOCK_SHARED;
2060 apr_file_t *lockfile_handle;
2064 locktype = APR_FLOCK_EXCLUSIVE;
2067 if (locktype == APR_FLOCK_EXCLUSIVE)
2070 /* locktype is never read after this block, so we don't need to bother
2071 setting it. If that were to ever change, uncomment the following
2074 locktype |= APR_FLOCK_NONBLOCK;
2077 SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
2081 /* Get lock on the filehandle. */
2082 return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
2087 /* Data consistency/coherency operations. */
2089 svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
2092 apr_os_file_t filehand;
2094 /* First make sure that any user-space buffered data is flushed. */
2095 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
2096 N_("Can't flush file '%s'"),
2097 N_("Can't flush stream"),
2100 apr_os_file_get(&filehand, file);
2102 /* Call the operating system specific function to actually force the
2107 if (! FlushFileBuffers(filehand))
2108 return svn_error_wrap_apr(apr_get_os_error(),
2109 _("Can't flush file to disk"));
2115 rv = fsync(filehand);
2116 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
2118 /* If the file is in a memory filesystem, fsync() may return
2119 EINVAL. Presumably the user knows the risks, and we can just
2120 ignore the error. */
2121 if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
2122 return SVN_NO_ERROR;
2125 return svn_error_wrap_apr(apr_get_os_error(),
2126 _("Can't flush file to disk"));
2130 return SVN_NO_ERROR;
2135 /* TODO write test for these two functions, then refactor. */
2137 /* Set RESULT to an svn_stringbuf_t containing the contents of FILE.
2138 FILENAME is the FILE's on-disk APR-safe name, or NULL if that name
2139 isn't known. If CHECK_SIZE is TRUE, the function will attempt to
2140 first stat() the file to determine it's size before sucking its
2141 contents into the stringbuf. (Doing so can prevent unnecessary
2142 memory usage, an unwanted side effect of the stringbuf growth and
2143 reallocation mechanism.) */
2144 static svn_error_t *
2145 stringbuf_from_aprfile(svn_stringbuf_t **result,
2146 const char *filename,
2148 svn_boolean_t check_size,
2153 svn_stringbuf_t *res = NULL;
2154 apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE;
2155 char *buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
2157 /* If our caller wants us to check the size of the file for
2158 efficient memory handling, we'll try to do so. */
2161 apr_status_t status;
2163 /* If our caller didn't tell us the file's name, we'll ask APR
2164 if it knows the name. No problem if we can't figure it out. */
2167 const char *filename_apr;
2168 if (! (status = apr_file_name_get(&filename_apr, file)))
2169 filename = filename_apr;
2172 /* If we now know the filename, try to stat(). If we succeed,
2173 we know how to allocate our stringbuf. */
2177 if (! (status = apr_stat(&finfo, filename, APR_FINFO_MIN, pool)))
2178 res_initial_len = (apr_size_t)finfo.size;
2183 /* XXX: We should check the incoming data for being of type binary. */
2185 res = svn_stringbuf_create_ensure(res_initial_len, pool);
2187 /* apr_file_read will not return data and eof in the same call. So this loop
2188 * is safe from missing read data. */
2189 len = SVN__STREAM_CHUNK_SIZE;
2190 err = svn_io_file_read(file, buf, &len, pool);
2193 svn_stringbuf_appendbytes(res, buf, len);
2194 len = SVN__STREAM_CHUNK_SIZE;
2195 err = svn_io_file_read(file, buf, &len, pool);
2198 /* Having read all the data we *expect* EOF */
2199 if (err && !APR_STATUS_IS_EOF(err->apr_err))
2201 svn_error_clear(err);
2204 return SVN_NO_ERROR;
2208 svn_stringbuf_from_file2(svn_stringbuf_t **result,
2209 const char *filename,
2214 if (filename[0] == '-' && filename[1] == '\0')
2216 apr_status_t apr_err;
2217 if ((apr_err = apr_file_open_stdin(&f, pool)))
2218 return svn_error_wrap_apr(apr_err, _("Can't open stdin"));
2219 SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool));
2223 SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool));
2224 SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool));
2226 return svn_io_file_close(f, pool);
2231 svn_stringbuf_from_file(svn_stringbuf_t **result,
2232 const char *filename,
2235 if (filename[0] == '-' && filename[1] == '\0')
2236 return svn_error_create
2237 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2238 _("Reading from stdin is disallowed"));
2239 return svn_stringbuf_from_file2(result, filename, pool);
2243 svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
2247 return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
2255 svn_io_remove_file2(const char *path,
2256 svn_boolean_t ignore_enoent,
2257 apr_pool_t *scratch_pool)
2259 apr_status_t apr_err;
2260 const char *path_apr;
2262 SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
2264 apr_err = apr_file_remove(path_apr, scratch_pool);
2267 && (APR_STATUS_IS_ENOENT(apr_err)
2268 || SVN__APR_STATUS_IS_ENOTDIR(apr_err))))
2269 return SVN_NO_ERROR;
2272 /* If the target is read only NTFS reports EACCESS and FAT/FAT32
2274 if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
2276 /* Set the destination file writable because Windows will not
2277 allow us to delete when path is read-only */
2278 SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool));
2279 apr_err = apr_file_remove(path_apr, scratch_pool);
2282 return SVN_NO_ERROR;
2286 apr_status_t os_err = APR_TO_OS_ERROR(apr_err);
2287 /* Check to make sure we aren't trying to delete a directory */
2288 if (os_err == ERROR_ACCESS_DENIED || os_err == ERROR_SHARING_VIOLATION)
2292 if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
2293 && finfo.filetype == APR_REG)
2295 WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr,
2300 /* Just return the delete error */
2305 return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
2306 svn_dirent_local_style(path, scratch_pool));
2308 return SVN_NO_ERROR;
2313 svn_io_remove_dir(const char *path, apr_pool_t *pool)
2315 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
2319 Mac OS X has a bug where if you're reading the contents of a
2320 directory via readdir in a loop, and you remove one of the entries in
2321 the directory and the directory has 338 or more files in it you will
2322 skip over some of the entries in the directory. Needless to say,
2323 this causes problems if you are using this kind of loop inside a
2324 function that is recursively deleting a directory, because when you
2325 get around to removing the directory it will still have something in
2326 it. A similar problem has been observed in other BSDs. This bug has
2327 since been fixed. See http://www.vnode.ch/fixing_seekdir for details.
2329 The workaround is to delete the files only _after_ the initial
2330 directory scan. A previous workaround involving rewinddir is
2331 problematic on Win32 and some NFS clients, notably NetBSD.
2333 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
2334 http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
2337 /* Neither windows nor unix allows us to delete a non-empty
2340 This is a function to perform the equivalent of 'rm -rf'. */
2342 svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
2343 svn_cancel_func_t cancel_func, void *cancel_baton,
2347 apr_pool_t *subpool;
2348 apr_hash_t *dirents;
2349 apr_hash_index_t *hi;
2351 /* Check for pending cancellation request.
2352 If we need to bail out, do so early. */
2355 SVN_ERR((*cancel_func)(cancel_baton));
2357 subpool = svn_pool_create(pool);
2359 err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
2362 /* if the directory doesn't exist, our mission is accomplished */
2363 if (ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err))
2365 svn_error_clear(err);
2366 return SVN_NO_ERROR;
2368 return svn_error_trace(err);
2371 for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
2373 const char *name = svn__apr_hash_index_key(hi);
2374 const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
2375 const char *fullpath;
2377 fullpath = svn_dirent_join(path, name, subpool);
2378 if (dirent->kind == svn_node_dir)
2380 /* Don't check for cancellation, the callee will immediately do so */
2381 SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func,
2382 cancel_baton, subpool));
2387 SVN_ERR((*cancel_func)(cancel_baton));
2389 err = svn_io_remove_file2(fullpath, FALSE, subpool);
2391 return svn_error_createf
2392 (err->apr_err, err, _("Can't remove '%s'"),
2393 svn_dirent_local_style(fullpath, subpool));
2397 svn_pool_destroy(subpool);
2399 return svn_io_dir_remove_nonrecursive(path, pool);
2403 svn_io_get_dir_filenames(apr_hash_t **dirents,
2407 return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
2412 svn_io_dirent2_create(apr_pool_t *result_pool)
2414 svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
2416 /*dirent->kind = svn_node_none;
2417 dirent->special = FALSE;*/
2418 dirent->filesize = SVN_INVALID_FILESIZE;
2419 /*dirent->mtime = 0;*/
2425 svn_io_dirent2_dup(const svn_io_dirent2_t *item,
2426 apr_pool_t *result_pool)
2428 return apr_pmemdup(result_pool,
2434 svn_io_get_dirents3(apr_hash_t **dirents,
2436 svn_boolean_t only_check_type,
2437 apr_pool_t *result_pool,
2438 apr_pool_t *scratch_pool)
2440 apr_status_t status;
2441 apr_dir_t *this_dir;
2442 apr_finfo_t this_entry;
2443 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
2445 if (!only_check_type)
2446 flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
2448 *dirents = apr_hash_make(result_pool);
2450 SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
2452 for (status = apr_dir_read(&this_entry, flags, this_dir);
2453 status == APR_SUCCESS;
2454 status = apr_dir_read(&this_entry, flags, this_dir))
2456 if ((this_entry.name[0] == '.')
2457 && ((this_entry.name[1] == '\0')
2458 || ((this_entry.name[1] == '.')
2459 && (this_entry.name[2] == '\0'))))
2466 svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
2468 SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
2470 map_apr_finfo_to_node_kind(&(dirent->kind),
2474 if (!only_check_type)
2476 dirent->filesize = this_entry.size;
2477 dirent->mtime = this_entry.mtime;
2480 svn_hash_sets(*dirents, name, dirent);
2484 if (! (APR_STATUS_IS_ENOENT(status)))
2485 return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
2486 svn_dirent_local_style(path, scratch_pool));
2488 status = apr_dir_close(this_dir);
2490 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
2491 svn_dirent_local_style(path, scratch_pool));
2493 return SVN_NO_ERROR;
2497 svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
2499 svn_boolean_t verify_truename,
2500 svn_boolean_t ignore_enoent,
2501 apr_pool_t *result_pool,
2502 apr_pool_t *scratch_pool)
2505 svn_io_dirent2_t *dirent;
2507 apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
2508 | APR_FINFO_SIZE | APR_FINFO_MTIME;
2510 #if defined(WIN32) || defined(__OS2__)
2511 if (verify_truename)
2512 wanted |= APR_FINFO_NAME;
2515 err = svn_io_stat(&finfo, path, wanted, scratch_pool);
2517 if (err && ignore_enoent &&
2518 (APR_STATUS_IS_ENOENT(err->apr_err)
2519 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2521 svn_error_clear(err);
2522 dirent = svn_io_dirent2_create(result_pool);
2523 SVN_ERR_ASSERT(dirent->kind == svn_node_none);
2526 return SVN_NO_ERROR;
2530 #if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
2531 if (verify_truename)
2533 const char *requested_name = svn_dirent_basename(path, NULL);
2535 if (requested_name[0] == '\0')
2537 /* No parent directory. No need to stat/verify */
2539 #if defined(WIN32) || defined(__OS2__)
2540 else if (finfo.name)
2542 const char *name_on_disk;
2543 SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
2546 if (strcmp(name_on_disk, requested_name) /* != 0 */)
2550 *dirent_p = svn_io_dirent2_create(result_pool);
2551 return SVN_NO_ERROR;
2554 return svn_error_createf(APR_ENOENT, NULL,
2555 _("Path '%s' not found, case obstructed by '%s'"),
2556 svn_dirent_local_style(path, scratch_pool),
2560 #elif defined(DARWIN)
2561 /* Currently apr doesn't set finfo.name on DARWIN, returning
2563 ### Can we optimize this in another way? */
2566 apr_hash_t *dirents;
2568 err = svn_io_get_dirents3(&dirents,
2569 svn_dirent_dirname(path, scratch_pool),
2570 TRUE /* only_check_type */,
2571 scratch_pool, scratch_pool);
2573 if (err && ignore_enoent
2574 && (APR_STATUS_IS_ENOENT(err->apr_err)
2575 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2577 svn_error_clear(err);
2579 *dirent_p = svn_io_dirent2_create(result_pool);
2580 return SVN_NO_ERROR;
2585 if (! svn_hash_gets(dirents, requested_name))
2589 *dirent_p = svn_io_dirent2_create(result_pool);
2590 return SVN_NO_ERROR;
2593 return svn_error_createf(APR_ENOENT, NULL,
2594 _("Path '%s' not found"),
2595 svn_dirent_local_style(path, scratch_pool));
2602 dirent = svn_io_dirent2_create(result_pool);
2603 map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
2605 dirent->filesize = finfo.size;
2606 dirent->mtime = finfo.mtime;
2610 return SVN_NO_ERROR;
2613 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2614 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2616 /* Handle an error from the child process (before command execution) by
2617 printing DESC and the error string corresponding to STATUS to stderr. */
2619 handle_child_process_error(apr_pool_t *pool, apr_status_t status,
2623 apr_file_t *errfile;
2626 /* We can't do anything if we get an error here, so just return. */
2627 if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool))
2632 /* What we get from APR is in native encoding. */
2633 apr_file_printf(errfile, "%s: %s",
2634 desc, apr_strerror(status, errbuf,
2640 svn_io_start_cmd3(apr_proc_t *cmd_proc,
2643 const char *const *args,
2644 const char *const *env,
2645 svn_boolean_t inherit,
2646 svn_boolean_t infile_pipe,
2648 svn_boolean_t outfile_pipe,
2649 apr_file_t *outfile,
2650 svn_boolean_t errfile_pipe,
2651 apr_file_t *errfile,
2654 apr_status_t apr_err;
2655 apr_procattr_t *cmdproc_attr;
2657 const char **args_native;
2658 const char *cmd_apr;
2660 SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
2661 SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
2662 SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
2664 /* Create the process attributes. */
2665 apr_err = apr_procattr_create(&cmdproc_attr, pool);
2667 return svn_error_wrap_apr(apr_err,
2668 _("Can't create process '%s' attributes"),
2671 /* Make sure we invoke cmd directly, not through a shell. */
2672 apr_err = apr_procattr_cmdtype_set(cmdproc_attr,
2673 inherit ? APR_PROGRAM_PATH : APR_PROGRAM);
2675 return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
2678 /* Set the process's working directory. */
2681 const char *path_apr;
2683 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2684 apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
2686 return svn_error_wrap_apr(apr_err,
2687 _("Can't set process '%s' directory"),
2691 /* Use requested inputs and outputs.
2693 ### Unfortunately each of these apr functions creates a pipe and then
2694 overwrites the pipe file descriptor with the descriptor we pass
2695 in. The pipes can then never be closed. This is an APR bug. */
2698 apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
2700 return svn_error_wrap_apr(apr_err,
2701 _("Can't set process '%s' child input"),
2706 apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
2708 return svn_error_wrap_apr(apr_err,
2709 _("Can't set process '%s' child outfile"),
2714 apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
2716 return svn_error_wrap_apr(apr_err,
2717 _("Can't set process '%s' child errfile"),
2721 /* Forward request for pipes to APR. */
2722 if (infile_pipe || outfile_pipe || errfile_pipe)
2724 apr_err = apr_procattr_io_set(cmdproc_attr,
2725 infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
2726 outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
2727 errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE);
2730 return svn_error_wrap_apr(apr_err,
2731 _("Can't set process '%s' stdio pipes"),
2735 /* Have the child print any problems executing its program to errfile. */
2736 apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
2738 return svn_error_wrap_apr(apr_err,
2739 _("Can't set process '%s' child errfile for "
2742 apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
2743 handle_child_process_error);
2745 return svn_error_wrap_apr(apr_err,
2746 _("Can't set process '%s' error handler"),
2749 /* Convert cmd and args from UTF-8 */
2750 SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool));
2751 for (num_args = 0; args[num_args]; num_args++)
2753 args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
2754 args_native[num_args] = NULL;
2757 /* ### Well, it turns out that on APR on Windows expects all
2758 program args to be in UTF-8. Callers of svn_io_run_cmd
2759 should be aware of that. */
2760 SVN_ERR(cstring_from_utf8(&args_native[num_args],
2761 args[num_args], pool));
2765 /* Start the cmd command. */
2766 apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
2767 inherit ? NULL : env, cmdproc_attr, pool);
2769 return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
2771 return SVN_NO_ERROR;
2777 svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
2780 apr_exit_why_e *exitwhy,
2783 apr_status_t apr_err;
2784 apr_exit_why_e exitwhy_val;
2787 /* The Win32 apr_proc_wait doesn't set this... */
2788 exitwhy_val = APR_PROC_EXIT;
2790 /* Wait for the cmd command to finish. */
2791 apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT);
2792 if (!APR_STATUS_IS_CHILD_DONE(apr_err))
2793 return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"),
2797 *exitwhy = exitwhy_val;
2798 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)
2799 && APR_PROC_CHECK_CORE_DUMP(exitwhy_val))
2800 return svn_error_createf
2801 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2802 _("Process '%s' failed (signal %d, core dumped)"),
2804 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val))
2805 return svn_error_createf
2806 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2807 _("Process '%s' failed (signal %d)"),
2809 else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
2810 /* Don't really know what happened here. */
2811 return svn_error_createf
2812 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2813 _("Process '%s' failed (exitwhy %d, exitcode %d)"),
2814 cmd, exitwhy_val, exitcode_val);
2817 *exitcode = exitcode_val;
2818 else if (exitcode_val != 0)
2819 return svn_error_createf
2820 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2821 _("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
2823 return SVN_NO_ERROR;
2828 svn_io_run_cmd(const char *path,
2830 const char *const *args,
2832 apr_exit_why_e *exitwhy,
2833 svn_boolean_t inherit,
2835 apr_file_t *outfile,
2836 apr_file_t *errfile,
2839 apr_proc_t cmd_proc;
2841 SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
2842 FALSE, infile, FALSE, outfile, FALSE, errfile,
2845 return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
2850 svn_io_run_diff2(const char *dir,
2851 const char *const *user_args,
2858 apr_file_t *outfile,
2859 apr_file_t *errfile,
2860 const char *diff_cmd,
2866 int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
2867 apr_pool_t *subpool = svn_pool_create(pool);
2869 if (pexitcode == NULL)
2870 pexitcode = &exitcode;
2872 if (user_args != NULL)
2873 nargs += num_user_args;
2875 nargs += 1; /* -u */
2878 nargs += 2; /* the -L and the label itself */
2880 nargs += 2; /* the -L and the label itself */
2882 args = apr_palloc(subpool, nargs * sizeof(char *));
2885 args[i++] = diff_cmd;
2887 if (user_args != NULL)
2890 for (j = 0; j < num_user_args; ++j)
2891 args[i++] = user_args[j];
2894 args[i++] = "-u"; /* assume -u if the user didn't give us any args */
2907 args[i++] = svn_dirent_local_style(from, subpool);
2908 args[i++] = svn_dirent_local_style(to, subpool);
2911 SVN_ERR_ASSERT(i == nargs);
2913 SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
2914 NULL, outfile, errfile, subpool));
2916 /* The man page for (GNU) diff describes the return value as:
2918 "An exit status of 0 means no differences were found, 1 means
2919 some differences were found, and 2 means trouble."
2921 A return value of 2 typically occurs when diff cannot read its input
2922 or write to its output, but in any case we probably ought to return an
2923 error for anything other than 0 or 1 as the output is likely to be
2926 if (*pexitcode != 0 && *pexitcode != 1)
2927 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
2928 _("'%s' returned %d"),
2929 svn_dirent_local_style(diff_cmd, pool),
2932 svn_pool_destroy(subpool);
2934 return SVN_NO_ERROR;
2939 svn_io_run_diff3_3(int *exitcode,
2944 const char *mine_label,
2945 const char *older_label,
2946 const char *yours_label,
2948 const char *diff3_cmd,
2949 const apr_array_header_t *user_args,
2952 const char **args = apr_palloc(pool,
2962 /* Labels fall back to sensible defaults if not specified. */
2963 if (mine_label == NULL)
2964 mine_label = ".working";
2965 if (older_label == NULL)
2966 older_label = ".old";
2967 if (yours_label == NULL)
2968 yours_label = ".new";
2970 /* Set up diff3 command line. */
2971 args[i++] = diff3_cmd;
2975 for (j = 0; j < user_args->nelts; ++j)
2976 args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
2978 nargs += user_args->nelts;
2983 args[i++] = "-E"; /* We tried "-A" here, but that caused
2984 overlapping identical changes to
2985 conflict. See issue #682. */
2992 args[i++] = mine_label;
2994 args[i++] = older_label; /* note: this label is ignored if
2995 using 2-part markers, which is the
2998 args[i++] = yours_label;
2999 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
3001 svn_boolean_t has_arg;
3003 /* ### FIXME: we really shouldn't be reading the config here;
3004 instead, the necessary bits should be passed in by the caller.
3005 But should we add another parameter to this function, when the
3006 whole external diff3 thing might eventually go away? */
3010 SVN_ERR(svn_config_get_config(&config, pool));
3011 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
3012 SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS,
3013 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG,
3017 const char *diff_cmd, *diff_utf8;
3018 svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
3019 SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF);
3020 SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool));
3021 args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8, NULL);
3028 args[i++] = svn_dirent_local_style(mine, pool);
3029 args[i++] = svn_dirent_local_style(older, pool);
3030 args[i++] = svn_dirent_local_style(yours, pool);
3033 SVN_ERR_ASSERT(i == nargs);
3036 /* Run diff3, output the merged text into the scratch file. */
3037 SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
3039 TRUE, /* keep environment */
3043 /* According to the diff3 docs, a '0' means the merge was clean, and
3044 '1' means conflict markers were found. Anything else is real
3046 if ((*exitcode != 0) && (*exitcode != 1))
3047 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3048 _("Error running '%s': exitcode was %d, "
3050 "\nin directory '%s', basenames:\n%s\n%s\n%s"),
3051 svn_dirent_local_style(diff3_cmd, pool),
3053 svn_dirent_local_style(dir, pool),
3054 /* Don't call svn_path_local_style() on
3055 the basenames. We don't want them to
3056 be absolute, and we don't need the
3057 separator conversion. */
3058 mine, older, yours);
3060 return SVN_NO_ERROR;
3064 /* Canonicalize a string for hashing. Modifies KEY in place. */
3065 static APR_INLINE char *
3066 fileext_tolower(char *key)
3069 for (p = key; *p != 0; ++p)
3070 *p = (char)apr_tolower(*p);
3076 svn_io_parse_mimetypes_file(apr_hash_t **type_map,
3077 const char *mimetypes_file,
3080 svn_error_t *err = SVN_NO_ERROR;
3081 apr_hash_t *types = apr_hash_make(pool);
3082 svn_boolean_t eof = FALSE;
3083 svn_stringbuf_t *buf;
3084 apr_pool_t *subpool = svn_pool_create(pool);
3085 apr_file_t *types_file;
3086 svn_stream_t *mimetypes_stream;
3088 SVN_ERR(svn_io_file_open(&types_file, mimetypes_file,
3089 APR_READ, APR_OS_DEFAULT, pool));
3090 mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool);
3094 apr_array_header_t *tokens;
3097 svn_pool_clear(subpool);
3100 if ((err = svn_stream_readline(mimetypes_stream, &buf,
3101 APR_EOL_STR, &eof, subpool)))
3104 /* Only pay attention to non-empty, non-comment lines. */
3109 if (buf->data[0] == '#')
3112 /* Tokenize (into our return pool). */
3113 tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
3114 if (tokens->nelts < 2)
3117 /* The first token in a multi-token line is the media type.
3118 Subsequent tokens are filename extensions associated with
3120 type = APR_ARRAY_IDX(tokens, 0, const char *);
3121 for (i = 1; i < tokens->nelts; i++)
3123 /* We can safely address 'ext' as a non-const string because
3124 * we know svn_cstring_split() allocated it in 'pool' for us. */
3125 char *ext = APR_ARRAY_IDX(tokens, i, char *);
3126 fileext_tolower(ext);
3127 svn_hash_sets(types, ext, type);
3133 svn_pool_destroy(subpool);
3135 /* If there was an error above, close the file (ignoring any error
3136 from *that*) and return the originally error. */
3139 svn_error_clear(svn_stream_close(mimetypes_stream));
3143 /* Close the stream (which closes the underlying file, too). */
3144 SVN_ERR(svn_stream_close(mimetypes_stream));
3147 return SVN_NO_ERROR;
3152 svn_io_detect_mimetype2(const char **mimetype,
3154 apr_hash_t *mimetype_map,
3157 static const char * const generic_binary = "application/octet-stream";
3159 svn_node_kind_t kind;
3162 unsigned char block[1024];
3163 apr_size_t amt_read = sizeof(block);
3165 /* Default return value is NULL. */
3168 /* If there is a mimetype_map provided, we'll first try to look up
3169 our file's extension in the map. Failing that, we'll run the
3173 const char *type_from_map;
3174 char *path_ext; /* Can point to physical const memory but only when
3175 svn_path_splitext sets it to "". */
3176 svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
3177 fileext_tolower(path_ext);
3178 if ((type_from_map = svn_hash_gets(mimetype_map, path_ext)))
3180 *mimetype = type_from_map;
3181 return SVN_NO_ERROR;
3185 /* See if this file even exists, and make sure it really is a file. */
3186 SVN_ERR(svn_io_check_path(file, &kind, pool));
3187 if (kind != svn_node_file)
3188 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
3189 _("Can't detect MIME type of non-file '%s'"),
3190 svn_dirent_local_style(file, pool));
3192 SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
3194 /* Read a block of data from FILE. */
3195 err = svn_io_file_read(fh, block, &amt_read, pool);
3196 if (err && ! APR_STATUS_IS_EOF(err->apr_err))
3198 svn_error_clear(err);
3200 /* Now close the file. No use keeping it open any more. */
3201 SVN_ERR(svn_io_file_close(fh, pool));
3203 if (svn_io_is_binary_data(block, amt_read))
3204 *mimetype = generic_binary;
3206 return SVN_NO_ERROR;
3211 svn_io_is_binary_data(const void *data, apr_size_t len)
3213 const unsigned char *buf = data;
3215 if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
3217 /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
3218 * Treat it as plain text. */
3222 /* Right now, this function is going to be really stupid. It's
3223 going to examine the block of data, and make sure that 15%
3224 of the bytes are such that their value is in the ranges 0x07-0x0D
3225 or 0x20-0x7F, and that none of those bytes is 0x00. If those
3226 criteria are not met, we're calling it binary.
3228 NOTE: Originally, I intended to target 85% of the bytes being in
3229 the specified ranges, but I flubbed the condition. At any rate,
3230 folks aren't complaining, so I'm not sure that it's worth
3231 adjusting this retroactively now. --cmpilato */
3235 apr_size_t binary_count = 0;
3237 /* Run through the data we've read, counting the 'binary-ish'
3238 bytes. HINT: If we see a 0x00 byte, we'll set our count to its
3239 max and stop reading the file. */
3240 for (i = 0; i < len; i++)
3248 || ((buf[i] > 0x0D) && (buf[i] < 0x20))
3255 return (((binary_count * 1000) / len) > 850);
3263 svn_io_detect_mimetype(const char **mimetype,
3267 return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
3272 svn_io_file_open(apr_file_t **new_file, const char *fname,
3273 apr_int32_t flag, apr_fileperms_t perm,
3276 const char *fname_apr;
3277 apr_status_t status;
3279 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3280 status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
3284 return svn_error_wrap_apr(status, _("Can't open file '%s'"),
3285 svn_dirent_local_style(fname, pool));
3287 return SVN_NO_ERROR;
3291 static APR_INLINE svn_error_t *
3292 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
3293 const char *msg, const char *msg_no_name,
3300 return SVN_NO_ERROR;
3302 err = svn_io_file_name_get(&name, file, pool);
3305 svn_error_clear(err);
3307 /* ### Issue #3014: Return a specific error for broken pipes,
3308 * ### with a single element in the error chain. */
3309 if (APR_STATUS_IS_EPIPE(status))
3310 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
3313 return svn_error_wrap_apr(status, _(msg),
3314 try_utf8_from_internal_style(name, pool));
3316 return svn_error_wrap_apr(status, "%s", _(msg_no_name));
3321 svn_io_file_close(apr_file_t *file, apr_pool_t *pool)
3323 return do_io_file_wrapper_cleanup(file, apr_file_close(file),
3324 N_("Can't close file '%s'"),
3325 N_("Can't close stream"),
3331 svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
3333 return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file),
3334 N_("Can't read file '%s'"),
3335 N_("Can't read stream"),
3341 svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
3343 return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file),
3344 N_("Can't write file '%s'"),
3345 N_("Can't write stream"),
3351 svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
3352 apr_file_t *file, apr_pool_t *pool)
3354 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3355 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3357 return do_io_file_wrapper_cleanup(
3358 file, apr_file_info_get(finfo, wanted, file),
3359 N_("Can't get attribute information from file '%s'"),
3360 N_("Can't get attribute information from stream"),
3366 svn_io_file_read(apr_file_t *file, void *buf,
3367 apr_size_t *nbytes, apr_pool_t *pool)
3369 return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes),
3370 N_("Can't read file '%s'"),
3371 N_("Can't read stream"),
3377 svn_io_file_read_full2(apr_file_t *file, void *buf,
3378 apr_size_t nbytes, apr_size_t *bytes_read,
3379 svn_boolean_t *hit_eof,
3382 apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
3385 if (APR_STATUS_IS_EOF(status))
3388 return SVN_NO_ERROR;
3394 return do_io_file_wrapper_cleanup(file, status,
3395 N_("Can't read file '%s'"),
3396 N_("Can't read stream"),
3402 svn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
3403 apr_off_t *offset, apr_pool_t *pool)
3405 return do_io_file_wrapper_cleanup(
3406 file, apr_file_seek(file, where, offset),
3407 N_("Can't set position pointer in file '%s'"),
3408 N_("Can't set position pointer in stream"),
3414 svn_io_file_write(apr_file_t *file, const void *buf,
3415 apr_size_t *nbytes, apr_pool_t *pool)
3417 return svn_error_trace(do_io_file_wrapper_cleanup(
3418 file, apr_file_write(file, buf, nbytes),
3419 N_("Can't write to file '%s'"),
3420 N_("Can't write to stream"),
3426 svn_io_file_write_full(apr_file_t *file, const void *buf,
3427 apr_size_t nbytes, apr_size_t *bytes_written,
3430 /* We cannot simply call apr_file_write_full on Win32 as it may fail
3431 for larger values of NBYTES. In that case, we have to emulate the
3432 "_full" part here. Thus, always call apr_file_write directly on
3433 Win32 as this minimizes overhead for small data buffers. */
3435 #define MAXBUFSIZE 30*1024
3436 apr_size_t bw = nbytes;
3437 apr_size_t to_write = nbytes;
3439 /* try a simple "write everything at once" first */
3440 apr_status_t rv = apr_file_write(file, buf, &bw);
3441 buf = (char *)buf + bw;
3444 /* if the OS cannot handle that, use smaller chunks */
3445 if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
3446 && nbytes > MAXBUFSIZE)
3449 bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
3450 rv = apr_file_write(file, buf, &bw);
3451 buf = (char *)buf + bw;
3453 } while (rv == APR_SUCCESS && to_write > 0);
3456 /* bytes_written may actually be NULL */
3458 *bytes_written = nbytes - to_write;
3461 apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
3464 return svn_error_trace(do_io_file_wrapper_cleanup(
3466 N_("Can't write to file '%s'"),
3467 N_("Can't write to stream"),
3473 svn_io_write_unique(const char **tmp_path,
3474 const char *dirpath,
3477 svn_io_file_del_t delete_when,
3480 apr_file_t *new_file;
3483 SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
3484 delete_when, pool, pool));
3486 err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
3489 err = svn_io_file_flush_to_disk(new_file, pool);
3491 return svn_error_trace(
3492 svn_error_compose_create(err,
3493 svn_io_file_close(new_file, pool)));
3498 svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
3500 /* This is a work-around. APR would flush the write buffer
3501 _after_ truncating the file causing now invalid buffered
3502 data to be written behind OFFSET. */
3503 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
3504 N_("Can't flush file '%s'"),
3505 N_("Can't flush stream"),
3508 return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset),
3509 N_("Can't truncate file '%s'"),
3510 N_("Can't truncate stream"),
3516 svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
3520 apr_size_t total_read = 0;
3521 svn_boolean_t eof = FALSE;
3524 apr_size_t buf_size = *limit;
3526 while (buf_size > 0)
3528 /* read a fair chunk of data at once. But don't get too ambitious
3529 * as that would result in too much waste. Also make sure we can
3530 * put a NUL after the last byte read.
3532 apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
3533 apr_size_t bytes_read = 0;
3539 /* read data block (or just a part of it) */
3540 SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
3541 &bytes_read, &eof, pool));
3543 /* look or a newline char */
3544 buf[bytes_read] = 0;
3545 eol = strchr(buf, '\n');
3548 apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
3551 *limit = total_read + (eol - buf);
3553 /* correct the file pointer:
3554 * appear as though we just had read the newline char
3556 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
3558 return SVN_NO_ERROR;
3562 /* no EOL found but we hit the end of the file.
3563 * Generate a nice EOF error object and return it.
3566 SVN_ERR(svn_io_file_getc(&dummy, file, pool));
3569 /* next data chunk */
3570 buf_size -= bytes_read;
3572 total_read += bytes_read;
3575 /* buffer limit has been exceeded without finding the EOL */
3576 err = svn_io_file_name_get(&name, file, pool);
3579 svn_error_clear(err);
3582 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
3583 _("Can't read length line in file '%s'"),
3584 svn_dirent_local_style(name, pool));
3586 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
3587 _("Can't read length line in stream"));
3592 svn_io_stat(apr_finfo_t *finfo, const char *fname,
3593 apr_int32_t wanted, apr_pool_t *pool)
3595 apr_status_t status;
3596 const char *fname_apr;
3598 /* APR doesn't like "" directories */
3599 if (fname[0] == '\0')
3602 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3604 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3605 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3607 status = apr_stat(finfo, fname_apr, wanted, pool);
3609 return svn_error_wrap_apr(status, _("Can't stat '%s'"),
3610 svn_dirent_local_style(fname, pool));
3612 return SVN_NO_ERROR;
3617 svn_io_file_rename(const char *from_path, const char *to_path,
3620 apr_status_t status = APR_SUCCESS;
3621 const char *from_path_apr, *to_path_apr;
3623 SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
3624 SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
3626 status = apr_file_rename(from_path_apr, to_path_apr, pool);
3628 #if defined(WIN32) || defined(__OS2__)
3629 /* If the target file is read only NTFS reports EACCESS and
3630 FAT/FAT32 reports EEXIST */
3631 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
3633 /* Set the destination file writable because Windows will not
3634 allow us to rename when to_path is read-only, but will
3635 allow renaming when from_path is read only. */
3636 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
3638 status = apr_file_rename(from_path_apr, to_path_apr, pool);
3640 WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool));
3641 #endif /* WIN32 || __OS2__ */
3644 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
3645 svn_dirent_local_style(from_path, pool),
3646 svn_dirent_local_style(to_path, pool));
3648 return SVN_NO_ERROR;
3653 svn_io_file_move(const char *from_path, const char *to_path,
3656 svn_error_t *err = svn_io_file_rename(from_path, to_path, pool);
3658 if (err && APR_STATUS_IS_EXDEV(err->apr_err))
3660 const char *tmp_to_path;
3662 svn_error_clear(err);
3664 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_to_path,
3665 svn_dirent_dirname(to_path, pool),
3666 svn_io_file_del_none,
3669 err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool);
3673 err = svn_io_file_rename(tmp_to_path, to_path, pool);
3677 err = svn_io_remove_file2(from_path, FALSE, pool);
3679 return SVN_NO_ERROR;
3681 svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool));
3686 svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool));
3692 /* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
3693 HIDDEN determines if the hidden attribute
3694 should be set on the newly created directory. */
3695 static svn_error_t *
3696 dir_make(const char *path, apr_fileperms_t perm,
3697 svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
3699 apr_status_t status;
3700 const char *path_apr;
3702 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
3704 /* APR doesn't like "" directories */
3705 if (path_apr[0] == '\0')
3708 #if (APR_OS_DEFAULT & APR_WSTICKY)
3709 /* The APR shipped with httpd 2.0.50 contains a bug where
3710 APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
3711 There is a special case for file creation, but not directory
3712 creation, so directories wind up getting created with the sticky
3713 bit set. (There is no such thing as a setuid directory, and the
3714 setgid bit is apparently ignored at mkdir() time.) If we detect
3715 this problem, work around it by unsetting those bits if we are
3716 passed APR_OS_DEFAULT. */
3717 if (perm == APR_OS_DEFAULT)
3718 perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY);
3721 status = apr_dir_make(path_apr, perm, pool);
3722 WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
3725 return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
3726 svn_dirent_local_style(path, pool));
3728 #ifdef APR_FILE_ATTR_HIDDEN
3732 status = apr_file_attrs_set(path_apr,
3733 APR_FILE_ATTR_HIDDEN,
3734 APR_FILE_ATTR_HIDDEN,
3737 /* on Windows, use our wrapper so we can also set the
3738 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */
3739 status = io_win_file_attrs_set(path_apr,
3740 FILE_ATTRIBUTE_HIDDEN |
3741 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
3742 FILE_ATTRIBUTE_HIDDEN |
3743 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
3748 return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
3749 svn_dirent_local_style(path, pool));
3753 /* Windows does not implement sgid. Skip here because retrieving
3754 the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented
3755 to be 'incredibly expensive'. */
3761 /* Per our contract, don't do error-checking. Some filesystems
3762 * don't support the sgid bit, and that's okay. */
3763 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool);
3766 apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
3770 return SVN_NO_ERROR;
3774 svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
3776 return dir_make(path, perm, FALSE, FALSE, pool);
3780 svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
3783 return dir_make(path, perm, TRUE, FALSE, pool);
3787 svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
3790 return dir_make(path, perm, FALSE, TRUE, pool);
3795 svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
3797 apr_status_t status;
3798 const char *dirname_apr;
3800 /* APR doesn't like "" directories */
3801 if (dirname[0] == '\0')
3804 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3806 status = apr_dir_open(new_dir, dirname_apr, pool);
3808 return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
3809 svn_dirent_local_style(dirname, pool));
3811 return SVN_NO_ERROR;
3815 svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
3817 apr_status_t status;
3818 const char *dirname_apr;
3820 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3822 status = apr_dir_remove(dirname_apr, pool);
3826 svn_boolean_t retry = TRUE;
3828 if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY)
3830 apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
3832 if (APR_STATUS_IS_ENOTEMPTY(empty_status))
3838 WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
3843 return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
3844 svn_dirent_local_style(dirname, pool));
3846 return SVN_NO_ERROR;
3851 svn_io_dir_read(apr_finfo_t *finfo,
3856 apr_status_t status;
3858 status = apr_dir_read(finfo, wanted, thedir);
3861 return svn_error_wrap_apr(status, _("Can't read directory"));
3863 /* It would be nice to use entry_name_to_utf8() below, but can we
3864 get the dir's path out of an apr_dir_t? I don't see a reliable
3868 SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
3871 SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
3873 return SVN_NO_ERROR;
3877 svn_io_dir_close(apr_dir_t *thedir)
3879 apr_status_t apr_err = apr_dir_close(thedir);
3881 return svn_error_wrap_apr(apr_err, _("Error closing directory"));
3883 return SVN_NO_ERROR;
3887 svn_io_dir_walk2(const char *dirname,
3889 svn_io_walk_func_t walk_func,
3893 apr_status_t apr_err;
3895 apr_pool_t *subpool;
3896 const char *dirname_apr;
3899 wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
3901 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3902 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3904 /* The documentation for apr_dir_read used to state that "." and ".."
3905 will be returned as the first two files, but it doesn't
3906 work that way in practice, in particular ext3 on Linux-2.6 doesn't
3907 follow the rules. For details see
3908 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
3910 If APR ever does implement "dot-first" then it would be possible to
3911 remove the svn_io_stat and walk_func calls and use the walk_func
3914 Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
3915 documented to provide it, so we have to do a bit extra. */
3916 SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool));
3917 SVN_ERR(cstring_from_utf8(&finfo.name,
3918 svn_dirent_basename(dirname, pool),
3920 finfo.valid |= APR_FINFO_NAME;
3921 SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
3923 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3925 /* APR doesn't like "" directories */
3926 if (dirname_apr[0] == '\0')
3929 apr_err = apr_dir_open(&handle, dirname_apr, pool);
3931 return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
3932 svn_dirent_local_style(dirname, pool));
3934 /* iteration subpool */
3935 subpool = svn_pool_create(pool);
3939 const char *name_utf8;
3940 const char *full_path;
3942 svn_pool_clear(subpool);
3944 apr_err = apr_dir_read(&finfo, wanted, handle);
3945 if (APR_STATUS_IS_ENOENT(apr_err))
3949 return svn_error_wrap_apr(apr_err,
3950 _("Can't read directory entry in '%s'"),
3951 svn_dirent_local_style(dirname, pool));
3954 if (finfo.filetype == APR_DIR)
3956 if (finfo.name[0] == '.'
3957 && (finfo.name[1] == '\0'
3958 || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
3959 /* skip "." and ".." */
3962 /* some other directory. recurse. it will be passed to the
3963 callback inside the recursion. */
3964 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
3966 full_path = svn_dirent_join(dirname, name_utf8, subpool);
3967 SVN_ERR(svn_io_dir_walk2(full_path,
3973 else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
3975 /* some other directory. pass it to the callback. */
3976 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
3978 full_path = svn_dirent_join(dirname, name_utf8, subpool);
3979 SVN_ERR((*walk_func)(walk_baton,
3985 Some other type of file; skip it for now. We've reserved the
3986 right to expand our coverage here in the future, though,
3987 without revving this API.
3991 svn_pool_destroy(subpool);
3993 apr_err = apr_dir_close(handle);
3995 return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
3996 svn_dirent_local_style(dirname, pool));
3998 return SVN_NO_ERROR;
4004 * Determine if a directory is empty or not.
4005 * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
4006 * @param path The directory.
4007 * @param pool Used for temporary allocation.
4008 * @remark If path is not a directory, or some other error occurs,
4009 * then return the appropriate apr status code.
4011 * (This function is written in APR style, in anticipation of
4012 * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
4015 dir_is_empty(const char *dir, apr_pool_t *pool)
4017 apr_status_t apr_err;
4018 apr_dir_t *dir_handle;
4020 apr_status_t retval = APR_SUCCESS;
4022 /* APR doesn't like "" directories */
4026 apr_err = apr_dir_open(&dir_handle, dir, pool);
4027 if (apr_err != APR_SUCCESS)
4030 for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle);
4031 apr_err == APR_SUCCESS;
4032 apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle))
4034 /* Ignore entries for this dir and its parent, robustly.
4035 (APR promises that they'll come first, so technically
4036 this guard could be moved outside the loop. But Ryan Bloom
4037 says he doesn't believe it, and I believe him. */
4038 if (! (finfo.name[0] == '.'
4039 && (finfo.name[1] == '\0'
4040 || (finfo.name[1] == '.' && finfo.name[2] == '\0'))))
4042 retval = APR_ENOTEMPTY;
4047 /* Make sure we broke out of the loop for the right reason. */
4048 if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
4051 apr_err = apr_dir_close(dir_handle);
4052 if (apr_err != APR_SUCCESS)
4060 svn_io_dir_empty(svn_boolean_t *is_empty_p,
4064 apr_status_t status;
4065 const char *path_apr;
4067 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4069 status = dir_is_empty(path_apr, pool);
4073 else if (APR_STATUS_IS_ENOTEMPTY(status))
4074 *is_empty_p = FALSE;
4076 return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
4077 svn_dirent_local_style(path, pool));
4079 return SVN_NO_ERROR;
4084 /*** Version/format files ***/
4087 svn_io_write_version_file(const char *path,
4091 const char *path_tmp;
4092 const char *format_contents = apr_psprintf(pool, "%d\n", version);
4094 SVN_ERR_ASSERT(version >= 0);
4096 SVN_ERR(svn_io_write_unique(&path_tmp,
4097 svn_dirent_dirname(path, pool),
4098 format_contents, strlen(format_contents),
4099 svn_io_file_del_none, pool));
4101 #if defined(WIN32) || defined(__OS2__)
4102 /* make the destination writable, but only on Windows, because
4103 Windows does not let us replace read-only files. */
4104 SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
4105 #endif /* WIN32 || __OS2__ */
4107 /* rename the temp file as the real destination */
4108 SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
4110 /* And finally remove the perms to make it read only */
4111 return svn_io_set_file_read_only(path, FALSE, pool);
4116 svn_io_read_version_file(int *version,
4120 apr_file_t *format_file;
4125 /* Read a chunk of data from PATH */
4126 SVN_ERR(svn_io_file_open(&format_file, path, APR_READ,
4127 APR_OS_DEFAULT, pool));
4129 err = svn_io_file_read(format_file, buf, &len, pool);
4131 /* Close the file. */
4132 SVN_ERR(svn_error_compose_create(err,
4133 svn_io_file_close(format_file, pool)));
4135 /* If there was no data in PATH, return an error. */
4137 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
4139 svn_dirent_local_style(path, pool));
4141 /* Check that the first line contains only digits. */
4145 for (i = 0; i < len; ++i)
4149 if (i > 0 && (c == '\r' || c == '\n'))
4154 if (! svn_ctype_isdigit(c))
4155 return svn_error_createf
4156 (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
4157 _("First line of '%s' contains non-digit"),
4158 svn_dirent_local_style(path, pool));
4162 /* Convert to integer. */
4163 SVN_ERR(svn_cstring_atoi(version, buf));
4165 return SVN_NO_ERROR;
4170 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
4171 static svn_error_t *
4172 contents_identical_p(svn_boolean_t *identical_p,
4178 apr_size_t bytes_read1, bytes_read2;
4179 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4180 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4181 apr_file_t *file1_h;
4182 apr_file_t *file2_h;
4183 svn_boolean_t eof1 = FALSE;
4184 svn_boolean_t eof2 = FALSE;
4186 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4189 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4193 return svn_error_trace(
4194 svn_error_compose_create(err,
4195 svn_io_file_close(file1_h, pool)));
4197 *identical_p = TRUE; /* assume TRUE, until disproved below */
4198 while (!err && !eof1 && !eof2)
4200 err = svn_io_file_read_full2(file1_h, buf1,
4201 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4206 err = svn_io_file_read_full2(file2_h, buf2,
4207 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4212 if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
4214 *identical_p = FALSE;
4219 /* Special case: one file being a prefix of the other and the shorter
4220 * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */
4221 if (!err && (eof1 != eof2))
4222 *identical_p = FALSE;
4224 return svn_error_trace(
4225 svn_error_compose_create(
4227 svn_error_compose_create(svn_io_file_close(file1_h, pool),
4228 svn_io_file_close(file2_h, pool))));
4233 /* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
4234 static svn_error_t *
4235 contents_three_identical_p(svn_boolean_t *identical_p12,
4236 svn_boolean_t *identical_p23,
4237 svn_boolean_t *identical_p13,
4241 apr_pool_t *scratch_pool)
4244 apr_size_t bytes_read1, bytes_read2, bytes_read3;
4245 char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4246 char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4247 char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4248 apr_file_t *file1_h;
4249 apr_file_t *file2_h;
4250 apr_file_t *file3_h;
4251 svn_boolean_t eof1 = FALSE;
4252 svn_boolean_t eof2 = FALSE;
4253 svn_boolean_t eof3 = FALSE;
4254 svn_boolean_t read_1, read_2, read_3;
4256 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4259 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4263 return svn_error_trace(
4264 svn_error_compose_create(err,
4265 svn_io_file_close(file1_h, scratch_pool)));
4267 err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
4271 return svn_error_trace(
4272 svn_error_compose_create(
4274 svn_error_compose_create(svn_io_file_close(file1_h,
4276 svn_io_file_close(file2_h,
4279 /* assume TRUE, until disproved below */
4280 *identical_p12 = *identical_p23 = *identical_p13 = TRUE;
4281 /* We need to read as long as no error occurs, and as long as one of the
4282 * flags could still change due to a read operation */
4284 && ((*identical_p12 && !eof1 && !eof2)
4285 || (*identical_p23 && !eof2 && !eof3)
4286 || (*identical_p13 && !eof1 && !eof3)))
4288 read_1 = read_2 = read_3 = FALSE;
4290 /* As long as a file is not at the end yet, and it is still
4291 * potentially identical to another file, we read the next chunk.*/
4292 if (!eof1 && (identical_p12 || identical_p13))
4294 err = svn_io_file_read_full2(file1_h, buf1,
4295 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4296 &eof1, scratch_pool);
4302 if (!eof2 && (identical_p12 || identical_p23))
4304 err = svn_io_file_read_full2(file2_h, buf2,
4305 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4306 &eof2, scratch_pool);
4312 if (!eof3 && (identical_p13 || identical_p23))
4314 err = svn_io_file_read_full2(file3_h, buf3,
4315 SVN__STREAM_CHUNK_SIZE, &bytes_read3,
4316 &eof3, scratch_pool);
4322 /* If the files are still marked identical, and at least one of them
4323 * is not at the end of file, we check whether they differ, and set
4324 * their flag to false then. */
4326 && (read_1 || read_2)
4328 || (bytes_read1 != bytes_read2)
4329 || memcmp(buf1, buf2, bytes_read1)))
4331 *identical_p12 = FALSE;
4335 && (read_2 || read_3)
4337 || (bytes_read2 != bytes_read3)
4338 || memcmp(buf2, buf3, bytes_read2)))
4340 *identical_p23 = FALSE;
4344 && (read_1 || read_3)
4346 || (bytes_read1 != bytes_read3)
4347 || memcmp(buf1, buf3, bytes_read3)))
4349 *identical_p13 = FALSE;
4353 return svn_error_trace(
4354 svn_error_compose_create(
4356 svn_error_compose_create(
4357 svn_io_file_close(file1_h, scratch_pool),
4358 svn_error_compose_create(
4359 svn_io_file_close(file2_h, scratch_pool),
4360 svn_io_file_close(file3_h, scratch_pool)))));
4366 svn_io_files_contents_same_p(svn_boolean_t *same,
4373 SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
4378 return SVN_NO_ERROR;
4381 SVN_ERR(contents_identical_p(&q, file1, file2, pool));
4388 return SVN_NO_ERROR;
4392 svn_io_files_contents_three_same_p(svn_boolean_t *same12,
4393 svn_boolean_t *same23,
4394 svn_boolean_t *same13,
4398 apr_pool_t *scratch_pool)
4400 svn_boolean_t diff_size12, diff_size23, diff_size13;
4402 SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
4410 if (diff_size12 && diff_size23 && diff_size13)
4412 *same12 = *same23 = *same13 = FALSE;
4414 else if (diff_size12 && diff_size23)
4416 *same12 = *same23 = FALSE;
4417 SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
4419 else if (diff_size23 && diff_size13)
4421 *same23 = *same13 = FALSE;
4422 SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
4424 else if (diff_size12 && diff_size13)
4426 *same12 = *same13 = FALSE;
4427 SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
4431 SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
4432 SVN_ERR(contents_three_identical_p(same12, same23, same13,
4433 file1, file2, file3,
4437 return SVN_NO_ERROR;
4441 /* Counter value of file_mktemp request (used in a threadsafe way), to make
4442 sure that a single process normally never generates the same tempname
4444 static volatile apr_uint32_t tempname_counter = 0;
4447 /* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
4448 Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name.
4449 Perform temporary allocations in SCRATCH_POOL and the result in
4451 static svn_error_t *
4452 temp_file_create(apr_file_t **new_file,
4453 const char **new_file_name,
4454 const char *directory,
4456 apr_pool_t *result_pool,
4457 apr_pool_t *scratch_pool)
4460 const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
4461 const char *templ_apr;
4462 apr_status_t status;
4464 SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
4466 /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
4467 data available in POOL and we need a non-const pointer here,
4468 as apr changes the template to return the new filename. */
4469 status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool);
4472 return svn_error_wrap_apr(status, _("Can't create temporary file from "
4473 "template '%s'"), templ);
4475 /* Translate the returned path back to utf-8 before returning it */
4476 return svn_error_trace(svn_path_cstring_to_utf8(new_file_name,
4480 /* The Windows implementation of apr_file_mktemp doesn't handle access
4481 denied errors correctly. Therefore we implement our own temp file
4482 creation function here. */
4484 /* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
4485 ### the function we used before. But we try to guess a more unique
4486 ### name before trying if it exists. */
4488 /* Offset by some time value and a unique request nr to make the number
4489 +- unique for both this process and on the computer */
4490 int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter)
4491 + GetCurrentProcessId();
4494 /* ### Maybe use an iterpool? */
4495 for (i = 0; i <= 99999; i++)
4497 apr_uint32_t unique_nr;
4498 const char *unique_name;
4499 const char *unique_name_apr;
4500 apr_file_t *try_file;
4501 apr_status_t apr_err;
4503 /* Generate a number that should be unique for this application and
4504 usually for the entire computer to reduce the number of cycles
4505 through this loop. (A bit of calculation is much cheaper then
4507 unique_nr = baseNr + 3 * i;
4509 unique_name = svn_dirent_join(directory,
4510 apr_psprintf(scratch_pool, "svn-%X",
4514 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
4516 apr_err = file_open(&try_file, unique_name_apr, flags,
4517 APR_OS_DEFAULT, FALSE, scratch_pool);
4519 if (APR_STATUS_IS_EEXIST(apr_err))
4523 /* On Win32, CreateFile fails with an "Access Denied" error
4524 code, rather than "File Already Exists", if the colliding
4525 name belongs to a directory. */
4527 if (APR_STATUS_IS_EACCES(apr_err))
4530 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
4531 APR_FINFO_TYPE, scratch_pool);
4533 if (!apr_err_2 && finfo.filetype == APR_DIR)
4536 apr_err_2 = APR_TO_OS_ERROR(apr_err);
4538 if (apr_err_2 == ERROR_ACCESS_DENIED ||
4539 apr_err_2 == ERROR_SHARING_VIOLATION)
4541 /* The file is in use by another process or is hidden;
4542 create a new name, but don't do this 99999 times in
4543 case the folder is not writable */
4548 /* Else fall through and return the original error. */
4551 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
4552 svn_dirent_local_style(unique_name,
4557 /* Move file to the right pool */
4558 apr_err = apr_file_setaside(new_file, try_file, result_pool);
4561 return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
4562 svn_dirent_local_style(unique_name,
4565 *new_file_name = apr_pstrdup(result_pool, unique_name);
4567 return SVN_NO_ERROR;
4571 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
4573 _("Unable to make name in '%s'"),
4574 svn_dirent_local_style(directory, scratch_pool));
4578 /* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
4580 svn_io_file_name_get(const char **filename,
4584 const char *fname_apr;
4585 apr_status_t status;
4587 status = apr_file_name_get(&fname_apr, file);
4589 return svn_error_wrap_apr(status, _("Can't get file name"));
4592 SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
4596 return SVN_NO_ERROR;
4601 svn_io_open_unique_file3(apr_file_t **file,
4602 const char **unique_path,
4603 const char *dirpath,
4604 svn_io_file_del_t delete_when,
4605 apr_pool_t *result_pool,
4606 apr_pool_t *scratch_pool)
4608 apr_file_t *tempfile;
4609 const char *tempname;
4610 struct temp_file_cleanup_s *baton = NULL;
4611 apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
4612 APR_BUFFERED | APR_BINARY);
4613 #if !defined(WIN32) && !defined(__OS2__)
4614 apr_fileperms_t perms;
4615 svn_boolean_t using_system_temp_dir = FALSE;
4618 SVN_ERR_ASSERT(file || unique_path);
4622 *unique_path = NULL;
4624 if (dirpath == NULL)
4626 #if !defined(WIN32) && !defined(__OS2__)
4627 using_system_temp_dir = TRUE;
4629 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
4632 switch (delete_when)
4634 case svn_io_file_del_on_pool_cleanup:
4635 baton = apr_palloc(result_pool, sizeof(*baton));
4636 baton->pool = result_pool;
4637 baton->fname_apr = NULL;
4639 /* Because cleanups are run LIFO, we need to make sure to register
4640 our cleanup before the apr_file_close cleanup:
4642 On Windows, you can't remove an open file.
4644 apr_pool_cleanup_register(result_pool, baton,
4645 temp_file_plain_cleanup_handler,
4646 temp_file_child_cleanup_handler);
4649 case svn_io_file_del_on_close:
4650 flags |= APR_DELONCLOSE;
4656 SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
4657 result_pool, scratch_pool));
4659 #if !defined(WIN32) && !defined(__OS2__)
4660 /* apr_file_mktemp() creates files with mode 0600.
4661 * This is appropriate if we're using a system temp dir since we don't
4662 * want to leak sensitive data into temp files other users can read.
4663 * If we're not using a system temp dir we're probably using the
4664 * .svn/tmp area and it's likely that the tempfile will end up being
4665 * copied or renamed into the working copy.
4666 * This would cause working files having mode 0600 while users might
4667 * expect to see 0644 or 0664. So we tweak perms of the tempfile in this
4668 * case, but only if the umask allows it. */
4669 if (!using_system_temp_dir)
4671 SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
4672 SVN_ERR(file_perms_set2(tempfile, perms, scratch_pool));
4679 SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
4682 *unique_path = tempname; /* Was allocated in result_pool */
4685 SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
4687 return SVN_NO_ERROR;
4691 svn_io_file_readline(apr_file_t *file,
4692 svn_stringbuf_t **stringbuf,
4696 apr_pool_t *result_pool,
4697 apr_pool_t *scratch_pool)
4699 svn_stringbuf_t *str;
4700 const char *eol_str;
4701 apr_size_t numbytes;
4704 svn_boolean_t found_eof;
4706 str = svn_stringbuf_create_ensure(80, result_pool);
4708 /* Read bytes into STR up to and including, but not storing,
4709 * the next EOL sequence. */
4717 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4718 &found_eof, scratch_pool));
4720 if (numbytes != 1 || len > max_len)
4734 if (!found_eof && len < max_len)
4738 /* Check for "\r\n" by peeking at the next byte. */
4740 SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool));
4741 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4742 &found_eof, scratch_pool));
4743 if (numbytes == 1 && c == '\n')
4750 /* Pretend we never peeked. */
4751 SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
4758 svn_stringbuf_appendbyte(str, c);
4770 return SVN_NO_ERROR;