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>
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"
69 #include "private/svn_utf_private.h"
70 #include "private/svn_dep_compat.h"
72 #define SVN_SLEEP_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS"
75 Windows is 'aided' by a number of types of applications that
76 follow other applications around and open up files they have
77 changed for various reasons (the most intrusive are virus
78 scanners). So, if one of these other apps has glommed onto
79 our file we may get an 'access denied' error.
81 This retry loop does not completely solve the problem (who
82 knows how long the other app is going to hold onto it for), but
83 goes a long way towards minimizing it. It is not an infinite
84 loop because there might really be an error.
86 Another reason for retrying delete operations on Windows
87 is that they are asynchronous -- the file or directory is not
88 actually deleted until the last handle to it is closed. The
89 retry loop cannot completely solve this problem either, but can
92 #define RETRY_MAX_ATTEMPTS 100
93 #define RETRY_INITIAL_SLEEP 1000
94 #define RETRY_MAX_SLEEP 128000
96 #define RETRY_LOOP(err, expr, retry_test, sleep_test) \
99 apr_status_t os_err = APR_TO_OS_ERROR(err); \
100 int sleep_count = RETRY_INITIAL_SLEEP; \
103 retries < RETRY_MAX_ATTEMPTS && (retry_test); \
104 os_err = APR_TO_OS_ERROR(err)) \
109 apr_sleep(sleep_count); \
110 if (sleep_count < RETRY_MAX_SLEEP) \
118 #if defined(EDEADLK) && APR_HAS_THREADS
119 #define FILE_LOCK_RETRY_LOOP(err, expr) \
122 (APR_STATUS_IS_EINTR(err) || os_err == EDEADLK), \
123 (!APR_STATUS_IS_EINTR(err)))
125 #define FILE_LOCK_RETRY_LOOP(err, expr) \
128 (APR_STATUS_IS_EINTR(err)), \
132 #ifndef WIN32_RETRY_LOOP
133 #if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP)
134 #define WIN32_RETRY_LOOP(err, expr) \
135 RETRY_LOOP(err, expr, (os_err == ERROR_ACCESS_DENIED \
136 || os_err == ERROR_SHARING_VIOLATION \
137 || os_err == ERROR_DIR_NOT_EMPTY), \
140 #define WIN32_RETRY_LOOP(err, expr) ((void)0)
146 #if _WIN32_WINNT < 0x600 /* Does the SDK assume Windows Vista+? */
147 typedef struct _FILE_RENAME_INFO {
148 BOOL ReplaceIfExists;
149 HANDLE RootDirectory;
150 DWORD FileNameLength;
152 } FILE_RENAME_INFO, *PFILE_RENAME_INFO;
154 typedef struct _FILE_DISPOSITION_INFO {
156 } FILE_DISPOSITION_INFO, *PFILE_DISPOSITION_INFO;
158 #define FileRenameInfo 3
159 #define FileDispositionInfo 4
160 #endif /* WIN32 < Vista */
162 /* One-time initialization of the late bound Windows API functions. */
163 static volatile svn_atomic_t win_dynamic_imports_state = 0;
165 /* Pointer to GetFinalPathNameByHandleW function from kernel32.dll. */
166 typedef DWORD (WINAPI *GETFINALPATHNAMEBYHANDLE)(
172 typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile,
173 int FileInformationClass,
174 LPVOID lpFileInformation,
177 static GETFINALPATHNAMEBYHANDLE get_final_path_name_by_handle_proc = NULL;
178 static SetFileInformationByHandle_t set_file_information_by_handle_proc = NULL;
180 /* Forward declaration. */
181 static svn_error_t * io_win_read_link(svn_string_t **dest,
187 /* Forward declaration */
189 dir_is_empty(const char *dir, apr_pool_t *pool);
190 static APR_INLINE svn_error_t *
191 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
192 const char *msg, const char *msg_no_name,
195 /* Local wrapper of svn_path_cstring_to_utf8() that does no copying on
196 * operating systems where APR always uses utf-8 as native path format */
198 cstring_to_utf8(const char **path_utf8,
199 const char *path_apr,
202 #if defined(WIN32) || defined(DARWIN)
203 *path_utf8 = path_apr;
206 return svn_path_cstring_to_utf8(path_utf8, path_apr, pool);
210 /* Local wrapper of svn_path_cstring_from_utf8() that does no copying on
211 * operating systems where APR always uses utf-8 as native path format */
213 cstring_from_utf8(const char **path_apr,
214 const char *path_utf8,
217 #if defined(WIN32) || defined(DARWIN)
218 *path_apr = path_utf8;
221 return svn_path_cstring_from_utf8(path_apr, path_utf8, pool);
225 /* Helper function that allows to convert an APR-level PATH to something
226 * that we can pass the svn_error_wrap_apr. Since we use it in context
227 * of error reporting, having *some* path info may be more useful than
228 * having none. Therefore, we use a best effort approach here.
230 * This is different from svn_io_file_name_get in that it uses a different
231 * signature style and will never fail.
234 try_utf8_from_internal_style(const char *path, apr_pool_t *pool)
237 const char *path_utf8;
243 /* (try to) convert PATH to UTF-8. If that fails, continue with the plain
244 * PATH because it is the best we have. It may actually be UTF-8 already.
246 error = cstring_to_utf8(&path_utf8, path, pool);
249 /* fallback to best representation we have */
251 svn_error_clear(error);
255 /* Toggle (back-)slashes etc. as necessary.
257 return svn_dirent_local_style(path_utf8, pool);
261 /* Set *NAME_P to the UTF-8 representation of directory entry NAME.
262 * NAME is in the internal encoding used by APR; PARENT is in
263 * UTF-8 and in internal (not local) style.
265 * Use PARENT only for generating an error string if the conversion
266 * fails because NAME could not be represented in UTF-8. In that
267 * case, return a two-level error in which the outer error's message
268 * mentions PARENT, but the inner error's message does not mention
269 * NAME (except possibly in hex) since NAME may not be printable.
270 * Such a compound error at least allows the user to go looking in the
271 * right directory for the problem.
273 * If there is any other error, just return that error directly.
275 * If there is any error, the effect on *NAME_P is undefined.
277 * *NAME_P and NAME may refer to the same storage.
280 entry_name_to_utf8(const char **name_p,
285 #if defined(WIN32) || defined(DARWIN)
286 *name_p = apr_pstrdup(pool, name);
289 svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool);
290 if (err && err->apr_err == APR_EINVAL)
292 return svn_error_createf(err->apr_err, err,
293 _("Error converting entry "
294 "in directory '%s' to UTF-8"),
295 svn_dirent_local_style(parent, pool));
304 map_apr_finfo_to_node_kind(svn_node_kind_t *kind,
305 svn_boolean_t *is_special,
310 if (finfo->filetype == APR_REG)
311 *kind = svn_node_file;
312 else if (finfo->filetype == APR_DIR)
313 *kind = svn_node_dir;
314 else if (finfo->filetype == APR_LNK)
317 *kind = svn_node_file;
320 *kind = svn_node_unknown;
323 /* Helper for svn_io_check_path() and svn_io_check_resolved_path();
324 essentially the same semantics as those two, with the obvious
325 interpretation for RESOLVE_SYMLINKS. */
327 io_check_path(const char *path,
328 svn_boolean_t resolve_symlinks,
329 svn_boolean_t *is_special_p,
330 svn_node_kind_t *kind,
335 apr_status_t apr_err;
336 const char *path_apr;
337 svn_boolean_t is_special = FALSE;
342 /* Not using svn_io_stat() here because we want to check the
343 apr_err return explicitly. */
344 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
346 flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK);
347 apr_err = apr_stat(&finfo, path_apr, flags, pool);
349 if (APR_STATUS_IS_ENOENT(apr_err))
350 *kind = svn_node_none;
351 else if (SVN__APR_STATUS_IS_ENOTDIR(apr_err))
352 *kind = svn_node_none;
354 return svn_error_wrap_apr(apr_err, _("Can't check path '%s'"),
355 svn_dirent_local_style(path, pool));
357 map_apr_finfo_to_node_kind(kind, &is_special, &finfo);
359 *is_special_p = is_special;
365 /* Wrapper for apr_file_open(), taking an APR-encoded filename. */
367 file_open(apr_file_t **f,
368 const char *fname_apr,
370 apr_fileperms_t perm,
371 svn_boolean_t retry_on_failure,
374 apr_status_t status = apr_file_open(f, fname_apr, flag, perm, pool);
376 if (retry_on_failure)
379 if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED))
381 if ((flag & (APR_CREATE | APR_EXCL)) == (APR_CREATE | APR_EXCL))
382 return status; /* Can't create if there is something */
384 if (flag & (APR_WRITE | APR_CREATE))
388 if (!apr_stat(&finfo, fname_apr, SVN__APR_FINFO_READONLY, pool))
390 if (finfo.protection & APR_FREADONLY)
391 return status; /* Retrying won't fix this */
397 WIN32_RETRY_LOOP(status, apr_file_open(f, fname_apr, flag, perm, pool));
404 svn_io_check_resolved_path(const char *path,
405 svn_node_kind_t *kind,
408 svn_boolean_t ignored;
409 return io_check_path(path, TRUE, &ignored, kind, pool);
413 svn_io_check_path(const char *path,
414 svn_node_kind_t *kind,
417 svn_boolean_t ignored;
418 return io_check_path(path, FALSE, &ignored, kind, pool);
422 svn_io_check_special_path(const char *path,
423 svn_node_kind_t *kind,
424 svn_boolean_t *is_special,
427 return io_check_path(path, FALSE, is_special, kind, pool);
430 struct temp_file_cleanup_s
433 /* The (APR-encoded) full path of the file to be removed, or NULL if
435 const char *fname_apr;
440 temp_file_plain_cleanup_handler(void *baton)
442 struct temp_file_cleanup_s *b = baton;
443 apr_status_t apr_err = APR_SUCCESS;
447 apr_err = apr_file_remove(b->fname_apr, b->pool);
448 WIN32_RETRY_LOOP(apr_err, apr_file_remove(b->fname_apr, b->pool));
456 temp_file_child_cleanup_handler(void *baton)
458 struct temp_file_cleanup_s *b = baton;
460 apr_pool_cleanup_kill(b->pool, b,
461 temp_file_plain_cleanup_handler);
468 svn_io_open_uniquely_named(apr_file_t **file,
469 const char **unique_path,
471 const char *filename,
473 svn_io_file_del_t delete_when,
474 apr_pool_t *result_pool,
475 apr_pool_t *scratch_pool)
479 struct temp_file_cleanup_s *baton = NULL;
481 /* At the beginning, we don't know whether unique_path will need
483 svn_boolean_t needs_utf8_conversion = TRUE;
485 SVN_ERR_ASSERT(file || unique_path);
488 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
489 if (filename == NULL)
490 filename = "tempfile";
494 path = svn_dirent_join(dirpath, filename, scratch_pool);
496 if (delete_when == svn_io_file_del_on_pool_cleanup)
498 baton = apr_palloc(result_pool, sizeof(*baton));
500 baton->pool = result_pool;
501 baton->fname_apr = NULL;
503 /* Because cleanups are run LIFO, we need to make sure to register
504 our cleanup before the apr_file_close cleanup:
506 On Windows, you can't remove an open file.
508 apr_pool_cleanup_register(result_pool, baton,
509 temp_file_plain_cleanup_handler,
510 temp_file_child_cleanup_handler);
513 for (i = 1; i <= 99999; i++)
515 const char *unique_name;
516 const char *unique_name_apr;
517 apr_file_t *try_file;
518 apr_status_t apr_err;
519 apr_int32_t flag = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL
520 | APR_BUFFERED | APR_BINARY);
522 if (delete_when == svn_io_file_del_on_close)
523 flag |= APR_DELONCLOSE;
525 /* Special case the first attempt -- if we can avoid having a
526 generated numeric portion at all, that's best. So first we
527 try with just the suffix; then future tries add a number
528 before the suffix. (A do-while loop could avoid the repeated
529 conditional, but it's not worth the clarity loss.)
531 If the first attempt fails, the first number will be "2".
532 This is good, since "1" would misleadingly imply that
533 the second attempt was actually the first... and if someone's
534 got conflicts on their conflicts, we probably don't want to
535 add to their confusion :-). */
537 unique_name = apr_psprintf(scratch_pool, "%s%s", path, suffix);
539 unique_name = apr_psprintf(scratch_pool, "%s.%u%s", path, i, suffix);
541 /* Hmmm. Ideally, we would append to a native-encoding buf
542 before starting iteration, then convert back to UTF-8 for
543 return. But I suppose that would make the appending code
544 sensitive to i18n in a way it shouldn't be... Oh well. */
545 if (needs_utf8_conversion)
547 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name,
551 /* The variable parts of unique_name will not require UTF8
552 conversion. Therefore, if UTF8 conversion had no effect
553 on it in the first iteration, it won't require conversion
554 in any future iteration. */
555 needs_utf8_conversion = strcmp(unique_name_apr, unique_name);
559 unique_name_apr = unique_name;
561 apr_err = file_open(&try_file, unique_name_apr, flag,
562 APR_OS_DEFAULT, FALSE, result_pool);
564 if (APR_STATUS_IS_EEXIST(apr_err))
568 /* On Win32, CreateFile fails with an "Access Denied" error
569 code, rather than "File Already Exists", if the colliding
570 name belongs to a directory. */
571 if (APR_STATUS_IS_EACCES(apr_err))
574 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
575 APR_FINFO_TYPE, scratch_pool);
577 if (!apr_err_2 && finfo.filetype == APR_DIR)
581 if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED) ||
582 apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
584 /* The file is in use by another process or is hidden;
585 create a new name, but don't do this 99999 times in
586 case the folder is not writable */
592 /* Else fall through and return the original error. */
599 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
600 svn_dirent_local_style(unique_name,
605 if (delete_when == svn_io_file_del_on_pool_cleanup)
606 baton->fname_apr = apr_pstrdup(result_pool, unique_name_apr);
611 apr_file_close(try_file);
613 *unique_path = apr_pstrdup(result_pool, unique_name);
623 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
625 _("Unable to make name for '%s'"),
626 svn_dirent_local_style(path, scratch_pool));
630 svn_io_create_unique_link(const char **unique_name_p,
638 const char *unique_name;
639 const char *unique_name_apr;
640 const char *dest_apr;
643 SVN_ERR(cstring_from_utf8(&dest_apr, dest, pool));
644 for (i = 1; i <= 99999; i++)
646 apr_status_t apr_err;
648 /* Special case the first attempt -- if we can avoid having a
649 generated numeric portion at all, that's best. So first we
650 try with just the suffix; then future tries add a number
651 before the suffix. (A do-while loop could avoid the repeated
652 conditional, but it's not worth the clarity loss.)
654 If the first attempt fails, the first number will be "2".
655 This is good, since "1" would misleadingly imply that
656 the second attempt was actually the first... and if someone's
657 got conflicts on their conflicts, we probably don't want to
658 add to their confusion :-). */
660 unique_name = apr_psprintf(pool, "%s%s", path, suffix);
662 unique_name = apr_psprintf(pool, "%s.%u%s", path, i, suffix);
664 /* Hmmm. Ideally, we would append to a native-encoding buf
665 before starting iteration, then convert back to UTF-8 for
666 return. But I suppose that would make the appending code
667 sensitive to i18n in a way it shouldn't be... Oh well. */
668 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, pool));
670 rv = symlink(dest_apr, unique_name_apr);
671 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
673 apr_err = apr_get_os_error();
675 if (rv == -1 && APR_STATUS_IS_EEXIST(apr_err))
677 else if (rv == -1 && apr_err)
679 /* On Win32, CreateFile fails with an "Access Denied" error
680 code, rather than "File Already Exists", if the colliding
681 name belongs to a directory. */
682 if (APR_STATUS_IS_EACCES(apr_err))
685 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
686 APR_FINFO_TYPE, pool);
689 && (finfo.filetype == APR_DIR))
692 /* Else ignore apr_err_2; better to fall through and
693 return the original error. */
696 *unique_name_p = NULL;
697 return svn_error_wrap_apr(apr_err,
698 _("Can't create symbolic link '%s'"),
699 svn_dirent_local_style(unique_name, pool));
703 *unique_name_p = unique_name;
708 *unique_name_p = NULL;
709 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
711 _("Unable to make name for '%s'"),
712 svn_dirent_local_style(path, pool));
714 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
715 _("Symbolic links are not supported on this "
721 svn_io_read_link(svn_string_t **dest,
725 #if defined(HAVE_READLINK)
726 svn_string_t dest_apr;
727 const char *path_apr;
731 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
733 rv = readlink(path_apr, buf, sizeof(buf) - 1);
734 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
737 return svn_error_wrap_apr(apr_get_os_error(),
738 _("Can't read contents of link"));
744 /* ### Cast needed, one of these interfaces is wrong */
745 return svn_utf_string_to_utf8((const svn_string_t **)dest, &dest_apr, pool);
747 return io_win_read_link(dest, path, pool);
749 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
750 _("Symbolic links are not supported on this "
757 svn_io_copy_link(const char *src,
763 svn_string_t *link_dest;
766 /* Notice what the link is pointing at... */
767 SVN_ERR(svn_io_read_link(&link_dest, src, pool));
769 /* Make a tmp-link pointing at the same thing. */
770 SVN_ERR(svn_io_create_unique_link(&dst_tmp, dst, link_dest->data,
773 /* Move the tmp-link to link. */
774 return svn_io_file_rename2(dst_tmp, dst, FALSE, pool);
777 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
778 _("Symbolic links are not supported on this "
783 /* Temporary directory name cache for svn_io_temp_dir() */
784 static volatile svn_atomic_t temp_dir_init_state = 0;
785 static const char *temp_dir;
787 /* Helper function to initialize temp dir. Passed to svn_atomic__init_once */
789 init_temp_dir(void *baton, apr_pool_t *scratch_pool)
791 /* Global pool for the temp path */
792 apr_pool_t *global_pool = svn_pool_create(NULL);
795 apr_status_t apr_err = apr_temp_dir_get(&dir, scratch_pool);
798 return svn_error_wrap_apr(apr_err, _("Can't find a temporary directory"));
800 SVN_ERR(cstring_to_utf8(&dir, dir, scratch_pool));
802 dir = svn_dirent_internal_style(dir, scratch_pool);
804 SVN_ERR(svn_dirent_get_absolute(&temp_dir, dir, global_pool));
811 svn_io_temp_dir(const char **dir,
814 SVN_ERR(svn_atomic__init_once(&temp_dir_init_state,
815 init_temp_dir, NULL, pool));
817 *dir = apr_pstrdup(pool, temp_dir);
825 /*** Creating, copying and appending files. ***/
827 /* Transfer the contents of FROM_FILE to TO_FILE, using POOL for temporary
830 * NOTE: We don't use apr_copy_file() for this, since it takes filenames
831 * as parameters. Since we want to copy to a temporary file
832 * and rename for atomicity (see below), this would require an extra
833 * close/open pair, which can be expensive, especially on
834 * remote file systems.
837 copy_contents(apr_file_t *from_file,
841 /* Copy bytes till the cows come home. */
844 char buf[SVN__STREAM_CHUNK_SIZE];
845 apr_size_t bytes_this_time = sizeof(buf);
846 apr_status_t read_err;
847 apr_status_t write_err;
850 read_err = apr_file_read(from_file, buf, &bytes_this_time);
851 if (read_err && !APR_STATUS_IS_EOF(read_err))
857 write_err = apr_file_write_full(to_file, buf, bytes_this_time, NULL);
863 if (read_err && APR_STATUS_IS_EOF(read_err))
865 /* Return the results of this close: an error, or success. */
874 svn_io_copy_file(const char *src,
876 svn_boolean_t copy_perms,
879 apr_file_t *from_file, *to_file;
880 apr_status_t apr_err;
884 /* ### NOTE: sometimes src == dst. In this case, because we copy to a
885 ### temporary file, and then rename over the top of the destination,
886 ### the net result is resetting the permissions on src/dst.
888 ### Note: specifically, this can happen during a switch when the desired
889 ### permissions for a file change from one branch to another. See
892 ### ... yes, we should avoid copying to the same file, and we should
893 ### make the "reset perms" explicit. The switch *happens* to work
894 ### because of this copy-to-temp-then-rename implementation. If it
895 ### weren't for that, the switch would break.
897 #ifdef CHECK_FOR_SAME_FILE
898 if (strcmp(src, dst) == 0)
902 SVN_ERR(svn_io_file_open(&from_file, src, APR_READ,
903 APR_OS_DEFAULT, pool));
905 /* For atomicity, we copy to a tmp file and then rename the tmp
906 file over the real destination. */
908 SVN_ERR(svn_io_open_unique_file3(&to_file, &dst_tmp,
909 svn_dirent_dirname(dst, pool),
910 svn_io_file_del_none, pool, pool));
912 apr_err = copy_contents(from_file, to_file, pool);
916 err = svn_error_wrap_apr(apr_err, _("Can't copy '%s' to '%s'"),
917 svn_dirent_local_style(src, pool),
918 svn_dirent_local_style(dst_tmp, pool));
923 err = svn_error_compose_create(err,
924 svn_io_file_close(from_file, pool));
926 err = svn_error_compose_create(err,
927 svn_io_file_close(to_file, pool));
931 return svn_error_compose_create(
933 svn_io_remove_file2(dst_tmp, TRUE, pool));
936 /* If copying perms, set the perms on dst_tmp now, so they will be
937 atomically inherited in the upcoming rename. But note that we
938 had to wait until now to set perms, because if they say
939 read-only, then we'd have failed filling dst_tmp's contents. */
941 SVN_ERR(svn_io_copy_perms(src, dst_tmp, pool));
943 return svn_error_trace(svn_io_file_rename2(dst_tmp, dst, FALSE, pool));
946 #if !defined(WIN32) && !defined(__OS2__)
947 /* Wrapper for apr_file_perms_set(), taking a UTF8-encoded filename. */
949 file_perms_set(const char *fname, apr_fileperms_t perms,
952 const char *fname_apr;
955 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
957 status = apr_file_perms_set(fname_apr, perms);
959 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
965 /* Set permissions PERMS on the FILE. This is a cheaper variant of the
966 * file_perms_set wrapper() function because no locale-dependent string
967 * conversion is required. POOL will be used for allocations.
970 file_perms_set2(apr_file_t* file, apr_fileperms_t perms, apr_pool_t *pool)
972 const char *fname_apr;
975 status = apr_file_name_get(&fname_apr, file);
977 return svn_error_wrap_apr(status, _("Can't get file name"));
979 status = apr_file_perms_set(fname_apr, perms);
981 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
982 try_utf8_from_internal_style(fname_apr, pool));
987 #endif /* !WIN32 && !__OS2__ */
990 svn_io_copy_perms(const char *src,
994 /* ### On Windows or OS/2, apr_file_perms_set always returns APR_ENOTIMPL,
995 and the path passed to apr_file_perms_set must be encoded
996 in the platform-specific path encoding; not necessary UTF-8.
997 We need a platform-specific implementation to get the
998 permissions right. */
1000 #if !defined(WIN32) && !defined(__OS2__)
1003 svn_node_kind_t kind;
1004 svn_boolean_t is_special;
1007 /* If DST is a symlink, don't bother copying permissions. */
1008 SVN_ERR(svn_io_check_special_path(dst, &kind, &is_special, pool));
1010 return SVN_NO_ERROR;
1012 SVN_ERR(svn_io_stat(&finfo, src, APR_FINFO_PROT, pool));
1013 err = file_perms_set(dst, finfo.protection, pool);
1016 /* We shouldn't be able to get APR_INCOMPLETE or APR_ENOTIMPL
1017 here under normal circumstances, because the perms themselves
1018 came from a call to apr_file_info_get(), and we already know
1019 this is the non-Win32 case. But if it does happen, it's not
1021 if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
1022 APR_STATUS_IS_ENOTIMPL(err->apr_err))
1023 svn_error_clear(err);
1026 return svn_error_quick_wrapf(
1027 err, _("Can't set permissions on '%s'"),
1028 svn_dirent_local_style(dst, pool));
1032 #endif /* !WIN32 && !__OS2__ */
1034 return SVN_NO_ERROR;
1039 svn_io_append_file(const char *src, const char *dst, apr_pool_t *pool)
1041 apr_status_t apr_err;
1042 const char *src_apr, *dst_apr;
1044 SVN_ERR(cstring_from_utf8(&src_apr, src, pool));
1045 SVN_ERR(cstring_from_utf8(&dst_apr, dst, pool));
1047 apr_err = apr_file_append(src_apr, dst_apr, APR_OS_DEFAULT, pool);
1050 return svn_error_wrap_apr(apr_err, _("Can't append '%s' to '%s'"),
1051 svn_dirent_local_style(src, pool),
1052 svn_dirent_local_style(dst, pool));
1054 return SVN_NO_ERROR;
1058 svn_error_t *svn_io_copy_dir_recursively(const char *src,
1059 const char *dst_parent,
1060 const char *dst_basename,
1061 svn_boolean_t copy_perms,
1062 svn_cancel_func_t cancel_func,
1066 svn_node_kind_t kind;
1067 apr_status_t status;
1068 const char *dst_path;
1069 apr_dir_t *this_dir;
1070 apr_finfo_t this_entry;
1071 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
1073 /* Make a subpool for recursion */
1074 apr_pool_t *subpool = svn_pool_create(pool);
1076 /* The 'dst_path' is simply dst_parent/dst_basename */
1077 dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
1079 /* Sanity checks: SRC and DST_PARENT are directories, and
1080 DST_BASENAME doesn't already exist in DST_PARENT. */
1081 SVN_ERR(svn_io_check_path(src, &kind, subpool));
1082 if (kind != svn_node_dir)
1083 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1084 _("Source '%s' is not a directory"),
1085 svn_dirent_local_style(src, pool));
1087 SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool));
1088 if (kind != svn_node_dir)
1089 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1090 _("Destination '%s' is not a directory"),
1091 svn_dirent_local_style(dst_parent, pool));
1093 SVN_ERR(svn_io_check_path(dst_path, &kind, subpool));
1094 if (kind != svn_node_none)
1095 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
1096 _("Destination '%s' already exists"),
1097 svn_dirent_local_style(dst_path, pool));
1099 /* Create the new directory. */
1100 /* ### TODO: copy permissions (needs apr_file_attrs_get()) */
1101 SVN_ERR(svn_io_dir_make(dst_path, APR_OS_DEFAULT, pool));
1103 /* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */
1104 SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
1106 for (status = apr_dir_read(&this_entry, flags, this_dir);
1107 status == APR_SUCCESS;
1108 status = apr_dir_read(&this_entry, flags, this_dir))
1110 if ((this_entry.name[0] == '.')
1111 && ((this_entry.name[1] == '\0')
1112 || ((this_entry.name[1] == '.')
1113 && (this_entry.name[2] == '\0'))))
1119 const char *src_target, *entryname_utf8;
1122 SVN_ERR(cancel_func(cancel_baton));
1124 SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
1126 src_target = svn_dirent_join(src, entryname_utf8, subpool);
1128 if (this_entry.filetype == APR_REG) /* regular file */
1130 const char *dst_target = svn_dirent_join(dst_path,
1133 SVN_ERR(svn_io_copy_file(src_target, dst_target,
1134 copy_perms, subpool));
1136 else if (this_entry.filetype == APR_LNK) /* symlink */
1138 const char *dst_target = svn_dirent_join(dst_path,
1141 SVN_ERR(svn_io_copy_link(src_target, dst_target,
1144 else if (this_entry.filetype == APR_DIR) /* recurse */
1146 /* Prevent infinite recursion by filtering off our
1147 newly created destination path. */
1148 if (strcmp(src, dst_parent) == 0
1149 && strcmp(entryname_utf8, dst_basename) == 0)
1152 SVN_ERR(svn_io_copy_dir_recursively
1161 /* ### support other APR node types someday?? */
1166 if (! (APR_STATUS_IS_ENOENT(status)))
1167 return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
1168 svn_dirent_local_style(src, pool));
1170 status = apr_dir_close(this_dir);
1172 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
1173 svn_dirent_local_style(src, pool));
1175 /* Free any memory used by recursion */
1176 svn_pool_destroy(subpool);
1178 return SVN_NO_ERROR;
1183 svn_io_make_dir_recursively(const char *path, apr_pool_t *pool)
1185 const char *path_apr;
1186 apr_status_t apr_err;
1188 if (svn_path_is_empty(path))
1189 /* Empty path (current dir) is assumed to always exist,
1190 so we do nothing, per docs. */
1191 return SVN_NO_ERROR;
1193 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1195 apr_err = apr_dir_make_recursive(path_apr, APR_OS_DEFAULT, pool);
1197 /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a
1199 if (apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
1200 WIN32_RETRY_LOOP(apr_err, apr_dir_make_recursive(path_apr,
1201 APR_OS_DEFAULT, pool));
1205 return svn_error_wrap_apr(apr_err, _("Can't make directory '%s'"),
1206 svn_dirent_local_style(path, pool));
1208 return SVN_NO_ERROR;
1212 svn_io_file_create_bytes(const char *file,
1213 const void *contents,
1215 apr_pool_t *scratch_pool)
1219 svn_error_t *err = SVN_NO_ERROR;
1221 SVN_ERR(svn_io_file_open(&f, file,
1222 (APR_WRITE | APR_CREATE | APR_EXCL),
1226 err = svn_io_file_write_full(f, contents, length, &written,
1229 err = svn_error_compose_create(
1231 svn_io_file_close(f, scratch_pool));
1235 /* Our caller doesn't know if we left a file or not if we return
1236 an error. Better to cleanup after ourselves if we created the
1238 return svn_error_trace(
1239 svn_error_compose_create(
1241 svn_io_remove_file2(file, TRUE, scratch_pool)));
1244 return SVN_NO_ERROR;
1248 svn_io_file_create(const char *file,
1249 const char *contents,
1252 return svn_error_trace(svn_io_file_create_bytes(file, contents,
1253 contents ? strlen(contents)
1259 svn_io_file_create_empty(const char *file,
1260 apr_pool_t *scratch_pool)
1262 return svn_error_trace(svn_io_file_create_bytes(file, NULL, 0,
1267 svn_io_dir_file_copy(const char *src_path,
1268 const char *dest_path,
1272 const char *file_dest_path = svn_dirent_join(dest_path, file, pool);
1273 const char *file_src_path = svn_dirent_join(src_path, file, pool);
1275 return svn_error_trace(
1276 svn_io_copy_file(file_src_path, file_dest_path, TRUE, pool));
1280 /*** Modtime checking. ***/
1283 svn_io_file_affected_time(apr_time_t *apr_time,
1289 SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_MIN | APR_FINFO_LINK, pool));
1291 *apr_time = finfo.mtime;
1293 return SVN_NO_ERROR;
1298 svn_io_set_file_affected_time(apr_time_t apr_time,
1302 apr_status_t status;
1303 const char *native_path;
1305 SVN_ERR(cstring_from_utf8(&native_path, path, pool));
1306 status = apr_file_mtime_set(native_path, apr_time, pool);
1309 return svn_error_wrap_apr(status, _("Can't set access time of '%s'"),
1310 svn_dirent_local_style(path, pool));
1312 return SVN_NO_ERROR;
1317 svn_io_sleep_for_timestamps(const char *path, apr_pool_t *pool)
1319 apr_time_t now, then;
1321 char *sleep_env_var;
1323 sleep_env_var = getenv(SVN_SLEEP_ENV_VAR);
1325 if (sleep_env_var && apr_strnatcasecmp(sleep_env_var, "yes") == 0)
1326 return; /* Allow skipping for testing */
1328 now = apr_time_now();
1330 /* Calculate 0.02 seconds after the next second wallclock tick. */
1331 then = apr_time_make(apr_time_sec(now) + 1, APR_USEC_PER_SEC / 50);
1333 /* Worst case is waiting one second, so we can use that time to determine
1334 if we can sleep shorter than that */
1339 err = svn_io_stat(&finfo, path, APR_FINFO_MTIME | APR_FINFO_LINK, pool);
1343 svn_error_clear(err); /* Fall back on original behavior */
1345 else if (finfo.mtime % APR_USEC_PER_SEC)
1347 /* Very simplistic but safe approach:
1348 If the filesystem has < sec mtime we can be reasonably sure
1349 that the filesystem has some sub-second resolution. On Windows
1350 it is likely to be sub-millisecond; on Linux systems it depends
1351 on the filesystem, ext4 is typically 1ms, 4ms or 10ms resolution.
1353 ## Perhaps find a better algorithm here. This will fail once
1354 in every 1000 cases on a millisecond precision filesystem
1355 if the mtime happens to be an exact second.
1357 But better to fail once in every thousand cases than every
1358 time, like we did before.
1360 Note for further research on algorithm:
1361 FAT32 has < 1 sec precision on ctime, but 2 sec on mtime.
1363 Linux/ext4 with CONFIG_HZ=250 has high resolution
1364 apr_time_now and although the filesystem timestamps
1365 have similar high precision they are only updated with
1366 a coarser 4ms resolution. */
1368 /* 10 milliseconds after now. */
1369 #ifndef SVN_HI_RES_SLEEP_MS
1370 #define SVN_HI_RES_SLEEP_MS 10
1372 then = now + apr_time_from_msec(SVN_HI_RES_SLEEP_MS);
1375 /* Remove time taken to do stat() from sleep. */
1376 now = apr_time_now();
1380 return; /* Passing negative values may suspend indefinitely (Windows) */
1382 /* (t < 1000 will be round to 0 in apr) */
1383 if (then - now < 1000)
1386 apr_sleep(then - now);
1391 svn_io_filesizes_different_p(svn_boolean_t *different_p,
1398 apr_status_t status;
1399 const char *file1_apr, *file2_apr;
1401 /* Not using svn_io_stat() because don't want to generate
1402 svn_error_t objects for non-error conditions. */
1404 SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool));
1405 SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool));
1407 /* Stat both files */
1408 status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool);
1411 /* If we got an error stat'ing a file, it could be because the
1412 file was removed... or who knows. Whatever the case, we
1413 don't know if the filesizes are definitely different, so
1414 assume that they're not. */
1415 *different_p = FALSE;
1416 return SVN_NO_ERROR;
1419 status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool);
1422 /* See previous comment. */
1423 *different_p = FALSE;
1424 return SVN_NO_ERROR;
1427 /* Examine file sizes */
1428 if (finfo1.size == finfo2.size)
1429 *different_p = FALSE;
1431 *different_p = TRUE;
1433 return SVN_NO_ERROR;
1438 svn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
1439 svn_boolean_t *different_p23,
1440 svn_boolean_t *different_p13,
1444 apr_pool_t *scratch_pool)
1446 apr_finfo_t finfo1, finfo2, finfo3;
1447 apr_status_t status1, status2, status3;
1448 const char *file1_apr, *file2_apr, *file3_apr;
1450 /* Not using svn_io_stat() because don't want to generate
1451 svn_error_t objects for non-error conditions. */
1453 SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool));
1454 SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool));
1455 SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool));
1457 /* Stat all three files */
1458 status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool);
1459 status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool);
1460 status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool);
1462 /* If we got an error stat'ing a file, it could be because the
1463 file was removed... or who knows. Whatever the case, we
1464 don't know if the filesizes are definitely different, so
1465 assume that they're not. */
1466 *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size;
1467 *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size;
1468 *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size;
1470 return SVN_NO_ERROR;
1475 svn_io_file_checksum2(svn_checksum_t **checksum,
1477 svn_checksum_kind_t kind,
1480 svn_stream_t *file_stream;
1483 SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool));
1484 file_stream = svn_stream_from_aprfile2(f, FALSE, pool);
1485 SVN_ERR(svn_stream_contents_checksum(checksum, file_stream, kind,
1488 return SVN_NO_ERROR;
1493 svn_io_file_checksum(unsigned char digest[],
1497 svn_checksum_t *checksum;
1499 SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool));
1500 memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE);
1502 return SVN_NO_ERROR;
1507 /*** Permissions and modes. ***/
1509 #if !defined(WIN32) && !defined(__OS2__)
1510 /* Given the file specified by PATH, attempt to create an
1511 identical version of it owned by the current user. This is done by
1512 moving it to a temporary location, copying the file back to its old
1513 path, then deleting the temporarily moved version. All temporary
1514 allocations are done in POOL. */
1515 static svn_error_t *
1516 reown_file(const char *path,
1519 const char *unique_name;
1521 SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name,
1522 svn_dirent_dirname(path, pool),
1523 svn_io_file_del_none, pool, pool));
1524 SVN_ERR(svn_io_file_rename2(path, unique_name, FALSE, pool));
1525 SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool));
1526 return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool));
1529 /* Determine what the PERMS for a new file should be by looking at the
1530 permissions of a temporary file that we create in DIRECTORY.
1531 DIRECTORY can be NULL in which case the system temporary dir is used.
1532 Unfortunately, umask() as defined in POSIX provides no thread-safe way
1533 to get at the current value of the umask, so what we're doing here is
1534 the only way we have to determine which combination of write bits
1535 (User/Group/World) should be set by default.
1536 Make temporary allocations in SCRATCH_POOL. */
1537 static svn_error_t *
1538 get_default_file_perms(apr_fileperms_t *perms,
1539 const char *directory,
1540 apr_pool_t *scratch_pool)
1542 /* the default permissions as read from the temp folder */
1543 static apr_fileperms_t default_perms = 0;
1545 /* Technically, this "racy": Multiple threads may use enter here and
1546 try to figure out the default permission concurrently. That's fine
1547 since they will end up with the same results. Even more technical,
1548 apr_fileperms_t is an atomic type on 32+ bit machines.
1550 if (default_perms == 0)
1554 const char *fname_base, *fname;
1555 apr_uint32_t randomish;
1558 /* Get the perms for a newly created file to find out what bits
1561 Explicitly delete the file because we want this file to be as
1562 short-lived as possible since its presence means other
1563 processes may have to try multiple names.
1565 Using svn_io_open_uniquely_named() here because other tempfile
1566 creation functions tweak the permission bits of files they create.
1568 Note that APR pool structures are allocated as the first item
1569 in their first memory page (with e.g. 4kB granularity), i.e. the
1570 lower bits tend to be identical between pool instances. That is
1571 particularly true for the MMAPed allocator.
1573 randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool
1574 + (apr_uint32_t)((apr_uintptr_t)scratch_pool / 4096)
1575 + (apr_uint32_t)apr_time_now());
1576 fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish);
1578 SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, directory, fname_base,
1579 NULL, svn_io_file_del_none,
1580 scratch_pool, scratch_pool));
1581 err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool);
1582 err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool));
1583 err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE,
1586 *perms = finfo.protection;
1587 default_perms = finfo.protection;
1590 *perms = default_perms;
1592 return SVN_NO_ERROR;
1595 /* OR together permission bits of the file FD and the default permissions
1596 of a file as determined by get_default_file_perms(). DIRECTORY is used
1597 to create temporary files, DIRECTORY can be NULL. Do temporary
1598 allocations in SCRATCH_POOL. */
1599 static svn_error_t *
1600 merge_default_file_perms(apr_file_t *fd,
1601 apr_fileperms_t *perms,
1602 const char *directory,
1603 apr_pool_t *scratch_pool)
1606 apr_fileperms_t default_perms;
1608 SVN_ERR(get_default_file_perms(&default_perms, directory, scratch_pool));
1609 SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool));
1611 /* Glom the perms together. */
1612 *perms = default_perms | finfo.protection;
1613 return SVN_NO_ERROR;
1616 /* This is a helper function for the svn_io_set_file_read* functions
1617 that attempts to honor the users umask when dealing with
1618 permission changes. It is a no-op when invoked on a symlink. */
1619 static svn_error_t *
1620 io_set_file_perms(const char *path,
1621 svn_boolean_t change_readwrite,
1622 svn_boolean_t enable_write,
1623 svn_boolean_t change_executable,
1624 svn_boolean_t executable,
1625 svn_boolean_t ignore_enoent,
1628 apr_status_t status;
1629 const char *path_apr;
1631 apr_fileperms_t perms_to_set;
1633 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1635 /* Try to change only a minimal amount of the perms first
1636 by getting the current perms and adding bits
1637 only on where read perms are granted. If this fails
1638 fall through to just setting file attributes. */
1639 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool);
1642 if (ignore_enoent && (APR_STATUS_IS_ENOENT(status)
1643 || SVN__APR_STATUS_IS_ENOTDIR(status)))
1644 return SVN_NO_ERROR;
1645 else if (status != APR_ENOTIMPL)
1646 return svn_error_wrap_apr(status,
1647 _("Can't change perms of file '%s'"),
1648 svn_dirent_local_style(path, pool));
1649 return SVN_NO_ERROR;
1652 if (finfo.filetype == APR_LNK)
1653 return SVN_NO_ERROR;
1655 perms_to_set = finfo.protection;
1656 if (change_readwrite)
1658 if (enable_write) /* Make read-write. */
1660 /* Tweak the owner bits only. The group/other bits aren't safe to
1661 * touch because we may end up setting them in undesired ways. */
1662 perms_to_set |= (APR_UREAD|APR_UWRITE);
1666 if (finfo.protection & APR_UREAD)
1667 perms_to_set &= ~APR_UWRITE;
1668 if (finfo.protection & APR_GREAD)
1669 perms_to_set &= ~APR_GWRITE;
1670 if (finfo.protection & APR_WREAD)
1671 perms_to_set &= ~APR_WWRITE;
1675 if (change_executable)
1679 if (finfo.protection & APR_UREAD)
1680 perms_to_set |= APR_UEXECUTE;
1681 if (finfo.protection & APR_GREAD)
1682 perms_to_set |= APR_GEXECUTE;
1683 if (finfo.protection & APR_WREAD)
1684 perms_to_set |= APR_WEXECUTE;
1688 if (finfo.protection & APR_UREAD)
1689 perms_to_set &= ~APR_UEXECUTE;
1690 if (finfo.protection & APR_GREAD)
1691 perms_to_set &= ~APR_GEXECUTE;
1692 if (finfo.protection & APR_WREAD)
1693 perms_to_set &= ~APR_WEXECUTE;
1697 /* If we aren't changing anything then just return, this saves
1698 some system calls and helps with shared working copies */
1699 if (perms_to_set == finfo.protection)
1700 return SVN_NO_ERROR;
1702 status = apr_file_perms_set(path_apr, perms_to_set);
1704 return SVN_NO_ERROR;
1706 if (APR_STATUS_IS_EPERM(status))
1708 /* We don't have permissions to change the
1709 permissions! Try a move, copy, and delete
1710 workaround to see if we can get the file owned by
1711 us. If these succeed, try the permissions set
1714 Note that we only attempt this in the
1715 stat-available path. This assumes that the
1716 move-copy workaround will only be helpful on
1717 platforms that implement apr_stat. */
1718 SVN_ERR(reown_file(path, pool));
1719 status = apr_file_perms_set(path_apr, perms_to_set);
1723 return SVN_NO_ERROR;
1725 if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1726 return SVN_NO_ERROR;
1727 else if (status == APR_ENOTIMPL)
1729 /* At least try to set the attributes. */
1730 apr_fileattrs_t attrs = 0;
1731 apr_fileattrs_t attrs_values = 0;
1733 if (change_readwrite)
1735 attrs = APR_FILE_ATTR_READONLY;
1737 attrs_values = APR_FILE_ATTR_READONLY;
1739 if (change_executable)
1741 attrs = APR_FILE_ATTR_EXECUTABLE;
1743 attrs_values = APR_FILE_ATTR_EXECUTABLE;
1745 status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
1748 return svn_error_wrap_apr(status,
1749 _("Can't change perms of file '%s'"),
1750 svn_dirent_local_style(path, pool));
1752 #endif /* !WIN32 && !__OS2__ */
1755 /* This is semantically the same as the APR utf8_to_unicode_path
1756 function, but reimplemented here because APR does not export it. */
1758 svn_io__utf8_to_unicode_longpath(const WCHAR **result,
1760 apr_pool_t *result_pool)
1762 /* This is correct, we don't twist the filename if it will
1763 * definitely be shorter than 248 characters. It merits some
1764 * performance testing to see if this has any effect, but there
1765 * seem to be applications that get confused by the resulting
1766 * Unicode \\?\ style file names, especially if they use argv[0]
1767 * or call the Win32 API functions such as GetModuleName, etc.
1768 * Not every application is prepared to handle such names.
1770 * Note also this is shorter than MAX_PATH, as directory paths
1771 * are actually limited to 248 characters.
1773 * Note that a utf-8 name can never result in more wide chars
1774 * than the original number of utf-8 narrow chars.
1776 const WCHAR *prefix = NULL;
1777 const size_t srclen = strlen(source);
1782 if (svn_ctype_isalpha(source[0]) && source[1] == ':'
1783 && (source[2] == '/' || source[2] == '\\'))
1785 /* This is an ordinary absolute path. */
1786 prefix = L"\\\\?\\";
1788 else if ((source[0] == '/' || source[0] == '\\')
1789 && (source[1] == '/' || source[1] == '\\')
1790 && source[2] != '?')
1792 /* This is a UNC path */
1793 source += 2; /* Skip the leading slashes */
1794 prefix = L"\\\\?\\UNC\\";
1798 SVN_ERR(svn_utf__win32_utf8_to_utf16(&(const WCHAR*)buffer, source,
1799 prefix, result_pool));
1801 /* Convert slashes to backslashes because the \\?\ path format
1802 does not allow backslashes as path separators. */
1804 for (; *buffer; ++buffer)
1809 return SVN_NO_ERROR;
1812 /* This is semantically the same as the APR unicode_to_utf8_path
1813 function, but reimplemented here because APR does not export it. */
1814 static svn_error_t *
1815 io_unicode_to_utf8_path(const char **result,
1816 const WCHAR *source,
1817 apr_pool_t *result_pool)
1819 const char *utf8_buffer;
1822 SVN_ERR(svn_utf__win32_utf16_to_utf8(&utf8_buffer, source,
1823 NULL, result_pool));
1826 *result = utf8_buffer;
1827 return SVN_NO_ERROR;
1830 /* We know that the non-empty buffer returned from the UTF-16 to
1831 UTF-8 conversion function is in fact writable. */
1832 buffer = (char*)utf8_buffer;
1834 /* Skip the leading 4 characters if the path begins \\?\, or substitute
1835 * // for the \\?\UNC\ path prefix, allocating the maximum string
1836 * length based on the remaining string, plus the trailing null.
1837 * then transform \\'s back into /'s since the \\?\ form never
1838 * allows '/' path separators, and APR always uses '/'s.
1840 if (0 == strncmp(buffer, "\\\\?\\", 4))
1843 if (0 == strncmp(buffer, "UNC\\", 4))
1851 for (; *buffer; ++buffer)
1853 if (*buffer == '\\')
1856 return SVN_NO_ERROR;
1859 static svn_error_t *
1860 io_win_file_attrs_set(const char *fname,
1865 /* this is an implementation of apr_file_attrs_set() but one
1866 that uses the proper Windows attributes instead of the apr
1867 attributes. This way, we can apply any Windows file and
1868 folder attributes even if apr doesn't implement them */
1870 const WCHAR *wfname;
1872 SVN_ERR(svn_io__utf8_to_unicode_longpath(&wfname, fname, pool));
1874 flags = GetFileAttributesW(wfname);
1875 if (flags == 0xFFFFFFFF)
1876 return svn_error_wrap_apr(apr_get_os_error(),
1877 _("Can't get attributes of file '%s'"),
1878 svn_dirent_local_style(fname, pool));
1880 flags &= ~attr_mask;
1881 flags |= (attributes & attr_mask);
1883 if (!SetFileAttributesW(wfname, flags))
1884 return svn_error_wrap_apr(apr_get_os_error(),
1885 _("Can't set attributes of file '%s'"),
1886 svn_dirent_local_style(fname, pool));
1888 return SVN_NO_ERROR;
1891 static svn_error_t *win_init_dynamic_imports(void *baton, apr_pool_t *pool)
1893 HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
1897 get_final_path_name_by_handle_proc = (GETFINALPATHNAMEBYHANDLE)
1898 GetProcAddress(kernel32, "GetFinalPathNameByHandleW");
1900 set_file_information_by_handle_proc = (SetFileInformationByHandle_t)
1901 GetProcAddress(kernel32, "SetFileInformationByHandle");
1904 return SVN_NO_ERROR;
1907 static svn_error_t * io_win_read_link(svn_string_t **dest,
1911 SVN_ERR(svn_atomic__init_once(&win_dynamic_imports_state,
1912 win_init_dynamic_imports, NULL, pool));
1914 if (get_final_path_name_by_handle_proc)
1917 apr_status_t status;
1919 apr_os_file_t filehand;
1920 WCHAR wdest[APR_PATH_MAX];
1923 /* reserve one char for terminating zero. */
1924 DWORD wdest_len = sizeof(wdest)/sizeof(wdest[0]) - 1;
1926 status = apr_file_open(&file, path, APR_OPENINFO, APR_OS_DEFAULT, pool);
1929 return svn_error_wrap_apr(status,
1930 _("Can't read contents of link"));
1932 apr_os_file_get(&filehand, file);
1934 rv = get_final_path_name_by_handle_proc(
1935 filehand, wdest, wdest_len,
1936 FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
1938 /* Save error code. */
1939 status = apr_get_os_error();
1941 /* Close file/directory handle in any case. */
1942 apr_file_close(file);
1944 /* GetFinaPathNameByHandleW returns number of characters copied to
1945 * output buffer. Returns zero on error. Returns required buffer size
1946 * if supplied buffer is not enough. */
1947 if (rv > wdest_len || rv == 0)
1949 return svn_error_wrap_apr(status,
1950 _("Can't read contents of link"));
1953 /* GetFinaPathNameByHandleW doesn't add terminating NUL. */
1955 SVN_ERR(io_unicode_to_utf8_path(&data, wdest, pool));
1957 /* The result is already in the correct pool, so avoid copying
1958 it to create the string. */
1959 *dest = svn_string_create_empty(pool);
1962 (*dest)->data = data;
1963 (*dest)->len = strlen(data);
1966 return SVN_NO_ERROR;
1970 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1971 _("Symbolic links are not supported on this "
1976 /* Wrapper around Windows API function SetFileInformationByHandle() that
1977 * returns APR status instead of boolean flag. */
1979 win32_set_file_information_by_handle(HANDLE hFile,
1980 int FileInformationClass,
1981 LPVOID lpFileInformation,
1984 svn_error_clear(svn_atomic__init_once(&win_dynamic_imports_state,
1985 win_init_dynamic_imports,
1988 if (!set_file_information_by_handle_proc)
1990 return SVN_ERR_UNSUPPORTED_FEATURE;
1993 if (!set_file_information_by_handle_proc(hFile, FileInformationClass,
1997 return apr_get_os_error();
2004 svn_io__win_delete_file_on_close(apr_file_t *file,
2008 FILE_DISPOSITION_INFO disposition_info;
2010 apr_status_t status;
2012 apr_os_file_get(&hFile, file);
2014 disposition_info.DeleteFile = TRUE;
2016 status = win32_set_file_information_by_handle(hFile, FileDispositionInfo,
2018 sizeof(disposition_info));
2022 return svn_error_wrap_apr(status, _("Can't remove file '%s'"),
2023 svn_dirent_local_style(path, pool));
2026 return SVN_NO_ERROR;
2030 svn_io__win_rename_open_file(apr_file_t *file,
2031 const char *from_path,
2032 const char *to_path,
2035 WCHAR *w_final_abspath;
2038 FILE_RENAME_INFO *rename_info;
2040 apr_status_t status;
2042 apr_os_file_get(&hFile, file);
2044 SVN_ERR(svn_io__utf8_to_unicode_longpath(
2045 &w_final_abspath, svn_dirent_local_style(to_path,pool),
2048 path_len = wcslen(w_final_abspath);
2049 rename_size = sizeof(*rename_info) + sizeof(WCHAR) * path_len;
2051 /* The rename info struct doesn't need hacks for long paths,
2052 so no ugly escaping calls here */
2053 rename_info = apr_pcalloc(pool, rename_size);
2054 rename_info->ReplaceIfExists = TRUE;
2055 rename_info->FileNameLength = path_len;
2056 memcpy(rename_info->FileName, w_final_abspath, path_len * sizeof(WCHAR));
2058 status = win32_set_file_information_by_handle(hFile, FileRenameInfo,
2062 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
2064 /* Set the destination file writable because Windows will not allow
2065 us to rename when final_abspath is read-only. */
2066 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
2068 status = win32_set_file_information_by_handle(hFile,
2074 /* Windows returns Vista+ client accessing network share stored on Windows
2075 Server 2003 returns ERROR_ACCESS_DENIED. The same happens when Vista+
2076 client access Windows Server 2008 with disabled SMBv2 protocol.
2078 So return SVN_ERR_UNSUPPORTED_FEATURE in this case like we do when
2079 SetFileInformationByHandle() is not available and let caller to
2082 See "Access denied error on checkout-commit after updating to 1.9.X"
2083 discussion on dev@s.a.o:
2084 http://svn.haxx.se/dev/archive-2015-09/0054.shtml */
2085 if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED))
2087 status = SVN_ERR_UNSUPPORTED_FEATURE;
2092 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
2093 svn_dirent_local_style(from_path, pool),
2094 svn_dirent_local_style(to_path, pool));
2097 return SVN_NO_ERROR;
2103 svn_io_set_file_read_write_carefully(const char *path,
2104 svn_boolean_t enable_write,
2105 svn_boolean_t ignore_enoent,
2109 return svn_io_set_file_read_write(path, ignore_enoent, pool);
2110 return svn_io_set_file_read_only(path, ignore_enoent, pool);
2114 svn_io_set_file_read_only(const char *path,
2115 svn_boolean_t ignore_enoent,
2118 /* On Windows and OS/2, just set the file attributes -- on unix call
2119 our internal function which attempts to honor the umask. */
2120 #if !defined(WIN32) && !defined(__OS2__)
2121 return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE,
2122 ignore_enoent, pool);
2124 apr_status_t status;
2125 const char *path_apr;
2127 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2129 status = apr_file_attrs_set(path_apr,
2130 APR_FILE_ATTR_READONLY,
2131 APR_FILE_ATTR_READONLY,
2134 if (status && status != APR_ENOTIMPL)
2135 if (!(ignore_enoent && (APR_STATUS_IS_ENOENT(status)
2136 || SVN__APR_STATUS_IS_ENOTDIR(status))))
2137 return svn_error_wrap_apr(status,
2138 _("Can't set file '%s' read-only"),
2139 svn_dirent_local_style(path, pool));
2141 return SVN_NO_ERROR;
2147 svn_io_set_file_read_write(const char *path,
2148 svn_boolean_t ignore_enoent,
2151 /* On Windows and OS/2, just set the file attributes -- on unix call
2152 our internal function which attempts to honor the umask. */
2153 #if !defined(WIN32) && !defined(__OS2__)
2154 return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE,
2155 ignore_enoent, pool);
2157 apr_status_t status;
2158 const char *path_apr;
2160 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2162 status = apr_file_attrs_set(path_apr,
2164 APR_FILE_ATTR_READONLY,
2167 if (status && status != APR_ENOTIMPL)
2168 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
2169 return svn_error_wrap_apr(status,
2170 _("Can't set file '%s' read-write"),
2171 svn_dirent_local_style(path, pool));
2173 return SVN_NO_ERROR;
2178 svn_io_set_file_executable(const char *path,
2179 svn_boolean_t executable,
2180 svn_boolean_t ignore_enoent,
2183 /* On Windows and OS/2, just exit -- on unix call our internal function
2184 which attempts to honor the umask. */
2185 #if (!defined(WIN32) && !defined(__OS2__))
2186 return io_set_file_perms(path, FALSE, FALSE, TRUE, executable,
2187 ignore_enoent, pool);
2189 return SVN_NO_ERROR;
2195 svn_io__is_finfo_read_only(svn_boolean_t *read_only,
2196 apr_finfo_t *file_info,
2199 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2200 apr_status_t apr_err;
2206 apr_err = apr_uid_current(&uid, &gid, pool);
2209 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
2211 /* Check write bit for current user. */
2212 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
2213 *read_only = !(file_info->protection & APR_UWRITE);
2215 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
2216 *read_only = !(file_info->protection & APR_GWRITE);
2219 *read_only = !(file_info->protection & APR_WWRITE);
2221 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
2222 *read_only = (file_info->protection & APR_FREADONLY);
2225 return SVN_NO_ERROR;
2229 svn_io__is_finfo_executable(svn_boolean_t *executable,
2230 apr_finfo_t *file_info,
2233 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2234 apr_status_t apr_err;
2238 *executable = FALSE;
2240 apr_err = apr_uid_current(&uid, &gid, pool);
2243 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
2245 /* Check executable bit for current user. */
2246 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
2247 *executable = (file_info->protection & APR_UEXECUTE);
2249 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
2250 *executable = (file_info->protection & APR_GEXECUTE);
2253 *executable = (file_info->protection & APR_WEXECUTE);
2255 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
2256 *executable = FALSE;
2259 return SVN_NO_ERROR;
2263 svn_io_is_file_executable(svn_boolean_t *executable,
2267 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2268 apr_finfo_t file_info;
2270 SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
2272 SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
2274 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
2275 *executable = FALSE;
2278 return SVN_NO_ERROR;
2282 /*** File locking. ***/
2283 /* Clear all outstanding locks on ARG, an open apr_file_t *. */
2285 file_clear_locks(void *arg)
2287 apr_status_t apr_err;
2288 apr_file_t *f = arg;
2291 apr_err = apr_file_unlock(f);
2299 svn_io_lock_open_file(apr_file_t *lockfile_handle,
2300 svn_boolean_t exclusive,
2301 svn_boolean_t nonblocking,
2304 int locktype = APR_FLOCK_SHARED;
2305 apr_status_t apr_err;
2309 locktype = APR_FLOCK_EXCLUSIVE;
2311 locktype |= APR_FLOCK_NONBLOCK;
2313 /* We need this only in case of an error but this is cheap to get -
2314 * so we do it here for clarity. */
2315 apr_err = apr_file_name_get(&fname, lockfile_handle);
2317 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2319 /* Get lock on the filehandle. */
2320 apr_err = apr_file_lock(lockfile_handle, locktype);
2322 /* In deployments with two or more multithreaded servers running on
2323 the same system serving two or more fsfs repositories it is
2324 possible for a deadlock to occur when getting a write lock on
2325 db/txn-current-lock:
2329 thread 1: get lock in repos A
2330 thread 1: get lock in repos B
2331 thread 2: block getting lock in repos A
2332 thread 2: try to get lock in B *** deadlock ***
2334 Retry for a while for the deadlock to clear. */
2335 FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
2339 switch (locktype & APR_FLOCK_TYPEMASK)
2341 case APR_FLOCK_SHARED:
2342 return svn_error_wrap_apr(apr_err,
2343 _("Can't get shared lock on file '%s'"),
2344 try_utf8_from_internal_style(fname, pool));
2345 case APR_FLOCK_EXCLUSIVE:
2346 return svn_error_wrap_apr(apr_err,
2347 _("Can't get exclusive lock on file '%s'"),
2348 try_utf8_from_internal_style(fname, pool));
2350 SVN_ERR_MALFUNCTION();
2354 /* On Windows, a process may not release file locks before closing the
2355 handle, and in this case the outstanding locks are unlocked by the OS.
2356 However, this is not recommended, because the actual unlocking may be
2357 postponed depending on available system resources. We explicitly unlock
2358 the file as a part of the pool cleanup handler. */
2359 apr_pool_cleanup_register(pool, lockfile_handle,
2361 apr_pool_cleanup_null);
2363 return SVN_NO_ERROR;
2367 svn_io_unlock_open_file(apr_file_t *lockfile_handle,
2371 apr_status_t apr_err;
2373 /* We need this only in case of an error but this is cheap to get -
2374 * so we do it here for clarity. */
2375 apr_err = apr_file_name_get(&fname, lockfile_handle);
2377 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2379 /* The actual unlock attempt. */
2380 apr_err = apr_file_unlock(lockfile_handle);
2382 return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
2383 try_utf8_from_internal_style(fname, pool));
2385 apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks);
2387 return SVN_NO_ERROR;
2391 svn_io_file_lock2(const char *lock_file,
2392 svn_boolean_t exclusive,
2393 svn_boolean_t nonblocking,
2396 int locktype = APR_FLOCK_SHARED;
2397 apr_file_t *lockfile_handle;
2401 locktype = APR_FLOCK_EXCLUSIVE;
2404 if (locktype == APR_FLOCK_EXCLUSIVE)
2407 /* locktype is never read after this block, so we don't need to bother
2408 setting it. If that were to ever change, uncomment the following
2411 locktype |= APR_FLOCK_NONBLOCK;
2414 SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
2418 /* Get lock on the filehandle. */
2419 return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
2423 svn_io__file_lock_autocreate(const char *lock_file,
2427 = svn_io_file_lock2(lock_file, TRUE, FALSE, pool);
2428 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
2430 /* No lock file? No big deal; these are just empty files anyway.
2431 Create it and try again. */
2432 svn_error_clear(err);
2434 /* This file creation is racy.
2435 We don't care as long as file gets created at all. */
2436 err = svn_io_file_create_empty(lock_file, pool);
2437 if (err && APR_STATUS_IS_EEXIST(err->apr_err))
2439 svn_error_clear(err);
2443 /* Finally, lock the file - if it exists */
2445 err = svn_io_file_lock2(lock_file, TRUE, FALSE, pool);
2448 return svn_error_trace(err);
2453 /* Data consistency/coherency operations. */
2455 svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
2458 apr_os_file_t filehand;
2460 apr_status_t apr_err;
2462 /* We need this only in case of an error but this is cheap to get -
2463 * so we do it here for clarity. */
2464 apr_err = apr_file_name_get(&fname, file);
2466 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2468 /* ### In apr 1.4+ we could delegate most of this function to
2469 apr_file_sync(). The only major difference is that this doesn't
2470 contain the retry loop for EINTR on linux. */
2472 /* First make sure that any user-space buffered data is flushed. */
2473 SVN_ERR(svn_io_file_flush(file, pool));
2475 apr_os_file_get(&filehand, file);
2477 /* Call the operating system specific function to actually force the
2482 if (! FlushFileBuffers(filehand))
2483 return svn_error_wrap_apr(apr_get_os_error(),
2484 _("Can't flush file '%s' to disk"),
2485 try_utf8_from_internal_style(fname, pool));
2492 rv = fcntl(filehand, F_FULLFSYNC, 0);
2494 rv = fsync(filehand);
2496 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
2498 /* If the file is in a memory filesystem, fsync() may return
2499 EINVAL. Presumably the user knows the risks, and we can just
2500 ignore the error. */
2501 if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
2502 return SVN_NO_ERROR;
2505 return svn_error_wrap_apr(apr_get_os_error(),
2506 _("Can't flush file '%s' to disk"),
2507 try_utf8_from_internal_style(fname, pool));
2511 return SVN_NO_ERROR;
2516 /* TODO write test for these two functions, then refactor. */
2518 /* Set RESULT to an svn_stringbuf_t containing the contents of FILE.
2519 FILENAME is the FILE's on-disk APR-safe name, or NULL if that name
2520 isn't known. If CHECK_SIZE is TRUE, the function will attempt to
2521 first stat() the file to determine it's size before sucking its
2522 contents into the stringbuf. (Doing so can prevent unnecessary
2523 memory usage, an unwanted side effect of the stringbuf growth and
2524 reallocation mechanism.) */
2525 static svn_error_t *
2526 stringbuf_from_aprfile(svn_stringbuf_t **result,
2527 const char *filename,
2529 svn_boolean_t check_size,
2534 svn_stringbuf_t *res = NULL;
2535 apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE;
2538 /* If our caller wants us to check the size of the file for
2539 efficient memory handling, we'll try to do so. */
2542 apr_finfo_t finfo = { 0 };
2544 /* In some cases we get size 0 and no error for non files,
2545 so we also check for the name. (= cached in apr_file_t) */
2546 if (! apr_file_info_get(&finfo, APR_FINFO_SIZE, file) && finfo.fname)
2548 /* we've got the file length. Now, read it in one go. */
2550 res_initial_len = (apr_size_t)finfo.size;
2551 res = svn_stringbuf_create_ensure(res_initial_len, pool);
2552 SVN_ERR(svn_io_file_read_full2(file, res->data,
2553 res_initial_len, &res->len,
2555 res->data[res->len] = 0;
2558 return SVN_NO_ERROR;
2562 /* XXX: We should check the incoming data for being of type binary. */
2563 buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
2564 res = svn_stringbuf_create_ensure(res_initial_len, pool);
2566 /* apr_file_read will not return data and eof in the same call. So this loop
2567 * is safe from missing read data. */
2568 len = SVN__STREAM_CHUNK_SIZE;
2569 err = svn_io_file_read(file, buf, &len, pool);
2572 svn_stringbuf_appendbytes(res, buf, len);
2573 len = SVN__STREAM_CHUNK_SIZE;
2574 err = svn_io_file_read(file, buf, &len, pool);
2577 /* Having read all the data we *expect* EOF */
2578 if (err && !APR_STATUS_IS_EOF(err->apr_err))
2579 return svn_error_trace(err);
2580 svn_error_clear(err);
2583 return SVN_NO_ERROR;
2587 svn_stringbuf_from_file2(svn_stringbuf_t **result,
2588 const char *filename,
2593 if (filename[0] == '-' && filename[1] == '\0')
2595 apr_status_t apr_err;
2596 if ((apr_err = apr_file_open_stdin(&f, pool)))
2597 return svn_error_wrap_apr(apr_err, _("Can't open stdin"));
2598 SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool));
2602 SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool));
2603 SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool));
2605 return svn_io_file_close(f, pool);
2610 svn_stringbuf_from_file(svn_stringbuf_t **result,
2611 const char *filename,
2614 if (filename[0] == '-' && filename[1] == '\0')
2615 return svn_error_create
2616 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2617 _("Reading from stdin is disallowed"));
2618 return svn_stringbuf_from_file2(result, filename, pool);
2622 svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
2626 return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
2634 svn_io_remove_file2(const char *path,
2635 svn_boolean_t ignore_enoent,
2636 apr_pool_t *scratch_pool)
2638 apr_status_t apr_err;
2639 const char *path_apr;
2641 SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
2643 apr_err = apr_file_remove(path_apr, scratch_pool);
2646 /* If the target is read only NTFS reports EACCESS and FAT/FAT32
2648 if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
2650 /* Set the destination file writable because Windows will not
2651 allow us to delete when path is read-only */
2652 SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool));
2653 apr_err = apr_file_remove(path_apr, scratch_pool);
2656 /* Check to make sure we aren't trying to delete a directory */
2657 if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED)
2658 || apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
2662 if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
2663 && finfo.filetype == APR_REG)
2665 WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr, scratch_pool));
2669 /* Just return the delete error */
2674 return SVN_NO_ERROR;
2676 else if (ignore_enoent && (APR_STATUS_IS_ENOENT(apr_err)
2677 || SVN__APR_STATUS_IS_ENOTDIR(apr_err)))
2679 return SVN_NO_ERROR;
2683 return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
2684 svn_dirent_local_style(path, scratch_pool));
2690 svn_io_remove_dir(const char *path, apr_pool_t *pool)
2692 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
2696 Mac OS X has a bug where if you're reading the contents of a
2697 directory via readdir in a loop, and you remove one of the entries in
2698 the directory and the directory has 338 or more files in it you will
2699 skip over some of the entries in the directory. Needless to say,
2700 this causes problems if you are using this kind of loop inside a
2701 function that is recursively deleting a directory, because when you
2702 get around to removing the directory it will still have something in
2703 it. A similar problem has been observed in other BSDs. This bug has
2704 since been fixed. See http://www.vnode.ch/fixing_seekdir for details.
2706 The workaround is to delete the files only _after_ the initial
2707 directory scan. A previous workaround involving rewinddir is
2708 problematic on Win32 and some NFS clients, notably NetBSD.
2710 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
2711 http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
2714 /* Neither windows nor unix allows us to delete a non-empty
2717 This is a function to perform the equivalent of 'rm -rf'. */
2719 svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
2720 svn_cancel_func_t cancel_func, void *cancel_baton,
2724 apr_pool_t *subpool;
2725 apr_hash_t *dirents;
2726 apr_hash_index_t *hi;
2728 /* Check for pending cancellation request.
2729 If we need to bail out, do so early. */
2732 SVN_ERR(cancel_func(cancel_baton));
2734 subpool = svn_pool_create(pool);
2736 err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
2739 /* if the directory doesn't exist, our mission is accomplished */
2740 if (ignore_enoent && (APR_STATUS_IS_ENOENT(err->apr_err)
2741 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2743 svn_error_clear(err);
2744 return SVN_NO_ERROR;
2746 return svn_error_trace(err);
2749 for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
2751 const char *name = apr_hash_this_key(hi);
2752 const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
2753 const char *fullpath;
2755 fullpath = svn_dirent_join(path, name, subpool);
2756 if (dirent->kind == svn_node_dir)
2758 /* Don't check for cancellation, the callee will immediately do so */
2759 SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func,
2760 cancel_baton, subpool));
2765 SVN_ERR(cancel_func(cancel_baton));
2767 err = svn_io_remove_file2(fullpath, FALSE, subpool);
2769 return svn_error_createf
2770 (err->apr_err, err, _("Can't remove '%s'"),
2771 svn_dirent_local_style(fullpath, subpool));
2775 svn_pool_destroy(subpool);
2777 return svn_io_dir_remove_nonrecursive(path, pool);
2781 svn_io_get_dir_filenames(apr_hash_t **dirents,
2785 return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
2790 svn_io_dirent2_create(apr_pool_t *result_pool)
2792 svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
2794 /*dirent->kind = svn_node_none;
2795 dirent->special = FALSE;*/
2796 dirent->filesize = SVN_INVALID_FILESIZE;
2797 /*dirent->mtime = 0;*/
2803 svn_io_dirent2_dup(const svn_io_dirent2_t *item,
2804 apr_pool_t *result_pool)
2806 return apr_pmemdup(result_pool,
2812 svn_io_get_dirents3(apr_hash_t **dirents,
2814 svn_boolean_t only_check_type,
2815 apr_pool_t *result_pool,
2816 apr_pool_t *scratch_pool)
2818 apr_status_t status;
2819 apr_dir_t *this_dir;
2820 apr_finfo_t this_entry;
2821 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
2823 if (!only_check_type)
2824 flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
2826 *dirents = apr_hash_make(result_pool);
2828 SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
2830 for (status = apr_dir_read(&this_entry, flags, this_dir);
2831 status == APR_SUCCESS;
2832 status = apr_dir_read(&this_entry, flags, this_dir))
2834 if ((this_entry.name[0] == '.')
2835 && ((this_entry.name[1] == '\0')
2836 || ((this_entry.name[1] == '.')
2837 && (this_entry.name[2] == '\0'))))
2844 svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
2846 SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
2848 map_apr_finfo_to_node_kind(&(dirent->kind),
2852 if (!only_check_type)
2854 dirent->filesize = this_entry.size;
2855 dirent->mtime = this_entry.mtime;
2858 svn_hash_sets(*dirents, name, dirent);
2862 if (! (APR_STATUS_IS_ENOENT(status)))
2863 return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
2864 svn_dirent_local_style(path, scratch_pool));
2866 status = apr_dir_close(this_dir);
2868 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
2869 svn_dirent_local_style(path, scratch_pool));
2871 return SVN_NO_ERROR;
2875 svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
2877 svn_boolean_t verify_truename,
2878 svn_boolean_t ignore_enoent,
2879 apr_pool_t *result_pool,
2880 apr_pool_t *scratch_pool)
2883 svn_io_dirent2_t *dirent;
2885 apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
2886 | APR_FINFO_SIZE | APR_FINFO_MTIME;
2888 #if defined(WIN32) || defined(__OS2__)
2889 if (verify_truename)
2890 wanted |= APR_FINFO_NAME;
2893 err = svn_io_stat(&finfo, path, wanted, scratch_pool);
2895 if (err && ignore_enoent &&
2896 (APR_STATUS_IS_ENOENT(err->apr_err)
2897 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2899 svn_error_clear(err);
2900 dirent = svn_io_dirent2_create(result_pool);
2901 SVN_ERR_ASSERT(dirent->kind == svn_node_none);
2904 return SVN_NO_ERROR;
2908 #if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
2909 if (verify_truename)
2911 const char *requested_name = svn_dirent_basename(path, NULL);
2913 if (requested_name[0] == '\0')
2915 /* No parent directory. No need to stat/verify */
2917 #if defined(WIN32) || defined(__OS2__)
2918 else if (finfo.name)
2920 const char *name_on_disk;
2921 SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
2924 if (strcmp(name_on_disk, requested_name) /* != 0 */)
2928 *dirent_p = svn_io_dirent2_create(result_pool);
2929 return SVN_NO_ERROR;
2932 return svn_error_createf(APR_ENOENT, NULL,
2933 _("Path '%s' not found, case obstructed by '%s'"),
2934 svn_dirent_local_style(path, scratch_pool),
2938 #elif defined(DARWIN)
2939 /* Currently apr doesn't set finfo.name on DARWIN, returning
2941 ### Can we optimize this in another way? */
2944 apr_hash_t *dirents;
2946 err = svn_io_get_dirents3(&dirents,
2947 svn_dirent_dirname(path, scratch_pool),
2948 TRUE /* only_check_type */,
2949 scratch_pool, scratch_pool);
2951 if (err && ignore_enoent
2952 && (APR_STATUS_IS_ENOENT(err->apr_err)
2953 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2955 svn_error_clear(err);
2957 *dirent_p = svn_io_dirent2_create(result_pool);
2958 return SVN_NO_ERROR;
2963 if (! svn_hash_gets(dirents, requested_name))
2967 *dirent_p = svn_io_dirent2_create(result_pool);
2968 return SVN_NO_ERROR;
2971 return svn_error_createf(APR_ENOENT, NULL,
2972 _("Path '%s' not found"),
2973 svn_dirent_local_style(path, scratch_pool));
2980 dirent = svn_io_dirent2_create(result_pool);
2981 map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
2983 dirent->filesize = finfo.size;
2984 dirent->mtime = finfo.mtime;
2988 return SVN_NO_ERROR;
2991 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2992 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2994 /* Handle an error from the child process (before command execution) by
2995 printing DESC and the error string corresponding to STATUS to stderr. */
2997 handle_child_process_error(apr_pool_t *pool, apr_status_t status,
3001 apr_file_t *errfile;
3004 /* We can't do anything if we get an error here, so just return. */
3005 if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool))
3010 /* What we get from APR is in native encoding. */
3011 apr_file_printf(errfile, "%s: %s",
3012 desc, apr_strerror(status, errbuf,
3018 svn_io_start_cmd3(apr_proc_t *cmd_proc,
3021 const char *const *args,
3022 const char *const *env,
3023 svn_boolean_t inherit,
3024 svn_boolean_t infile_pipe,
3026 svn_boolean_t outfile_pipe,
3027 apr_file_t *outfile,
3028 svn_boolean_t errfile_pipe,
3029 apr_file_t *errfile,
3032 apr_status_t apr_err;
3033 apr_procattr_t *cmdproc_attr;
3035 const char **args_native;
3036 const char *cmd_apr;
3038 SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
3039 SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
3040 SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
3042 /* Create the process attributes. */
3043 apr_err = apr_procattr_create(&cmdproc_attr, pool);
3045 return svn_error_wrap_apr(apr_err,
3046 _("Can't create process '%s' attributes"),
3049 /* Make sure we invoke cmd directly, not through a shell. */
3050 apr_err = apr_procattr_cmdtype_set(cmdproc_attr,
3051 inherit ? APR_PROGRAM_PATH : APR_PROGRAM);
3053 return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
3056 /* Set the process's working directory. */
3059 const char *path_apr;
3061 /* APR doesn't like our canonical path format for current directory */
3062 if (path[0] == '\0')
3065 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
3066 apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
3068 return svn_error_wrap_apr(apr_err,
3069 _("Can't set process '%s' directory"),
3073 /* Use requested inputs and outputs.
3075 ### Unfortunately each of these apr functions creates a pipe and then
3076 overwrites the pipe file descriptor with the descriptor we pass
3077 in. The pipes can then never be closed. This is an APR bug. */
3080 apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
3082 return svn_error_wrap_apr(apr_err,
3083 _("Can't set process '%s' child input"),
3088 apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
3090 return svn_error_wrap_apr(apr_err,
3091 _("Can't set process '%s' child outfile"),
3096 apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
3098 return svn_error_wrap_apr(apr_err,
3099 _("Can't set process '%s' child errfile"),
3103 /* Forward request for pipes to APR. */
3104 if (infile_pipe || outfile_pipe || errfile_pipe)
3106 apr_err = apr_procattr_io_set(cmdproc_attr,
3107 infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
3108 outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
3109 errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE);
3112 return svn_error_wrap_apr(apr_err,
3113 _("Can't set process '%s' stdio pipes"),
3117 /* Have the child print any problems executing its program to errfile. */
3118 apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
3120 return svn_error_wrap_apr(apr_err,
3121 _("Can't set process '%s' child errfile for "
3124 apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
3125 handle_child_process_error);
3127 return svn_error_wrap_apr(apr_err,
3128 _("Can't set process '%s' error handler"),
3131 /* Convert cmd and args from UTF-8 */
3132 SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool));
3133 for (num_args = 0; args[num_args]; num_args++)
3135 args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
3136 args_native[num_args] = NULL;
3139 /* ### Well, it turns out that on APR on Windows expects all
3140 program args to be in UTF-8. Callers of svn_io_run_cmd
3141 should be aware of that. */
3142 SVN_ERR(cstring_from_utf8(&args_native[num_args],
3143 args[num_args], pool));
3147 /* Start the cmd command. */
3148 apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
3149 inherit ? NULL : env, cmdproc_attr, pool);
3151 return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
3153 return SVN_NO_ERROR;
3159 svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
3162 apr_exit_why_e *exitwhy,
3165 apr_status_t apr_err;
3166 apr_exit_why_e exitwhy_val;
3169 /* The Win32 apr_proc_wait doesn't set this... */
3170 exitwhy_val = APR_PROC_EXIT;
3172 /* Wait for the cmd command to finish. */
3173 apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT);
3174 if (!APR_STATUS_IS_CHILD_DONE(apr_err))
3175 return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"),
3179 *exitwhy = exitwhy_val;
3180 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)
3181 && APR_PROC_CHECK_CORE_DUMP(exitwhy_val))
3182 return svn_error_createf
3183 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3184 _("Process '%s' failed (signal %d, core dumped)"),
3186 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val))
3187 return svn_error_createf
3188 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3189 _("Process '%s' failed (signal %d)"),
3191 else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
3192 /* Don't really know what happened here. */
3193 return svn_error_createf
3194 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3195 _("Process '%s' failed (exitwhy %d, exitcode %d)"),
3196 cmd, exitwhy_val, exitcode_val);
3199 *exitcode = exitcode_val;
3200 else if (exitcode_val != 0)
3201 return svn_error_createf
3202 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3203 _("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
3205 return SVN_NO_ERROR;
3210 svn_io_run_cmd(const char *path,
3212 const char *const *args,
3214 apr_exit_why_e *exitwhy,
3215 svn_boolean_t inherit,
3217 apr_file_t *outfile,
3218 apr_file_t *errfile,
3221 apr_proc_t cmd_proc;
3223 SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
3224 FALSE, infile, FALSE, outfile, FALSE, errfile,
3227 return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
3232 svn_io_run_diff2(const char *dir,
3233 const char *const *user_args,
3240 apr_file_t *outfile,
3241 apr_file_t *errfile,
3242 const char *diff_cmd,
3248 int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
3249 apr_pool_t *subpool = svn_pool_create(pool);
3251 if (pexitcode == NULL)
3252 pexitcode = &exitcode;
3254 if (user_args != NULL)
3255 nargs += num_user_args;
3257 nargs += 1; /* -u */
3260 nargs += 2; /* the -L and the label itself */
3262 nargs += 2; /* the -L and the label itself */
3264 args = apr_palloc(subpool, nargs * sizeof(char *));
3267 args[i++] = diff_cmd;
3269 if (user_args != NULL)
3272 for (j = 0; j < num_user_args; ++j)
3273 args[i++] = user_args[j];
3276 args[i++] = "-u"; /* assume -u if the user didn't give us any args */
3289 args[i++] = svn_dirent_local_style(from, subpool);
3290 args[i++] = svn_dirent_local_style(to, subpool);
3293 SVN_ERR_ASSERT(i == nargs);
3295 SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
3296 NULL, outfile, errfile, subpool));
3298 /* The man page for (GNU) diff describes the return value as:
3300 "An exit status of 0 means no differences were found, 1 means
3301 some differences were found, and 2 means trouble."
3303 A return value of 2 typically occurs when diff cannot read its input
3304 or write to its output, but in any case we probably ought to return an
3305 error for anything other than 0 or 1 as the output is likely to be
3308 if (*pexitcode != 0 && *pexitcode != 1)
3309 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3310 _("'%s' returned %d"),
3311 svn_dirent_local_style(diff_cmd, pool),
3314 svn_pool_destroy(subpool);
3316 return SVN_NO_ERROR;
3321 svn_io_run_diff3_3(int *exitcode,
3326 const char *mine_label,
3327 const char *older_label,
3328 const char *yours_label,
3330 const char *diff3_cmd,
3331 const apr_array_header_t *user_args,
3334 const char **args = apr_palloc(pool,
3344 /* Labels fall back to sensible defaults if not specified. */
3345 if (mine_label == NULL)
3346 mine_label = ".working";
3347 if (older_label == NULL)
3348 older_label = ".old";
3349 if (yours_label == NULL)
3350 yours_label = ".new";
3352 /* Set up diff3 command line. */
3353 args[i++] = diff3_cmd;
3357 for (j = 0; j < user_args->nelts; ++j)
3358 args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
3360 nargs += user_args->nelts;
3365 args[i++] = "-E"; /* We tried "-A" here, but that caused
3366 overlapping identical changes to
3367 conflict. See issue #682. */
3374 args[i++] = mine_label;
3376 args[i++] = older_label; /* note: this label is ignored if
3377 using 2-part markers, which is the
3380 args[i++] = yours_label;
3381 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
3383 svn_boolean_t has_arg;
3385 /* ### FIXME: we really shouldn't be reading the config here;
3386 instead, the necessary bits should be passed in by the caller.
3387 But should we add another parameter to this function, when the
3388 whole external diff3 thing might eventually go away? */
3392 SVN_ERR(svn_config_get_config(&config, pool));
3393 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
3394 SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS,
3395 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG,
3399 const char *diff_cmd, *diff_utf8;
3400 svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
3401 SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF);
3402 SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool));
3403 args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8,
3411 args[i++] = svn_dirent_local_style(mine, pool);
3412 args[i++] = svn_dirent_local_style(older, pool);
3413 args[i++] = svn_dirent_local_style(yours, pool);
3416 SVN_ERR_ASSERT(i == nargs);
3419 /* Run diff3, output the merged text into the scratch file. */
3420 SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
3422 TRUE, /* keep environment */
3426 /* According to the diff3 docs, a '0' means the merge was clean, and
3427 '1' means conflict markers were found. Anything else is real
3429 if ((*exitcode != 0) && (*exitcode != 1))
3430 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3431 _("Error running '%s': exitcode was %d, "
3433 "\nin directory '%s', basenames:\n%s\n%s\n%s"),
3434 svn_dirent_local_style(diff3_cmd, pool),
3436 svn_dirent_local_style(dir, pool),
3437 /* Don't call svn_path_local_style() on
3438 the basenames. We don't want them to
3439 be absolute, and we don't need the
3440 separator conversion. */
3441 mine, older, yours);
3443 return SVN_NO_ERROR;
3447 /* Canonicalize a string for hashing. Modifies KEY in place. */
3448 static APR_INLINE char *
3449 fileext_tolower(char *key)
3452 for (p = key; *p != 0; ++p)
3453 *p = (char)apr_tolower(*p);
3459 svn_io_parse_mimetypes_file(apr_hash_t **type_map,
3460 const char *mimetypes_file,
3463 svn_error_t *err = SVN_NO_ERROR;
3464 apr_hash_t *types = apr_hash_make(pool);
3465 svn_boolean_t eof = FALSE;
3466 svn_stringbuf_t *buf;
3467 apr_pool_t *subpool = svn_pool_create(pool);
3468 apr_file_t *types_file;
3469 svn_stream_t *mimetypes_stream;
3471 SVN_ERR(svn_io_file_open(&types_file, mimetypes_file,
3472 APR_READ, APR_OS_DEFAULT, pool));
3473 mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool);
3477 apr_array_header_t *tokens;
3480 svn_pool_clear(subpool);
3483 if ((err = svn_stream_readline(mimetypes_stream, &buf,
3484 APR_EOL_STR, &eof, subpool)))
3487 /* Only pay attention to non-empty, non-comment lines. */
3492 if (buf->data[0] == '#')
3495 /* Tokenize (into our return pool). */
3496 tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
3497 if (tokens->nelts < 2)
3500 /* The first token in a multi-token line is the media type.
3501 Subsequent tokens are filename extensions associated with
3503 type = APR_ARRAY_IDX(tokens, 0, const char *);
3504 for (i = 1; i < tokens->nelts; i++)
3506 /* We can safely address 'ext' as a non-const string because
3507 * we know svn_cstring_split() allocated it in 'pool' for us. */
3508 char *ext = APR_ARRAY_IDX(tokens, i, char *);
3509 fileext_tolower(ext);
3510 svn_hash_sets(types, ext, type);
3516 svn_pool_destroy(subpool);
3518 /* If there was an error above, close the file (ignoring any error
3519 from *that*) and return the originally error. */
3522 svn_error_clear(svn_stream_close(mimetypes_stream));
3526 /* Close the stream (which closes the underlying file, too). */
3527 SVN_ERR(svn_stream_close(mimetypes_stream));
3530 return SVN_NO_ERROR;
3535 svn_io_detect_mimetype2(const char **mimetype,
3537 apr_hash_t *mimetype_map,
3540 static const char * const generic_binary = "application/octet-stream";
3542 svn_node_kind_t kind;
3545 unsigned char block[1024];
3546 apr_size_t amt_read = sizeof(block);
3548 /* Default return value is NULL. */
3551 /* If there is a mimetype_map provided, we'll first try to look up
3552 our file's extension in the map. Failing that, we'll run the
3556 const char *type_from_map;
3557 char *path_ext; /* Can point to physical const memory but only when
3558 svn_path_splitext sets it to "". */
3559 svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
3560 fileext_tolower(path_ext);
3561 if ((type_from_map = svn_hash_gets(mimetype_map, path_ext)))
3563 *mimetype = type_from_map;
3564 return SVN_NO_ERROR;
3568 /* See if this file even exists, and make sure it really is a file. */
3569 SVN_ERR(svn_io_check_path(file, &kind, pool));
3570 if (kind != svn_node_file)
3571 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
3572 _("Can't detect MIME type of non-file '%s'"),
3573 svn_dirent_local_style(file, pool));
3575 SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
3577 /* Read a block of data from FILE. */
3578 err = svn_io_file_read(fh, block, &amt_read, pool);
3579 if (err && ! APR_STATUS_IS_EOF(err->apr_err))
3581 svn_error_clear(err);
3583 /* Now close the file. No use keeping it open any more. */
3584 SVN_ERR(svn_io_file_close(fh, pool));
3586 if (svn_io_is_binary_data(block, amt_read))
3587 *mimetype = generic_binary;
3589 return SVN_NO_ERROR;
3594 svn_io_is_binary_data(const void *data, apr_size_t len)
3596 const unsigned char *buf = data;
3598 if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
3600 /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
3601 * Treat it as plain text. */
3605 /* Right now, this function is going to be really stupid. It's
3606 going to examine the block of data, and make sure that 15%
3607 of the bytes are such that their value is in the ranges 0x07-0x0D
3608 or 0x20-0x7F, and that none of those bytes is 0x00. If those
3609 criteria are not met, we're calling it binary.
3611 NOTE: Originally, I intended to target 85% of the bytes being in
3612 the specified ranges, but I flubbed the condition. At any rate,
3613 folks aren't complaining, so I'm not sure that it's worth
3614 adjusting this retroactively now. --cmpilato */
3618 apr_size_t binary_count = 0;
3620 /* Run through the data we've read, counting the 'binary-ish'
3621 bytes. HINT: If we see a 0x00 byte, we'll set our count to its
3622 max and stop reading the file. */
3623 for (i = 0; i < len; i++)
3631 || ((buf[i] > 0x0D) && (buf[i] < 0x20))
3638 return (((binary_count * 1000) / len) > 850);
3646 svn_io_detect_mimetype(const char **mimetype,
3650 return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
3655 svn_io_file_open(apr_file_t **new_file, const char *fname,
3656 apr_int32_t flag, apr_fileperms_t perm,
3659 const char *fname_apr;
3660 apr_status_t status;
3662 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3663 status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
3667 return svn_error_wrap_apr(status, _("Can't open file '%s'"),
3668 svn_dirent_local_style(fname, pool));
3670 return SVN_NO_ERROR;
3674 static APR_INLINE svn_error_t *
3675 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
3676 const char *msg, const char *msg_no_name,
3683 return SVN_NO_ERROR;
3685 err = svn_io_file_name_get(&name, file, pool);
3688 svn_error_clear(err);
3690 /* ### Issue #3014: Return a specific error for broken pipes,
3691 * ### with a single element in the error chain. */
3692 if (SVN__APR_STATUS_IS_EPIPE(status))
3693 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
3696 return svn_error_wrap_apr(status, _(msg),
3697 try_utf8_from_internal_style(name, pool));
3699 return svn_error_wrap_apr(status, "%s", _(msg_no_name));
3704 svn_io_file_close(apr_file_t *file, apr_pool_t *pool)
3706 return do_io_file_wrapper_cleanup(file, apr_file_close(file),
3707 N_("Can't close file '%s'"),
3708 N_("Can't close stream"),
3714 svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
3716 return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file),
3717 N_("Can't read file '%s'"),
3718 N_("Can't read stream"),
3724 svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
3726 return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file),
3727 N_("Can't write file '%s'"),
3728 N_("Can't write stream"),
3734 svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
3735 apr_file_t *file, apr_pool_t *pool)
3737 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3738 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3740 return do_io_file_wrapper_cleanup(
3741 file, apr_file_info_get(finfo, wanted, file),
3742 N_("Can't get attribute information from file '%s'"),
3743 N_("Can't get attribute information from stream"),
3748 svn_io_file_size_get(svn_filesize_t *filesize_p, apr_file_t *file,
3752 SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, file, pool));
3754 *filesize_p = finfo.size;
3755 return SVN_NO_ERROR;
3759 svn_io_file_get_offset(apr_off_t *offset_p,
3765 /* Note that, for buffered files, one (possibly surprising) side-effect
3766 of this call is to flush any unwritten data to disk. */
3768 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
3771 return SVN_NO_ERROR;
3775 svn_io_file_read(apr_file_t *file, void *buf,
3776 apr_size_t *nbytes, apr_pool_t *pool)
3778 return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes),
3779 N_("Can't read file '%s'"),
3780 N_("Can't read stream"),
3786 svn_io_file_read_full2(apr_file_t *file, void *buf,
3787 apr_size_t nbytes, apr_size_t *bytes_read,
3788 svn_boolean_t *hit_eof,
3791 apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
3794 if (APR_STATUS_IS_EOF(status))
3797 return SVN_NO_ERROR;
3803 return do_io_file_wrapper_cleanup(file, status,
3804 N_("Can't read file '%s'"),
3805 N_("Can't read stream"),
3811 svn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
3812 apr_off_t *offset, apr_pool_t *pool)
3814 return do_io_file_wrapper_cleanup(
3815 file, apr_file_seek(file, where, offset),
3816 N_("Can't set position pointer in file '%s'"),
3817 N_("Can't set position pointer in stream"),
3822 svn_io_file_aligned_seek(apr_file_t *file,
3823 apr_off_t block_size,
3824 apr_off_t *buffer_start,
3826 apr_pool_t *scratch_pool)
3828 const apr_size_t apr_default_buffer_size = 4096;
3829 apr_size_t file_buffer_size = apr_default_buffer_size;
3830 apr_off_t desired_offset = 0;
3831 apr_off_t current = 0;
3832 apr_off_t aligned_offset = 0;
3833 svn_boolean_t fill_buffer = FALSE;
3835 /* paranoia check: huge blocks on 32 bit machines may cause overflows */
3836 SVN_ERR_ASSERT(block_size == (apr_size_t)block_size);
3838 /* default for invalid block sizes */
3839 if (block_size == 0)
3840 block_size = apr_default_buffer_size;
3842 file_buffer_size = apr_file_buffer_size_get(file);
3844 /* don't try to set a buffer size for non-buffered files! */
3845 if (file_buffer_size == 0)
3847 aligned_offset = offset;
3849 else if (file_buffer_size != (apr_size_t)block_size)
3851 /* FILE has the wrong buffer size. correct it */
3853 file_buffer_size = (apr_size_t)block_size;
3854 buffer = apr_palloc(apr_file_pool_get(file), file_buffer_size);
3855 apr_file_buffer_set(file, buffer, file_buffer_size);
3857 /* seek to the start of the block and cause APR to read 1 block */
3858 aligned_offset = offset - (offset % block_size);
3863 aligned_offset = offset - (offset % file_buffer_size);
3865 /* We have no way to determine the block start of an APR file.
3866 Furthermore, we don't want to throw away the current buffer
3867 contents. Thus, we re-align the buffer only if the CURRENT
3868 offset definitely lies outside the desired, aligned buffer.
3869 This covers the typical case of linear reads getting very
3870 close to OFFSET but reading the previous / following block.
3872 Note that ALIGNED_OFFSET may still be within the current
3873 buffer and no I/O will actually happen in the FILL_BUFFER
3876 SVN_ERR(svn_io_file_seek(file, APR_CUR, ¤t, scratch_pool));
3877 fill_buffer = aligned_offset + file_buffer_size <= current
3878 || current <= aligned_offset;
3884 apr_status_t status;
3886 /* seek to the start of the block and cause APR to read 1 block */
3887 SVN_ERR(svn_io_file_seek(file, APR_SET, &aligned_offset,
3889 status = apr_file_getc(&dummy, file);
3891 /* read may fail if we seek to or behind EOF. That's ok then. */
3892 if (status != APR_SUCCESS && !APR_STATUS_IS_EOF(status))
3893 return do_io_file_wrapper_cleanup(file, status,
3894 N_("Can't read file '%s'"),
3895 N_("Can't read stream"),
3899 /* finally, seek to the OFFSET the caller wants */
3900 desired_offset = offset;
3901 SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, scratch_pool));
3902 if (desired_offset != offset)
3903 return do_io_file_wrapper_cleanup(file, APR_EOF,
3904 N_("Can't seek in file '%s'"),
3905 N_("Can't seek in stream"),
3908 /* return the buffer start that we (probably) enforced */
3910 *buffer_start = aligned_offset;
3912 return SVN_NO_ERROR;
3917 svn_io_file_write(apr_file_t *file, const void *buf,
3918 apr_size_t *nbytes, apr_pool_t *pool)
3920 return svn_error_trace(do_io_file_wrapper_cleanup(
3921 file, apr_file_write(file, buf, nbytes),
3922 N_("Can't write to file '%s'"),
3923 N_("Can't write to stream"),
3928 svn_io_file_flush(apr_file_t *file,
3929 apr_pool_t *scratch_pool)
3931 return svn_error_trace(do_io_file_wrapper_cleanup(
3932 file, apr_file_flush(file),
3933 N_("Can't flush file '%s'"),
3934 N_("Can't flush stream"),
3939 svn_io_file_write_full(apr_file_t *file, const void *buf,
3940 apr_size_t nbytes, apr_size_t *bytes_written,
3944 #define MAXBUFSIZE 30*1024
3945 apr_size_t bw = nbytes;
3946 apr_size_t to_write = nbytes;
3949 rv = apr_file_write_full(file, buf, nbytes, &bw);
3950 buf = (char *)buf + bw;
3953 /* Issue #1789: On Windows, writing may fail for large values of NBYTES.
3954 If that is the case, keep track of how many bytes have been written
3955 by the apr_file_write_full() call, and attempt to write the remaining
3956 part in smaller chunks. */
3957 if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
3958 && nbytes > MAXBUFSIZE)
3961 bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
3962 rv = apr_file_write(file, buf, &bw);
3963 buf = (char *)buf + bw;
3965 } while (rv == APR_SUCCESS && to_write > 0);
3968 /* bytes_written may actually be NULL */
3970 *bytes_written = nbytes - to_write;
3973 apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
3976 return svn_error_trace(do_io_file_wrapper_cleanup(
3978 N_("Can't write to file '%s'"),
3979 N_("Can't write to stream"),
3985 svn_io_write_unique(const char **tmp_path,
3986 const char *dirpath,
3989 svn_io_file_del_t delete_when,
3992 apr_file_t *new_file;
3995 SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
3996 delete_when, pool, pool));
3998 err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
4002 /* svn_io_file_flush_to_disk() can be very expensive, so use the
4003 cheaper standard flush if the file is created as temporary file
4005 if (delete_when == svn_io_file_del_none)
4006 err = svn_io_file_flush_to_disk(new_file, pool);
4008 err = svn_io_file_flush(new_file, pool);
4011 return svn_error_trace(
4012 svn_error_compose_create(err,
4013 svn_io_file_close(new_file, pool)));
4017 svn_io_write_atomic2(const char *final_path,
4020 const char *copy_perms_path,
4021 svn_boolean_t flush_to_disk,
4022 apr_pool_t *scratch_pool)
4024 apr_file_t *tmp_file;
4025 const char *tmp_path;
4027 const char *dirname = svn_dirent_dirname(final_path, scratch_pool);
4029 SVN_ERR(svn_io_open_unique_file3(&tmp_file, &tmp_path, dirname,
4030 svn_io_file_del_none,
4031 scratch_pool, scratch_pool));
4033 err = svn_io_file_write_full(tmp_file, buf, nbytes, NULL, scratch_pool);
4035 if (!err && flush_to_disk)
4036 err = svn_io_file_flush_to_disk(tmp_file, scratch_pool);
4038 err = svn_error_compose_create(err,
4039 svn_io_file_close(tmp_file, scratch_pool));
4041 if (!err && copy_perms_path)
4042 err = svn_io_copy_perms(copy_perms_path, tmp_path, scratch_pool);
4045 err = svn_io_file_rename2(tmp_path, final_path, flush_to_disk,
4050 err = svn_error_compose_create(err,
4051 svn_io_remove_file2(tmp_path, TRUE,
4054 return svn_error_createf(err->apr_err, err,
4055 _("Can't write '%s' atomically"),
4056 svn_dirent_local_style(final_path,
4060 return SVN_NO_ERROR;
4064 svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
4066 /* Workaround for yet another APR issue with trunc.
4068 If the APR file internally is in read mode, the current buffer pointer
4069 will not be clipped to the valid data range. get_file_offset may then
4070 return an invalid position *after* new data was written to it.
4072 To prevent this, write 1 dummy byte just after the OFFSET at which we
4073 will trunc it. That will force the APR file into write mode
4074 internally and the flush() work-around below becomes effective. */
4075 apr_off_t position = 0;
4077 /* A frequent usage is OFFSET==0, in which case we don't need to preserve
4078 any file content or file pointer. */
4081 SVN_ERR(svn_io_file_seek(file, APR_CUR, &position, pool));
4082 SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool));
4084 SVN_ERR(svn_io_file_putc(0, file, pool));
4086 /* This is a work-around. APR would flush the write buffer
4087 _after_ truncating the file causing now invalid buffered
4088 data to be written behind OFFSET. */
4089 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
4090 N_("Can't flush file '%s'"),
4091 N_("Can't flush stream"),
4094 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset),
4095 N_("Can't truncate file '%s'"),
4096 N_("Can't truncate stream"),
4099 /* Restore original file pointer, if necessary.
4100 It's currently at OFFSET. */
4101 if (position < offset)
4102 SVN_ERR(svn_io_file_seek(file, APR_SET, &position, pool));
4104 return SVN_NO_ERROR;
4109 svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
4113 apr_size_t total_read = 0;
4114 svn_boolean_t eof = FALSE;
4117 apr_size_t buf_size = *limit;
4119 while (buf_size > 0)
4121 /* read a fair chunk of data at once. But don't get too ambitious
4122 * as that would result in too much waste. Also make sure we can
4123 * put a NUL after the last byte read.
4125 apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
4126 apr_size_t bytes_read = 0;
4132 /* read data block (or just a part of it) */
4133 SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
4134 &bytes_read, &eof, pool));
4136 /* look or a newline char */
4137 buf[bytes_read] = 0;
4138 eol = strchr(buf, '\n');
4141 apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
4144 *limit = total_read + (eol - buf);
4146 /* correct the file pointer:
4147 * appear as though we just had read the newline char
4149 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
4151 return SVN_NO_ERROR;
4155 /* no EOL found but we hit the end of the file.
4156 * Generate a nice EOF error object and return it.
4159 SVN_ERR(svn_io_file_getc(&dummy, file, pool));
4162 /* next data chunk */
4163 buf_size -= bytes_read;
4165 total_read += bytes_read;
4168 /* buffer limit has been exceeded without finding the EOL */
4169 err = svn_io_file_name_get(&name, file, pool);
4172 svn_error_clear(err);
4175 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
4176 _("Can't read length line in file '%s'"),
4177 svn_dirent_local_style(name, pool));
4179 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
4180 _("Can't read length line in stream"));
4185 svn_io_stat(apr_finfo_t *finfo, const char *fname,
4186 apr_int32_t wanted, apr_pool_t *pool)
4188 apr_status_t status;
4189 const char *fname_apr;
4191 /* APR doesn't like "" directories */
4192 if (fname[0] == '\0')
4195 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
4197 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
4198 wanted &= ~SVN__APR_FINFO_MASK_OUT;
4200 status = apr_stat(finfo, fname_apr, wanted, pool);
4202 return svn_error_wrap_apr(status, _("Can't stat '%s'"),
4203 svn_dirent_local_style(fname, pool));
4205 return SVN_NO_ERROR;
4209 /* Platform specific implementation of apr_file_rename() to workaround
4210 APR problems on Windows. */
4212 win32_file_rename(const WCHAR *from_path_w,
4213 const WCHAR *to_path_w,
4214 svn_boolean_t flush_to_disk)
4216 /* APR calls MoveFileExW() with MOVEFILE_COPY_ALLOWED, while we rely
4217 * that rename is atomic operation. Call MoveFileEx directly on Windows
4218 * without MOVEFILE_COPY_ALLOWED flag to workaround it.
4221 DWORD flags = MOVEFILE_REPLACE_EXISTING;
4225 /* Do not return until the file has actually been moved on the disk. */
4226 flags |= MOVEFILE_WRITE_THROUGH;
4229 if (!MoveFileExW(from_path_w, to_path_w, flags))
4230 return apr_get_os_error();
4237 svn_io_file_rename2(const char *from_path, const char *to_path,
4238 svn_boolean_t flush_to_disk, apr_pool_t *pool)
4240 apr_status_t status = APR_SUCCESS;
4241 const char *from_path_apr, *to_path_apr;
4247 SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
4248 SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
4251 SVN_ERR(svn_io__utf8_to_unicode_longpath(&from_path_w, from_path_apr, pool));
4252 SVN_ERR(svn_io__utf8_to_unicode_longpath(&to_path_w, to_path_apr, pool));
4253 status = win32_file_rename(from_path_w, to_path_w, flush_to_disk);
4255 /* If the target file is read only NTFS reports EACCESS and
4256 FAT/FAT32 reports EEXIST */
4257 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
4259 /* Set the destination file writable because Windows will not
4260 allow us to rename when to_path is read-only, but will
4261 allow renaming when from_path is read only. */
4262 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
4264 status = win32_file_rename(from_path_w, to_path_w, flush_to_disk);
4266 WIN32_RETRY_LOOP(status, win32_file_rename(from_path_w, to_path_w,
4268 #elif defined(__OS2__)
4269 status = apr_file_rename(from_path_apr, to_path_apr, pool);
4270 /* If the target file is read only NTFS reports EACCESS and
4271 FAT/FAT32 reports EEXIST */
4272 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
4274 /* Set the destination file writable because OS/2 will not
4275 allow us to rename when to_path is read-only, but will
4276 allow renaming when from_path is read only. */
4277 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
4279 status = apr_file_rename(from_path_apr, to_path_apr, pool);
4282 status = apr_file_rename(from_path_apr, to_path_apr, pool);
4283 #endif /* WIN32 || __OS2__ */
4286 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
4287 svn_dirent_local_style(from_path, pool),
4288 svn_dirent_local_style(to_path, pool));
4290 #if defined(SVN_ON_POSIX)
4293 /* On POSIX, the file name is stored in the file's directory entry.
4294 Hence, we need to fsync() that directory as well.
4295 On other operating systems, we'd only be asking for trouble
4296 by trying to open and fsync a directory. */
4297 const char *dirname;
4300 dirname = svn_dirent_dirname(to_path, pool);
4301 SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
4303 SVN_ERR(svn_io_file_flush_to_disk(file, pool));
4304 SVN_ERR(svn_io_file_close(file, pool));
4306 #elif !defined(WIN32)
4307 /* Flush the target of the rename to disk. */
4311 SVN_ERR(svn_io_file_open(&file, to_path, APR_WRITE,
4312 APR_OS_DEFAULT, pool));
4313 SVN_ERR(svn_io_file_flush_to_disk(file, pool));
4314 SVN_ERR(svn_io_file_close(file, pool));
4318 return SVN_NO_ERROR;
4323 svn_io_file_move(const char *from_path, const char *to_path,
4326 svn_error_t *err = svn_error_trace(svn_io_file_rename2(from_path, to_path,
4329 if (err && APR_STATUS_IS_EXDEV(err->apr_err))
4331 svn_error_clear(err);
4333 /* svn_io_copy_file() performs atomic copy via temporary file. */
4334 err = svn_error_trace(svn_io_copy_file(from_path, to_path, TRUE,
4341 /* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
4342 HIDDEN determines if the hidden attribute
4343 should be set on the newly created directory. */
4344 static svn_error_t *
4345 dir_make(const char *path, apr_fileperms_t perm,
4346 svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
4348 apr_status_t status;
4349 const char *path_apr;
4351 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4353 /* APR doesn't like "" directories */
4354 if (path_apr[0] == '\0')
4357 #if (APR_OS_DEFAULT & APR_WSTICKY)
4358 /* The APR shipped with httpd 2.0.50 contains a bug where
4359 APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
4360 There is a special case for file creation, but not directory
4361 creation, so directories wind up getting created with the sticky
4362 bit set. (There is no such thing as a setuid directory, and the
4363 setgid bit is apparently ignored at mkdir() time.) If we detect
4364 this problem, work around it by unsetting those bits if we are
4365 passed APR_OS_DEFAULT. */
4366 if (perm == APR_OS_DEFAULT)
4367 perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY);
4370 status = apr_dir_make(path_apr, perm, pool);
4373 /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a
4375 if (status == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
4376 WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
4380 return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
4381 svn_dirent_local_style(path, pool));
4383 #ifdef APR_FILE_ATTR_HIDDEN
4387 status = apr_file_attrs_set(path_apr,
4388 APR_FILE_ATTR_HIDDEN,
4389 APR_FILE_ATTR_HIDDEN,
4392 return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
4393 svn_dirent_local_style(path, pool));
4395 /* on Windows, use our wrapper so we can also set the
4396 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */
4398 io_win_file_attrs_set(path_apr,
4399 FILE_ATTRIBUTE_HIDDEN |
4400 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
4401 FILE_ATTRIBUTE_HIDDEN |
4402 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
4405 return svn_error_createf(err->apr_err, err,
4406 _("Can't hide directory '%s'"),
4407 svn_dirent_local_style(path, pool));
4410 #endif /* APR_FILE_ATTR_HIDDEN */
4412 /* Windows does not implement sgid. Skip here because retrieving
4413 the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented
4414 to be 'incredibly expensive'. */
4420 /* Per our contract, don't do error-checking. Some filesystems
4421 * don't support the sgid bit, and that's okay. */
4422 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool);
4425 apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
4429 return SVN_NO_ERROR;
4433 svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
4435 return dir_make(path, perm, FALSE, FALSE, pool);
4439 svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
4442 return dir_make(path, perm, TRUE, FALSE, pool);
4446 svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
4449 return dir_make(path, perm, FALSE, TRUE, pool);
4454 svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
4456 apr_status_t status;
4457 const char *dirname_apr;
4459 /* APR doesn't like "" directories */
4460 if (dirname[0] == '\0')
4463 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4465 status = apr_dir_open(new_dir, dirname_apr, pool);
4467 return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
4468 svn_dirent_local_style(dirname, pool));
4470 return SVN_NO_ERROR;
4474 svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
4476 apr_status_t status;
4477 const char *dirname_apr;
4479 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4481 status = apr_dir_remove(dirname_apr, pool);
4485 svn_boolean_t retry = TRUE;
4487 if (status == APR_FROM_OS_ERROR(ERROR_DIR_NOT_EMPTY))
4489 apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
4491 if (APR_STATUS_IS_ENOTEMPTY(empty_status))
4497 WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
4502 return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
4503 svn_dirent_local_style(dirname, pool));
4505 return SVN_NO_ERROR;
4510 svn_io_dir_read(apr_finfo_t *finfo,
4515 apr_status_t status;
4517 status = apr_dir_read(finfo, wanted, thedir);
4520 return svn_error_wrap_apr(status, _("Can't read directory"));
4522 /* It would be nice to use entry_name_to_utf8() below, but can we
4523 get the dir's path out of an apr_dir_t? I don't see a reliable
4527 SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
4530 SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
4532 return SVN_NO_ERROR;
4536 svn_io_dir_close(apr_dir_t *thedir)
4538 apr_status_t apr_err = apr_dir_close(thedir);
4540 return svn_error_wrap_apr(apr_err, _("Error closing directory"));
4542 return SVN_NO_ERROR;
4546 svn_io_dir_walk2(const char *dirname,
4548 svn_io_walk_func_t walk_func,
4552 apr_status_t apr_err;
4554 apr_pool_t *subpool;
4555 const char *dirname_apr;
4558 wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
4560 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
4561 wanted &= ~SVN__APR_FINFO_MASK_OUT;
4563 /* The documentation for apr_dir_read used to state that "." and ".."
4564 will be returned as the first two files, but it doesn't
4565 work that way in practice, in particular ext3 on Linux-2.6 doesn't
4566 follow the rules. For details see
4567 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
4569 If APR ever does implement "dot-first" then it would be possible to
4570 remove the svn_io_stat and walk_func calls and use the walk_func
4573 Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
4574 documented to provide it, so we have to do a bit extra. */
4575 SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool));
4576 SVN_ERR(cstring_from_utf8(&finfo.name,
4577 svn_dirent_basename(dirname, pool),
4579 finfo.valid |= APR_FINFO_NAME;
4580 SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
4582 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4584 /* APR doesn't like "" directories */
4585 if (dirname_apr[0] == '\0')
4588 apr_err = apr_dir_open(&handle, dirname_apr, pool);
4590 return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
4591 svn_dirent_local_style(dirname, pool));
4593 /* iteration subpool */
4594 subpool = svn_pool_create(pool);
4598 const char *name_utf8;
4599 const char *full_path;
4601 svn_pool_clear(subpool);
4603 apr_err = apr_dir_read(&finfo, wanted, handle);
4604 if (APR_STATUS_IS_ENOENT(apr_err))
4608 return svn_error_wrap_apr(apr_err,
4609 _("Can't read directory entry in '%s'"),
4610 svn_dirent_local_style(dirname, pool));
4613 if (finfo.filetype == APR_DIR)
4615 if (finfo.name[0] == '.'
4616 && (finfo.name[1] == '\0'
4617 || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
4618 /* skip "." and ".." */
4621 /* some other directory. recurse. it will be passed to the
4622 callback inside the recursion. */
4623 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
4625 full_path = svn_dirent_join(dirname, name_utf8, subpool);
4626 SVN_ERR(svn_io_dir_walk2(full_path,
4632 else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
4634 /* some other directory. pass it to the callback. */
4635 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
4637 full_path = svn_dirent_join(dirname, name_utf8, subpool);
4638 SVN_ERR((*walk_func)(walk_baton,
4644 Some other type of file; skip it for now. We've reserved the
4645 right to expand our coverage here in the future, though,
4646 without revving this API.
4650 svn_pool_destroy(subpool);
4652 apr_err = apr_dir_close(handle);
4654 return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
4655 svn_dirent_local_style(dirname, pool));
4657 return SVN_NO_ERROR;
4663 * Determine if a directory is empty or not.
4664 * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
4665 * @param path The directory.
4666 * @param pool Used for temporary allocation.
4667 * @remark If path is not a directory, or some other error occurs,
4668 * then return the appropriate apr status code.
4670 * (This function is written in APR style, in anticipation of
4671 * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
4674 dir_is_empty(const char *dir, apr_pool_t *pool)
4676 apr_status_t apr_err;
4677 apr_dir_t *dir_handle;
4679 apr_status_t retval = APR_SUCCESS;
4681 /* APR doesn't like "" directories */
4685 apr_err = apr_dir_open(&dir_handle, dir, pool);
4686 if (apr_err != APR_SUCCESS)
4689 for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle);
4690 apr_err == APR_SUCCESS;
4691 apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle))
4693 /* Ignore entries for this dir and its parent, robustly.
4694 (APR promises that they'll come first, so technically
4695 this guard could be moved outside the loop. But Ryan Bloom
4696 says he doesn't believe it, and I believe him. */
4697 if (! (finfo.name[0] == '.'
4698 && (finfo.name[1] == '\0'
4699 || (finfo.name[1] == '.' && finfo.name[2] == '\0'))))
4701 retval = APR_ENOTEMPTY;
4706 /* Make sure we broke out of the loop for the right reason. */
4707 if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
4710 apr_err = apr_dir_close(dir_handle);
4711 if (apr_err != APR_SUCCESS)
4719 svn_io_dir_empty(svn_boolean_t *is_empty_p,
4723 apr_status_t status;
4724 const char *path_apr;
4726 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4728 status = dir_is_empty(path_apr, pool);
4732 else if (APR_STATUS_IS_ENOTEMPTY(status))
4733 *is_empty_p = FALSE;
4735 return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
4736 svn_dirent_local_style(path, pool));
4738 return SVN_NO_ERROR;
4743 /*** Version/format files ***/
4746 svn_io_write_version_file(const char *path,
4750 const char *path_tmp;
4751 const char *format_contents = apr_psprintf(pool, "%d\n", version);
4753 SVN_ERR_ASSERT(version >= 0);
4755 SVN_ERR(svn_io_write_unique(&path_tmp,
4756 svn_dirent_dirname(path, pool),
4757 format_contents, strlen(format_contents),
4758 svn_io_file_del_none, pool));
4760 #if defined(WIN32) || defined(__OS2__)
4761 /* make the destination writable, but only on Windows, because
4762 Windows does not let us replace read-only files. */
4763 SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
4764 #endif /* WIN32 || __OS2__ */
4766 /* rename the temp file as the real destination */
4767 SVN_ERR(svn_io_file_rename2(path_tmp, path, FALSE, pool));
4769 /* And finally remove the perms to make it read only */
4770 return svn_io_set_file_read_only(path, FALSE, pool);
4775 svn_io_read_version_file(int *version,
4779 apr_file_t *format_file;
4784 /* Read a chunk of data from PATH */
4785 SVN_ERR(svn_io_file_open(&format_file, path, APR_READ,
4786 APR_OS_DEFAULT, pool));
4788 err = svn_io_file_read(format_file, buf, &len, pool);
4790 /* Close the file. */
4791 SVN_ERR(svn_error_compose_create(err,
4792 svn_io_file_close(format_file, pool)));
4794 /* If there was no data in PATH, return an error. */
4796 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
4798 svn_dirent_local_style(path, pool));
4800 /* Check that the first line contains only digits. */
4804 for (i = 0; i < len; ++i)
4808 if (i > 0 && (c == '\r' || c == '\n'))
4813 if (! svn_ctype_isdigit(c))
4814 return svn_error_createf
4815 (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
4816 _("First line of '%s' contains non-digit"),
4817 svn_dirent_local_style(path, pool));
4821 /* Convert to integer. */
4822 SVN_ERR(svn_cstring_atoi(version, buf));
4824 return SVN_NO_ERROR;
4828 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
4829 static svn_error_t *
4830 contents_identical_p(svn_boolean_t *identical_p,
4836 apr_size_t bytes_read1, bytes_read2;
4837 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4838 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4839 apr_file_t *file1_h;
4840 apr_file_t *file2_h;
4841 svn_boolean_t eof1 = FALSE;
4842 svn_boolean_t eof2 = FALSE;
4844 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4847 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4851 return svn_error_trace(
4852 svn_error_compose_create(err,
4853 svn_io_file_close(file1_h, pool)));
4855 *identical_p = TRUE; /* assume TRUE, until disproved below */
4856 while (!err && !eof1 && !eof2)
4858 err = svn_io_file_read_full2(file1_h, buf1,
4859 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4864 err = svn_io_file_read_full2(file2_h, buf2,
4865 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4870 if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
4872 *identical_p = FALSE;
4877 /* Special case: one file being a prefix of the other and the shorter
4878 * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */
4879 if (!err && (eof1 != eof2))
4880 *identical_p = FALSE;
4882 return svn_error_trace(
4883 svn_error_compose_create(
4885 svn_error_compose_create(svn_io_file_close(file1_h, pool),
4886 svn_io_file_close(file2_h, pool))));
4891 /* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
4892 static svn_error_t *
4893 contents_three_identical_p(svn_boolean_t *identical_p12,
4894 svn_boolean_t *identical_p23,
4895 svn_boolean_t *identical_p13,
4899 apr_pool_t *scratch_pool)
4902 char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4903 char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4904 char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4905 apr_file_t *file1_h;
4906 apr_file_t *file2_h;
4907 apr_file_t *file3_h;
4908 svn_boolean_t eof1 = FALSE;
4909 svn_boolean_t eof2 = FALSE;
4910 svn_boolean_t eof3 = FALSE;
4912 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4915 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4919 return svn_error_trace(
4920 svn_error_compose_create(err,
4921 svn_io_file_close(file1_h, scratch_pool)));
4923 err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
4927 return svn_error_trace(
4928 svn_error_compose_create(
4930 svn_error_compose_create(svn_io_file_close(file1_h,
4932 svn_io_file_close(file2_h,
4935 /* assume TRUE, until disproved below */
4936 *identical_p12 = *identical_p23 = *identical_p13 = TRUE;
4937 /* We need to read as long as no error occurs, and as long as one of the
4938 * flags could still change due to a read operation */
4940 && ((*identical_p12 && !eof1 && !eof2)
4941 || (*identical_p23 && !eof2 && !eof3)
4942 || (*identical_p13 && !eof1 && !eof3)))
4944 apr_size_t bytes_read1, bytes_read2, bytes_read3;
4945 svn_boolean_t read_1, read_2, read_3;
4947 read_1 = read_2 = read_3 = FALSE;
4949 /* As long as a file is not at the end yet, and it is still
4950 * potentially identical to another file, we read the next chunk.*/
4951 if (!eof1 && (*identical_p12 || *identical_p13))
4953 err = svn_io_file_read_full2(file1_h, buf1,
4954 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4955 &eof1, scratch_pool);
4961 if (!eof2 && (*identical_p12 || *identical_p23))
4963 err = svn_io_file_read_full2(file2_h, buf2,
4964 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4965 &eof2, scratch_pool);
4971 if (!eof3 && (*identical_p13 || *identical_p23))
4973 err = svn_io_file_read_full2(file3_h, buf3,
4974 SVN__STREAM_CHUNK_SIZE, &bytes_read3,
4975 &eof3, scratch_pool);
4981 /* If the files are still marked identical, and at least one of them
4982 * is not at the end of file, we check whether they differ, and set
4983 * their flag to false then. */
4985 && (read_1 || read_2)
4987 || (bytes_read1 != bytes_read2)
4988 || memcmp(buf1, buf2, bytes_read1)))
4990 *identical_p12 = FALSE;
4994 && (read_2 || read_3)
4996 || (bytes_read2 != bytes_read3)
4997 || memcmp(buf2, buf3, bytes_read2)))
4999 *identical_p23 = FALSE;
5003 && (read_1 || read_3)
5005 || (bytes_read1 != bytes_read3)
5006 || memcmp(buf1, buf3, bytes_read3)))
5008 *identical_p13 = FALSE;
5012 return svn_error_trace(
5013 svn_error_compose_create(
5015 svn_error_compose_create(
5016 svn_io_file_close(file1_h, scratch_pool),
5017 svn_error_compose_create(
5018 svn_io_file_close(file2_h, scratch_pool),
5019 svn_io_file_close(file3_h, scratch_pool)))));
5025 svn_io_files_contents_same_p(svn_boolean_t *same,
5032 SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
5037 return SVN_NO_ERROR;
5040 SVN_ERR(contents_identical_p(&q, file1, file2, pool));
5047 return SVN_NO_ERROR;
5051 svn_io_files_contents_three_same_p(svn_boolean_t *same12,
5052 svn_boolean_t *same23,
5053 svn_boolean_t *same13,
5057 apr_pool_t *scratch_pool)
5059 svn_boolean_t diff_size12, diff_size23, diff_size13;
5061 SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
5069 if (diff_size12 && diff_size23 && diff_size13)
5071 *same12 = *same23 = *same13 = FALSE;
5073 else if (diff_size12 && diff_size23)
5075 *same12 = *same23 = FALSE;
5076 SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
5078 else if (diff_size23 && diff_size13)
5080 *same23 = *same13 = FALSE;
5081 SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
5083 else if (diff_size12 && diff_size13)
5085 *same12 = *same13 = FALSE;
5086 SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
5090 SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
5091 SVN_ERR(contents_three_identical_p(same12, same23, same13,
5092 file1, file2, file3,
5096 return SVN_NO_ERROR;
5100 /* Counter value of file_mktemp request (used in a threadsafe way), to make
5101 sure that a single process normally never generates the same tempname
5103 static volatile apr_uint32_t tempname_counter = 0;
5106 /* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
5107 Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name.
5108 Perform temporary allocations in SCRATCH_POOL and the result in
5110 static svn_error_t *
5111 temp_file_create(apr_file_t **new_file,
5112 const char **new_file_name,
5113 const char *directory,
5115 apr_pool_t *result_pool,
5116 apr_pool_t *scratch_pool)
5119 const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
5120 const char *templ_apr;
5121 apr_status_t status;
5123 SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
5125 /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
5126 data available in POOL and we need a non-const pointer here,
5127 as apr changes the template to return the new filename. */
5128 status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool);
5131 return svn_error_wrap_apr(status, _("Can't create temporary file from "
5132 "template '%s'"), templ);
5134 /* Translate the returned path back to utf-8 before returning it */
5135 return svn_error_trace(svn_path_cstring_to_utf8(new_file_name,
5139 /* The Windows implementation of apr_file_mktemp doesn't handle access
5140 denied errors correctly. Therefore we implement our own temp file
5141 creation function here. */
5143 /* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
5144 ### the function we used before. But we try to guess a more unique
5145 ### name before trying if it exists. */
5147 /* Offset by some time value and a unique request nr to make the number
5148 +- unique for both this process and on the computer */
5149 int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter)
5150 + GetCurrentProcessId();
5153 /* ### Maybe use an iterpool? */
5154 for (i = 0; i <= 99999; i++)
5156 apr_uint32_t unique_nr;
5157 const char *unique_name;
5158 const char *unique_name_apr;
5159 apr_file_t *try_file;
5160 apr_status_t apr_err;
5162 /* Generate a number that should be unique for this application and
5163 usually for the entire computer to reduce the number of cycles
5164 through this loop. (A bit of calculation is much cheaper than
5166 unique_nr = baseNr + 3 * i;
5168 unique_name = svn_dirent_join(directory,
5169 apr_psprintf(scratch_pool, "svn-%X",
5173 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
5175 apr_err = file_open(&try_file, unique_name_apr, flags,
5176 APR_OS_DEFAULT, FALSE, scratch_pool);
5178 if (APR_STATUS_IS_EEXIST(apr_err))
5182 /* On Win32, CreateFile fails with an "Access Denied" error
5183 code, rather than "File Already Exists", if the colliding
5184 name belongs to a directory. */
5186 if (APR_STATUS_IS_EACCES(apr_err))
5189 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
5190 APR_FINFO_TYPE, scratch_pool);
5192 if (!apr_err_2 && finfo.filetype == APR_DIR)
5195 if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED) ||
5196 apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
5198 /* The file is in use by another process or is hidden;
5199 create a new name, but don't do this 99999 times in
5200 case the folder is not writable */
5205 /* Else fall through and return the original error. */
5208 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
5209 svn_dirent_local_style(unique_name,
5214 /* Move file to the right pool */
5215 apr_err = apr_file_setaside(new_file, try_file, result_pool);
5218 return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
5219 svn_dirent_local_style(unique_name,
5222 *new_file_name = apr_pstrdup(result_pool, unique_name);
5224 return SVN_NO_ERROR;
5228 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
5230 _("Unable to make name in '%s'"),
5231 svn_dirent_local_style(directory, scratch_pool));
5235 /* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
5237 svn_io_file_name_get(const char **filename,
5241 const char *fname_apr;
5242 apr_status_t status;
5244 status = apr_file_name_get(&fname_apr, file);
5246 return svn_error_wrap_apr(status, _("Can't get file name"));
5249 SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
5253 return SVN_NO_ERROR;
5258 svn_io_open_unique_file3(apr_file_t **file,
5259 const char **unique_path,
5260 const char *dirpath,
5261 svn_io_file_del_t delete_when,
5262 apr_pool_t *result_pool,
5263 apr_pool_t *scratch_pool)
5265 apr_file_t *tempfile;
5266 const char *tempname;
5267 struct temp_file_cleanup_s *baton = NULL;
5268 apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
5269 APR_BUFFERED | APR_BINARY);
5270 #if !defined(WIN32) && !defined(__OS2__)
5271 apr_fileperms_t perms;
5272 svn_boolean_t using_system_temp_dir = FALSE;
5275 SVN_ERR_ASSERT(file || unique_path);
5279 *unique_path = NULL;
5281 if (dirpath == NULL)
5283 #if !defined(WIN32) && !defined(__OS2__)
5284 using_system_temp_dir = TRUE;
5286 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
5289 switch (delete_when)
5291 case svn_io_file_del_on_pool_cleanup:
5292 baton = apr_palloc(result_pool, sizeof(*baton));
5293 baton->pool = result_pool;
5294 baton->fname_apr = NULL;
5296 /* Because cleanups are run LIFO, we need to make sure to register
5297 our cleanup before the apr_file_close cleanup:
5299 On Windows, you can't remove an open file.
5301 apr_pool_cleanup_register(result_pool, baton,
5302 temp_file_plain_cleanup_handler,
5303 temp_file_child_cleanup_handler);
5306 case svn_io_file_del_on_close:
5307 flags |= APR_DELONCLOSE;
5313 SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
5314 result_pool, scratch_pool));
5316 #if !defined(WIN32) && !defined(__OS2__)
5317 /* apr_file_mktemp() creates files with mode 0600.
5318 * This is appropriate if we're using a system temp dir since we don't
5319 * want to leak sensitive data into temp files other users can read.
5320 * If we're not using a system temp dir we're probably using the
5321 * .svn/tmp area and it's likely that the tempfile will end up being
5322 * copied or renamed into the working copy.
5323 * This would cause working files having mode 0600 while users might
5324 * expect to see 0644 or 0664. So we tweak perms of the tempfile in this
5325 * case, but only if the umask allows it. */
5326 if (!using_system_temp_dir)
5330 SVN_ERR(merge_default_file_perms(tempfile, &perms, dirpath,
5332 err = file_perms_set2(tempfile, perms, scratch_pool);
5335 if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
5336 APR_STATUS_IS_ENOTIMPL(err->apr_err))
5337 svn_error_clear(err);
5340 return svn_error_quick_wrapf(
5341 err, _("Can't set permissions on '%s'"),
5342 svn_dirent_local_style(tempname, scratch_pool));
5351 SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
5354 *unique_path = tempname; /* Was allocated in result_pool */
5357 SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
5359 return SVN_NO_ERROR;
5363 svn_io_file_readline(apr_file_t *file,
5364 svn_stringbuf_t **stringbuf,
5368 apr_pool_t *result_pool,
5369 apr_pool_t *scratch_pool)
5371 svn_stringbuf_t *str;
5372 const char *eol_str;
5373 apr_size_t numbytes;
5376 svn_boolean_t found_eof;
5378 str = svn_stringbuf_create_ensure(80, result_pool);
5380 /* Read bytes into STR up to and including, but not storing,
5381 * the next EOL sequence. */
5389 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
5390 &found_eof, scratch_pool));
5392 if (numbytes != 1 || len > max_len)
5406 if (!found_eof && len < max_len)
5410 /* Check for "\r\n" by peeking at the next byte. */
5411 SVN_ERR(svn_io_file_get_offset(&pos, file, scratch_pool));
5412 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
5413 &found_eof, scratch_pool));
5414 if (numbytes == 1 && c == '\n')
5421 /* Pretend we never peeked. */
5422 SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
5429 svn_stringbuf_appendbyte(str, c);
5441 return SVN_NO_ERROR;