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 apr_err_2 = APR_TO_OS_ERROR(apr_err);
583 if (apr_err_2 == ERROR_ACCESS_DENIED ||
584 apr_err_2 == ERROR_SHARING_VIOLATION)
586 /* The file is in use by another process or is hidden;
587 create a new name, but don't do this 99999 times in
588 case the folder is not writable */
594 /* Else fall through and return the original error. */
601 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
602 svn_dirent_local_style(unique_name,
607 if (delete_when == svn_io_file_del_on_pool_cleanup)
608 baton->fname_apr = apr_pstrdup(result_pool, unique_name_apr);
613 apr_file_close(try_file);
615 *unique_path = apr_pstrdup(result_pool, unique_name);
625 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
627 _("Unable to make name for '%s'"),
628 svn_dirent_local_style(path, scratch_pool));
632 svn_io_create_unique_link(const char **unique_name_p,
640 const char *unique_name;
641 const char *unique_name_apr;
642 const char *dest_apr;
645 SVN_ERR(cstring_from_utf8(&dest_apr, dest, pool));
646 for (i = 1; i <= 99999; i++)
648 apr_status_t apr_err;
650 /* Special case the first attempt -- if we can avoid having a
651 generated numeric portion at all, that's best. So first we
652 try with just the suffix; then future tries add a number
653 before the suffix. (A do-while loop could avoid the repeated
654 conditional, but it's not worth the clarity loss.)
656 If the first attempt fails, the first number will be "2".
657 This is good, since "1" would misleadingly imply that
658 the second attempt was actually the first... and if someone's
659 got conflicts on their conflicts, we probably don't want to
660 add to their confusion :-). */
662 unique_name = apr_psprintf(pool, "%s%s", path, suffix);
664 unique_name = apr_psprintf(pool, "%s.%u%s", path, i, suffix);
666 /* Hmmm. Ideally, we would append to a native-encoding buf
667 before starting iteration, then convert back to UTF-8 for
668 return. But I suppose that would make the appending code
669 sensitive to i18n in a way it shouldn't be... Oh well. */
670 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, pool));
672 rv = symlink(dest_apr, unique_name_apr);
673 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
675 apr_err = apr_get_os_error();
677 if (rv == -1 && APR_STATUS_IS_EEXIST(apr_err))
679 else if (rv == -1 && apr_err)
681 /* On Win32, CreateFile fails with an "Access Denied" error
682 code, rather than "File Already Exists", if the colliding
683 name belongs to a directory. */
684 if (APR_STATUS_IS_EACCES(apr_err))
687 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
688 APR_FINFO_TYPE, pool);
691 && (finfo.filetype == APR_DIR))
694 /* Else ignore apr_err_2; better to fall through and
695 return the original error. */
698 *unique_name_p = NULL;
699 return svn_error_wrap_apr(apr_err,
700 _("Can't create symbolic link '%s'"),
701 svn_dirent_local_style(unique_name, pool));
705 *unique_name_p = unique_name;
710 *unique_name_p = NULL;
711 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
713 _("Unable to make name for '%s'"),
714 svn_dirent_local_style(path, pool));
716 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
717 _("Symbolic links are not supported on this "
723 svn_io_read_link(svn_string_t **dest,
727 #if defined(HAVE_READLINK)
728 svn_string_t dest_apr;
729 const char *path_apr;
733 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
735 rv = readlink(path_apr, buf, sizeof(buf) - 1);
736 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
739 return svn_error_wrap_apr(apr_get_os_error(),
740 _("Can't read contents of link"));
746 /* ### Cast needed, one of these interfaces is wrong */
747 return svn_utf_string_to_utf8((const svn_string_t **)dest, &dest_apr, pool);
749 return io_win_read_link(dest, path, pool);
751 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
752 _("Symbolic links are not supported on this "
759 svn_io_copy_link(const char *src,
765 svn_string_t *link_dest;
768 /* Notice what the link is pointing at... */
769 SVN_ERR(svn_io_read_link(&link_dest, src, pool));
771 /* Make a tmp-link pointing at the same thing. */
772 SVN_ERR(svn_io_create_unique_link(&dst_tmp, dst, link_dest->data,
775 /* Move the tmp-link to link. */
776 return svn_io_file_rename(dst_tmp, dst, pool);
779 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
780 _("Symbolic links are not supported on this "
785 /* Temporary directory name cache for svn_io_temp_dir() */
786 static volatile svn_atomic_t temp_dir_init_state = 0;
787 static const char *temp_dir;
789 /* Helper function to initialize temp dir. Passed to svn_atomic__init_once */
791 init_temp_dir(void *baton, apr_pool_t *scratch_pool)
793 /* Global pool for the temp path */
794 apr_pool_t *global_pool = svn_pool_create(NULL);
797 apr_status_t apr_err = apr_temp_dir_get(&dir, scratch_pool);
800 return svn_error_wrap_apr(apr_err, _("Can't find a temporary directory"));
802 SVN_ERR(cstring_to_utf8(&dir, dir, scratch_pool));
804 dir = svn_dirent_internal_style(dir, scratch_pool);
806 SVN_ERR(svn_dirent_get_absolute(&temp_dir, dir, global_pool));
813 svn_io_temp_dir(const char **dir,
816 SVN_ERR(svn_atomic__init_once(&temp_dir_init_state,
817 init_temp_dir, NULL, pool));
819 *dir = apr_pstrdup(pool, temp_dir);
827 /*** Creating, copying and appending files. ***/
829 /* Transfer the contents of FROM_FILE to TO_FILE, using POOL for temporary
832 * NOTE: We don't use apr_copy_file() for this, since it takes filenames
833 * as parameters. Since we want to copy to a temporary file
834 * and rename for atomicity (see below), this would require an extra
835 * close/open pair, which can be expensive, especially on
836 * remote file systems.
839 copy_contents(apr_file_t *from_file,
843 /* Copy bytes till the cows come home. */
846 char buf[SVN__STREAM_CHUNK_SIZE];
847 apr_size_t bytes_this_time = sizeof(buf);
848 apr_status_t read_err;
849 apr_status_t write_err;
852 read_err = apr_file_read(from_file, buf, &bytes_this_time);
853 if (read_err && !APR_STATUS_IS_EOF(read_err))
859 write_err = apr_file_write_full(to_file, buf, bytes_this_time, NULL);
865 if (read_err && APR_STATUS_IS_EOF(read_err))
867 /* Return the results of this close: an error, or success. */
876 svn_io_copy_file(const char *src,
878 svn_boolean_t copy_perms,
881 apr_file_t *from_file, *to_file;
882 apr_status_t apr_err;
886 /* ### NOTE: sometimes src == dst. In this case, because we copy to a
887 ### temporary file, and then rename over the top of the destination,
888 ### the net result is resetting the permissions on src/dst.
890 ### Note: specifically, this can happen during a switch when the desired
891 ### permissions for a file change from one branch to another. See
894 ### ... yes, we should avoid copying to the same file, and we should
895 ### make the "reset perms" explicit. The switch *happens* to work
896 ### because of this copy-to-temp-then-rename implementation. If it
897 ### weren't for that, the switch would break.
899 #ifdef CHECK_FOR_SAME_FILE
900 if (strcmp(src, dst) == 0)
904 SVN_ERR(svn_io_file_open(&from_file, src, APR_READ,
905 APR_OS_DEFAULT, pool));
907 /* For atomicity, we copy to a tmp file and then rename the tmp
908 file over the real destination. */
910 SVN_ERR(svn_io_open_unique_file3(&to_file, &dst_tmp,
911 svn_dirent_dirname(dst, pool),
912 svn_io_file_del_none, pool, pool));
914 apr_err = copy_contents(from_file, to_file, pool);
918 err = svn_error_wrap_apr(apr_err, _("Can't copy '%s' to '%s'"),
919 svn_dirent_local_style(src, pool),
920 svn_dirent_local_style(dst_tmp, pool));
925 err = svn_error_compose_create(err,
926 svn_io_file_close(from_file, pool));
928 err = svn_error_compose_create(err,
929 svn_io_file_close(to_file, pool));
933 return svn_error_compose_create(
935 svn_io_remove_file2(dst_tmp, TRUE, pool));
938 /* If copying perms, set the perms on dst_tmp now, so they will be
939 atomically inherited in the upcoming rename. But note that we
940 had to wait until now to set perms, because if they say
941 read-only, then we'd have failed filling dst_tmp's contents. */
943 SVN_ERR(svn_io_copy_perms(src, dst_tmp, pool));
945 return svn_error_trace(svn_io_file_rename(dst_tmp, dst, pool));
948 #if !defined(WIN32) && !defined(__OS2__)
949 /* Wrapper for apr_file_perms_set(), taking a UTF8-encoded filename. */
951 file_perms_set(const char *fname, apr_fileperms_t perms,
954 const char *fname_apr;
957 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
959 status = apr_file_perms_set(fname_apr, perms);
961 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
967 /* Set permissions PERMS on the FILE. This is a cheaper variant of the
968 * file_perms_set wrapper() function because no locale-dependent string
969 * conversion is required. POOL will be used for allocations.
972 file_perms_set2(apr_file_t* file, apr_fileperms_t perms, apr_pool_t *pool)
974 const char *fname_apr;
977 status = apr_file_name_get(&fname_apr, file);
979 return svn_error_wrap_apr(status, _("Can't get file name"));
981 status = apr_file_perms_set(fname_apr, perms);
983 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
984 try_utf8_from_internal_style(fname_apr, pool));
989 #endif /* !WIN32 && !__OS2__ */
992 svn_io_copy_perms(const char *src,
996 /* ### On Windows or OS/2, apr_file_perms_set always returns APR_ENOTIMPL,
997 and the path passed to apr_file_perms_set must be encoded
998 in the platform-specific path encoding; not necessary UTF-8.
999 We need a platform-specific implementation to get the
1000 permissions right. */
1002 #if !defined(WIN32) && !defined(__OS2__)
1005 svn_node_kind_t kind;
1006 svn_boolean_t is_special;
1009 /* If DST is a symlink, don't bother copying permissions. */
1010 SVN_ERR(svn_io_check_special_path(dst, &kind, &is_special, pool));
1012 return SVN_NO_ERROR;
1014 SVN_ERR(svn_io_stat(&finfo, src, APR_FINFO_PROT, pool));
1015 err = file_perms_set(dst, finfo.protection, pool);
1018 /* We shouldn't be able to get APR_INCOMPLETE or APR_ENOTIMPL
1019 here under normal circumstances, because the perms themselves
1020 came from a call to apr_file_info_get(), and we already know
1021 this is the non-Win32 case. But if it does happen, it's not
1023 if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
1024 APR_STATUS_IS_ENOTIMPL(err->apr_err))
1025 svn_error_clear(err);
1028 return svn_error_quick_wrapf(
1029 err, _("Can't set permissions on '%s'"),
1030 svn_dirent_local_style(dst, pool));
1034 #endif /* !WIN32 && !__OS2__ */
1036 return SVN_NO_ERROR;
1041 svn_io_append_file(const char *src, const char *dst, apr_pool_t *pool)
1043 apr_status_t apr_err;
1044 const char *src_apr, *dst_apr;
1046 SVN_ERR(cstring_from_utf8(&src_apr, src, pool));
1047 SVN_ERR(cstring_from_utf8(&dst_apr, dst, pool));
1049 apr_err = apr_file_append(src_apr, dst_apr, APR_OS_DEFAULT, pool);
1052 return svn_error_wrap_apr(apr_err, _("Can't append '%s' to '%s'"),
1053 svn_dirent_local_style(src, pool),
1054 svn_dirent_local_style(dst, pool));
1056 return SVN_NO_ERROR;
1060 svn_error_t *svn_io_copy_dir_recursively(const char *src,
1061 const char *dst_parent,
1062 const char *dst_basename,
1063 svn_boolean_t copy_perms,
1064 svn_cancel_func_t cancel_func,
1068 svn_node_kind_t kind;
1069 apr_status_t status;
1070 const char *dst_path;
1071 apr_dir_t *this_dir;
1072 apr_finfo_t this_entry;
1073 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
1075 /* Make a subpool for recursion */
1076 apr_pool_t *subpool = svn_pool_create(pool);
1078 /* The 'dst_path' is simply dst_parent/dst_basename */
1079 dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
1081 /* Sanity checks: SRC and DST_PARENT are directories, and
1082 DST_BASENAME doesn't already exist in DST_PARENT. */
1083 SVN_ERR(svn_io_check_path(src, &kind, subpool));
1084 if (kind != svn_node_dir)
1085 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1086 _("Source '%s' is not a directory"),
1087 svn_dirent_local_style(src, pool));
1089 SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool));
1090 if (kind != svn_node_dir)
1091 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1092 _("Destination '%s' is not a directory"),
1093 svn_dirent_local_style(dst_parent, pool));
1095 SVN_ERR(svn_io_check_path(dst_path, &kind, subpool));
1096 if (kind != svn_node_none)
1097 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
1098 _("Destination '%s' already exists"),
1099 svn_dirent_local_style(dst_path, pool));
1101 /* Create the new directory. */
1102 /* ### TODO: copy permissions (needs apr_file_attrs_get()) */
1103 SVN_ERR(svn_io_dir_make(dst_path, APR_OS_DEFAULT, pool));
1105 /* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */
1106 SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
1108 for (status = apr_dir_read(&this_entry, flags, this_dir);
1109 status == APR_SUCCESS;
1110 status = apr_dir_read(&this_entry, flags, this_dir))
1112 if ((this_entry.name[0] == '.')
1113 && ((this_entry.name[1] == '\0')
1114 || ((this_entry.name[1] == '.')
1115 && (this_entry.name[2] == '\0'))))
1121 const char *src_target, *entryname_utf8;
1124 SVN_ERR(cancel_func(cancel_baton));
1126 SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
1128 src_target = svn_dirent_join(src, entryname_utf8, subpool);
1130 if (this_entry.filetype == APR_REG) /* regular file */
1132 const char *dst_target = svn_dirent_join(dst_path,
1135 SVN_ERR(svn_io_copy_file(src_target, dst_target,
1136 copy_perms, subpool));
1138 else if (this_entry.filetype == APR_LNK) /* symlink */
1140 const char *dst_target = svn_dirent_join(dst_path,
1143 SVN_ERR(svn_io_copy_link(src_target, dst_target,
1146 else if (this_entry.filetype == APR_DIR) /* recurse */
1148 /* Prevent infinite recursion by filtering off our
1149 newly created destination path. */
1150 if (strcmp(src, dst_parent) == 0
1151 && strcmp(entryname_utf8, dst_basename) == 0)
1154 SVN_ERR(svn_io_copy_dir_recursively
1163 /* ### support other APR node types someday?? */
1168 if (! (APR_STATUS_IS_ENOENT(status)))
1169 return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
1170 svn_dirent_local_style(src, pool));
1172 status = apr_dir_close(this_dir);
1174 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
1175 svn_dirent_local_style(src, pool));
1177 /* Free any memory used by recursion */
1178 svn_pool_destroy(subpool);
1180 return SVN_NO_ERROR;
1185 svn_io_make_dir_recursively(const char *path, apr_pool_t *pool)
1187 const char *path_apr;
1188 apr_status_t apr_err;
1190 if (svn_path_is_empty(path))
1191 /* Empty path (current dir) is assumed to always exist,
1192 so we do nothing, per docs. */
1193 return SVN_NO_ERROR;
1195 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1197 apr_err = apr_dir_make_recursive(path_apr, APR_OS_DEFAULT, pool);
1199 /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a
1201 if (apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
1202 WIN32_RETRY_LOOP(apr_err, apr_dir_make_recursive(path_apr,
1203 APR_OS_DEFAULT, pool));
1207 return svn_error_wrap_apr(apr_err, _("Can't make directory '%s'"),
1208 svn_dirent_local_style(path, pool));
1210 return SVN_NO_ERROR;
1214 svn_io_file_create_bytes(const char *file,
1215 const void *contents,
1217 apr_pool_t *scratch_pool)
1221 svn_error_t *err = SVN_NO_ERROR;
1223 SVN_ERR(svn_io_file_open(&f, file,
1224 (APR_WRITE | APR_CREATE | APR_EXCL),
1228 err = svn_io_file_write_full(f, contents, length, &written,
1231 err = svn_error_compose_create(
1233 svn_io_file_close(f, scratch_pool));
1237 /* Our caller doesn't know if we left a file or not if we return
1238 an error. Better to cleanup after ourselves if we created the
1240 return svn_error_trace(
1241 svn_error_compose_create(
1243 svn_io_remove_file2(file, TRUE, scratch_pool)));
1246 return SVN_NO_ERROR;
1250 svn_io_file_create(const char *file,
1251 const char *contents,
1254 return svn_error_trace(svn_io_file_create_bytes(file, contents,
1255 contents ? strlen(contents)
1261 svn_io_file_create_empty(const char *file,
1262 apr_pool_t *scratch_pool)
1264 return svn_error_trace(svn_io_file_create_bytes(file, NULL, 0,
1269 svn_io_dir_file_copy(const char *src_path,
1270 const char *dest_path,
1274 const char *file_dest_path = svn_dirent_join(dest_path, file, pool);
1275 const char *file_src_path = svn_dirent_join(src_path, file, pool);
1277 return svn_error_trace(
1278 svn_io_copy_file(file_src_path, file_dest_path, TRUE, pool));
1282 /*** Modtime checking. ***/
1285 svn_io_file_affected_time(apr_time_t *apr_time,
1291 SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_MIN | APR_FINFO_LINK, pool));
1293 *apr_time = finfo.mtime;
1295 return SVN_NO_ERROR;
1300 svn_io_set_file_affected_time(apr_time_t apr_time,
1304 apr_status_t status;
1305 const char *native_path;
1307 SVN_ERR(cstring_from_utf8(&native_path, path, pool));
1308 status = apr_file_mtime_set(native_path, apr_time, pool);
1311 return svn_error_wrap_apr(status, _("Can't set access time of '%s'"),
1312 svn_dirent_local_style(path, pool));
1314 return SVN_NO_ERROR;
1319 svn_io_sleep_for_timestamps(const char *path, apr_pool_t *pool)
1321 apr_time_t now, then;
1323 char *sleep_env_var;
1325 sleep_env_var = getenv(SVN_SLEEP_ENV_VAR);
1327 if (sleep_env_var && apr_strnatcasecmp(sleep_env_var, "yes") == 0)
1328 return; /* Allow skipping for testing */
1330 now = apr_time_now();
1332 /* Calculate 0.02 seconds after the next second wallclock tick. */
1333 then = apr_time_make(apr_time_sec(now) + 1, APR_USEC_PER_SEC / 50);
1335 /* Worst case is waiting one second, so we can use that time to determine
1336 if we can sleep shorter than that */
1341 err = svn_io_stat(&finfo, path, APR_FINFO_MTIME | APR_FINFO_LINK, pool);
1345 svn_error_clear(err); /* Fall back on original behavior */
1347 else if (finfo.mtime % APR_USEC_PER_SEC)
1349 /* Very simplistic but safe approach:
1350 If the filesystem has < sec mtime we can be reasonably sure
1351 that the filesystem has some sub-second resolution. On Windows
1352 it is likely to be sub-millisecond; on Linux systems it depends
1353 on the filesystem, ext4 is typically 1ms, 4ms or 10ms resolution.
1355 ## Perhaps find a better algorithm here. This will fail once
1356 in every 1000 cases on a millisecond precision filesystem
1357 if the mtime happens to be an exact second.
1359 But better to fail once in every thousand cases than every
1360 time, like we did before.
1362 Note for further research on algorithm:
1363 FAT32 has < 1 sec precision on ctime, but 2 sec on mtime.
1365 Linux/ext4 with CONFIG_HZ=250 has high resolution
1366 apr_time_now and although the filesystem timestamps
1367 have similar high precision they are only updated with
1368 a coarser 4ms resolution. */
1370 /* 10 milliseconds after now. */
1371 #ifndef SVN_HI_RES_SLEEP_MS
1372 #define SVN_HI_RES_SLEEP_MS 10
1374 then = now + apr_time_from_msec(SVN_HI_RES_SLEEP_MS);
1377 /* Remove time taken to do stat() from sleep. */
1378 now = apr_time_now();
1382 return; /* Passing negative values may suspend indefinitely (Windows) */
1384 /* (t < 1000 will be round to 0 in apr) */
1385 if (then - now < 1000)
1388 apr_sleep(then - now);
1393 svn_io_filesizes_different_p(svn_boolean_t *different_p,
1400 apr_status_t status;
1401 const char *file1_apr, *file2_apr;
1403 /* Not using svn_io_stat() because don't want to generate
1404 svn_error_t objects for non-error conditions. */
1406 SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool));
1407 SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool));
1409 /* Stat both files */
1410 status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool);
1413 /* If we got an error stat'ing a file, it could be because the
1414 file was removed... or who knows. Whatever the case, we
1415 don't know if the filesizes are definitely different, so
1416 assume that they're not. */
1417 *different_p = FALSE;
1418 return SVN_NO_ERROR;
1421 status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool);
1424 /* See previous comment. */
1425 *different_p = FALSE;
1426 return SVN_NO_ERROR;
1429 /* Examine file sizes */
1430 if (finfo1.size == finfo2.size)
1431 *different_p = FALSE;
1433 *different_p = TRUE;
1435 return SVN_NO_ERROR;
1440 svn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
1441 svn_boolean_t *different_p23,
1442 svn_boolean_t *different_p13,
1446 apr_pool_t *scratch_pool)
1448 apr_finfo_t finfo1, finfo2, finfo3;
1449 apr_status_t status1, status2, status3;
1450 const char *file1_apr, *file2_apr, *file3_apr;
1452 /* Not using svn_io_stat() because don't want to generate
1453 svn_error_t objects for non-error conditions. */
1455 SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool));
1456 SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool));
1457 SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool));
1459 /* Stat all three files */
1460 status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool);
1461 status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool);
1462 status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool);
1464 /* If we got an error stat'ing a file, it could be because the
1465 file was removed... or who knows. Whatever the case, we
1466 don't know if the filesizes are definitely different, so
1467 assume that they're not. */
1468 *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size;
1469 *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size;
1470 *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size;
1472 return SVN_NO_ERROR;
1477 svn_io_file_checksum2(svn_checksum_t **checksum,
1479 svn_checksum_kind_t kind,
1482 svn_stream_t *file_stream;
1483 svn_stream_t *checksum_stream;
1486 SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool));
1487 file_stream = svn_stream_from_aprfile2(f, FALSE, pool);
1488 checksum_stream = svn_stream_checksummed2(file_stream, checksum, NULL, kind,
1491 /* Because the checksummed stream will force the reading (and
1492 checksumming) of all the file's bytes, we can just close the stream
1493 and let its magic work. */
1494 return svn_stream_close(checksum_stream);
1499 svn_io_file_checksum(unsigned char digest[],
1503 svn_checksum_t *checksum;
1505 SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool));
1506 memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE);
1508 return SVN_NO_ERROR;
1513 /*** Permissions and modes. ***/
1515 #if !defined(WIN32) && !defined(__OS2__)
1516 /* Given the file specified by PATH, attempt to create an
1517 identical version of it owned by the current user. This is done by
1518 moving it to a temporary location, copying the file back to its old
1519 path, then deleting the temporarily moved version. All temporary
1520 allocations are done in POOL. */
1521 static svn_error_t *
1522 reown_file(const char *path,
1525 const char *unique_name;
1527 SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name,
1528 svn_dirent_dirname(path, pool),
1529 svn_io_file_del_none, pool, pool));
1530 SVN_ERR(svn_io_file_rename(path, unique_name, pool));
1531 SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool));
1532 return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool));
1535 /* Determine what the PERMS for a new file should be by looking at the
1536 permissions of a temporary file that we create.
1537 Unfortunately, umask() as defined in POSIX provides no thread-safe way
1538 to get at the current value of the umask, so what we're doing here is
1539 the only way we have to determine which combination of write bits
1540 (User/Group/World) should be set by default.
1541 Make temporary allocations in SCRATCH_POOL. */
1542 static svn_error_t *
1543 get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool)
1545 /* the default permissions as read from the temp folder */
1546 static apr_fileperms_t default_perms = 0;
1548 /* Technically, this "racy": Multiple threads may use enter here and
1549 try to figure out the default permission concurrently. That's fine
1550 since they will end up with the same results. Even more technical,
1551 apr_fileperms_t is an atomic type on 32+ bit machines.
1553 if (default_perms == 0)
1557 const char *fname_base, *fname;
1558 apr_uint32_t randomish;
1561 /* Get the perms for a newly created file to find out what bits
1564 Explicitly delete the file because we want this file to be as
1565 short-lived as possible since its presence means other
1566 processes may have to try multiple names.
1568 Using svn_io_open_uniquely_named() here because other tempfile
1569 creation functions tweak the permission bits of files they create.
1571 randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool
1572 + (apr_uint32_t)apr_time_now());
1573 fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish);
1575 SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, NULL, fname_base,
1576 NULL, svn_io_file_del_none,
1577 scratch_pool, scratch_pool));
1578 err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool);
1579 err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool));
1580 err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE,
1583 *perms = finfo.protection;
1584 default_perms = finfo.protection;
1587 *perms = default_perms;
1589 return SVN_NO_ERROR;
1592 /* OR together permission bits of the file FD and the default permissions
1593 of a file as determined by get_default_file_perms(). Do temporary
1594 allocations in SCRATCH_POOL. */
1595 static svn_error_t *
1596 merge_default_file_perms(apr_file_t *fd, apr_fileperms_t *perms,
1597 apr_pool_t *scratch_pool)
1600 apr_fileperms_t default_perms;
1602 SVN_ERR(get_default_file_perms(&default_perms, scratch_pool));
1603 SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool));
1605 /* Glom the perms together. */
1606 *perms = default_perms | finfo.protection;
1607 return SVN_NO_ERROR;
1610 /* This is a helper function for the svn_io_set_file_read* functions
1611 that attempts to honor the users umask when dealing with
1612 permission changes. It is a no-op when invoked on a symlink. */
1613 static svn_error_t *
1614 io_set_file_perms(const char *path,
1615 svn_boolean_t change_readwrite,
1616 svn_boolean_t enable_write,
1617 svn_boolean_t change_executable,
1618 svn_boolean_t executable,
1619 svn_boolean_t ignore_enoent,
1622 apr_status_t status;
1623 const char *path_apr;
1625 apr_fileperms_t perms_to_set;
1627 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1629 /* Try to change only a minimal amount of the perms first
1630 by getting the current perms and adding bits
1631 only on where read perms are granted. If this fails
1632 fall through to just setting file attributes. */
1633 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool);
1636 if (ignore_enoent && (APR_STATUS_IS_ENOENT(status)
1637 || SVN__APR_STATUS_IS_ENOTDIR(status)))
1638 return SVN_NO_ERROR;
1639 else if (status != APR_ENOTIMPL)
1640 return svn_error_wrap_apr(status,
1641 _("Can't change perms of file '%s'"),
1642 svn_dirent_local_style(path, pool));
1643 return SVN_NO_ERROR;
1646 if (finfo.filetype == APR_LNK)
1647 return SVN_NO_ERROR;
1649 perms_to_set = finfo.protection;
1650 if (change_readwrite)
1652 if (enable_write) /* Make read-write. */
1654 /* Tweak the owner bits only. The group/other bits aren't safe to
1655 * touch because we may end up setting them in undesired ways. */
1656 perms_to_set |= (APR_UREAD|APR_UWRITE);
1660 if (finfo.protection & APR_UREAD)
1661 perms_to_set &= ~APR_UWRITE;
1662 if (finfo.protection & APR_GREAD)
1663 perms_to_set &= ~APR_GWRITE;
1664 if (finfo.protection & APR_WREAD)
1665 perms_to_set &= ~APR_WWRITE;
1669 if (change_executable)
1673 if (finfo.protection & APR_UREAD)
1674 perms_to_set |= APR_UEXECUTE;
1675 if (finfo.protection & APR_GREAD)
1676 perms_to_set |= APR_GEXECUTE;
1677 if (finfo.protection & APR_WREAD)
1678 perms_to_set |= APR_WEXECUTE;
1682 if (finfo.protection & APR_UREAD)
1683 perms_to_set &= ~APR_UEXECUTE;
1684 if (finfo.protection & APR_GREAD)
1685 perms_to_set &= ~APR_GEXECUTE;
1686 if (finfo.protection & APR_WREAD)
1687 perms_to_set &= ~APR_WEXECUTE;
1691 /* If we aren't changing anything then just return, this saves
1692 some system calls and helps with shared working copies */
1693 if (perms_to_set == finfo.protection)
1694 return SVN_NO_ERROR;
1696 status = apr_file_perms_set(path_apr, perms_to_set);
1698 return SVN_NO_ERROR;
1700 if (APR_STATUS_IS_EPERM(status))
1702 /* We don't have permissions to change the
1703 permissions! Try a move, copy, and delete
1704 workaround to see if we can get the file owned by
1705 us. If these succeed, try the permissions set
1708 Note that we only attempt this in the
1709 stat-available path. This assumes that the
1710 move-copy workaround will only be helpful on
1711 platforms that implement apr_stat. */
1712 SVN_ERR(reown_file(path, pool));
1713 status = apr_file_perms_set(path_apr, perms_to_set);
1717 return SVN_NO_ERROR;
1719 if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1720 return SVN_NO_ERROR;
1721 else if (status == APR_ENOTIMPL)
1723 /* At least try to set the attributes. */
1724 apr_fileattrs_t attrs = 0;
1725 apr_fileattrs_t attrs_values = 0;
1727 if (change_readwrite)
1729 attrs = APR_FILE_ATTR_READONLY;
1731 attrs_values = APR_FILE_ATTR_READONLY;
1733 if (change_executable)
1735 attrs = APR_FILE_ATTR_EXECUTABLE;
1737 attrs_values = APR_FILE_ATTR_EXECUTABLE;
1739 status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
1742 return svn_error_wrap_apr(status,
1743 _("Can't change perms of file '%s'"),
1744 svn_dirent_local_style(path, pool));
1746 #endif /* !WIN32 && !__OS2__ */
1749 /* This is semantically the same as the APR utf8_to_unicode_path
1750 function, but reimplemented here because APR does not export it. */
1752 svn_io__utf8_to_unicode_longpath(const WCHAR **result,
1754 apr_pool_t *result_pool)
1756 /* This is correct, we don't twist the filename if it will
1757 * definitely be shorter than 248 characters. It merits some
1758 * performance testing to see if this has any effect, but there
1759 * seem to be applications that get confused by the resulting
1760 * Unicode \\?\ style file names, especially if they use argv[0]
1761 * or call the Win32 API functions such as GetModuleName, etc.
1762 * Not every application is prepared to handle such names.
1764 * Note also this is shorter than MAX_PATH, as directory paths
1765 * are actually limited to 248 characters.
1767 * Note that a utf-8 name can never result in more wide chars
1768 * than the original number of utf-8 narrow chars.
1770 const WCHAR *prefix = NULL;
1771 const int srclen = strlen(source);
1776 if (svn_ctype_isalpha(source[0]) && source[1] == ':'
1777 && (source[2] == '/' || source[2] == '\\'))
1779 /* This is an ordinary absolute path. */
1780 prefix = L"\\\\?\\";
1782 else if ((source[0] == '/' || source[0] == '\\')
1783 && (source[1] == '/' || source[1] == '\\')
1784 && source[2] != '?')
1786 /* This is a UNC path */
1787 source += 2; /* Skip the leading slashes */
1788 prefix = L"\\\\?\\UNC\\";
1792 SVN_ERR(svn_utf__win32_utf8_to_utf16(&(const WCHAR*)buffer, source,
1793 prefix, result_pool));
1795 /* Convert slashes to backslashes because the \\?\ path format
1796 does not allow backslashes as path separators. */
1798 for (; *buffer; ++buffer)
1803 return SVN_NO_ERROR;
1806 /* This is semantically the same as the APR unicode_to_utf8_path
1807 function, but reimplemented here because APR does not export it. */
1808 static svn_error_t *
1809 io_unicode_to_utf8_path(const char **result,
1810 const WCHAR *source,
1811 apr_pool_t *result_pool)
1813 const char *utf8_buffer;
1816 SVN_ERR(svn_utf__win32_utf16_to_utf8(&utf8_buffer, source,
1817 NULL, result_pool));
1820 *result = utf8_buffer;
1821 return SVN_NO_ERROR;
1824 /* We know that the non-empty buffer returned from the UTF-16 to
1825 UTF-8 conversion function is in fact writable. */
1826 buffer = (char*)utf8_buffer;
1828 /* Skip the leading 4 characters if the path begins \\?\, or substitute
1829 * // for the \\?\UNC\ path prefix, allocating the maximum string
1830 * length based on the remaining string, plus the trailing null.
1831 * then transform \\'s back into /'s since the \\?\ form never
1832 * allows '/' path separators, and APR always uses '/'s.
1834 if (0 == strncmp(buffer, "\\\\?\\", 4))
1837 if (0 == strncmp(buffer, "UNC\\", 4))
1845 for (; *buffer; ++buffer)
1847 if (*buffer == '\\')
1850 return SVN_NO_ERROR;
1853 static svn_error_t *
1854 io_win_file_attrs_set(const char *fname,
1859 /* this is an implementation of apr_file_attrs_set() but one
1860 that uses the proper Windows attributes instead of the apr
1861 attributes. This way, we can apply any Windows file and
1862 folder attributes even if apr doesn't implement them */
1864 const WCHAR *wfname;
1866 SVN_ERR(svn_io__utf8_to_unicode_longpath(&wfname, fname, pool));
1868 flags = GetFileAttributesW(wfname);
1869 if (flags == 0xFFFFFFFF)
1870 return svn_error_wrap_apr(apr_get_os_error(),
1871 _("Can't get attributes of file '%s'"),
1872 svn_dirent_local_style(fname, pool));
1874 flags &= ~attr_mask;
1875 flags |= (attributes & attr_mask);
1877 if (!SetFileAttributesW(wfname, flags))
1878 return svn_error_wrap_apr(apr_get_os_error(),
1879 _("Can't set attributes of file '%s'"),
1880 svn_dirent_local_style(fname, pool));
1882 return SVN_NO_ERROR;;
1885 static svn_error_t *win_init_dynamic_imports(void *baton, apr_pool_t *pool)
1887 HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
1891 get_final_path_name_by_handle_proc = (GETFINALPATHNAMEBYHANDLE)
1892 GetProcAddress(kernel32, "GetFinalPathNameByHandleW");
1894 set_file_information_by_handle_proc = (SetFileInformationByHandle_t)
1895 GetProcAddress(kernel32, "SetFileInformationByHandle");
1898 return SVN_NO_ERROR;
1901 static svn_error_t * io_win_read_link(svn_string_t **dest,
1905 SVN_ERR(svn_atomic__init_once(&win_dynamic_imports_state,
1906 win_init_dynamic_imports, NULL, pool));
1908 if (get_final_path_name_by_handle_proc)
1911 apr_status_t status;
1913 apr_os_file_t filehand;
1914 WCHAR wdest[APR_PATH_MAX];
1917 /* reserve one char for terminating zero. */
1918 DWORD wdest_len = sizeof(wdest)/sizeof(wdest[0]) - 1;
1920 status = apr_file_open(&file, path, APR_OPENINFO, APR_OS_DEFAULT, pool);
1923 return svn_error_wrap_apr(status,
1924 _("Can't read contents of link"));
1926 apr_os_file_get(&filehand, file);
1928 rv = get_final_path_name_by_handle_proc(
1929 filehand, wdest, wdest_len,
1930 FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
1932 /* Save error code. */
1933 status = apr_get_os_error();
1935 /* Close file/directory handle in any case. */
1936 apr_file_close(file);
1938 /* GetFinaPathNameByHandleW returns number of characters copied to
1939 * output buffer. Returns zero on error. Returns required buffer size
1940 * if supplied buffer is not enough. */
1941 if (rv > wdest_len || rv == 0)
1943 return svn_error_wrap_apr(status,
1944 _("Can't read contents of link"));
1947 /* GetFinaPathNameByHandleW doesn't add terminating NUL. */
1949 SVN_ERR(io_unicode_to_utf8_path(&data, wdest, pool));
1951 /* The result is already in the correct pool, so avoid copying
1952 it to create the string. */
1953 *dest = svn_string_create_empty(pool);
1956 (*dest)->data = data;
1957 (*dest)->len = strlen(data);
1960 return SVN_NO_ERROR;
1964 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1965 _("Symbolic links are not supported on this "
1970 /* Wrapper around Windows API function SetFileInformationByHandle() that
1971 * returns APR status instead of boolean flag. */
1973 win32_set_file_information_by_handle(HANDLE hFile,
1974 int FileInformationClass,
1975 LPVOID lpFileInformation,
1978 svn_error_clear(svn_atomic__init_once(&win_dynamic_imports_state,
1979 win_init_dynamic_imports,
1982 if (!set_file_information_by_handle_proc)
1984 return SVN_ERR_UNSUPPORTED_FEATURE;
1987 if (!set_file_information_by_handle_proc(hFile, FileInformationClass,
1991 return apr_get_os_error();
1998 svn_io__win_delete_file_on_close(apr_file_t *file,
2002 FILE_DISPOSITION_INFO disposition_info;
2004 apr_status_t status;
2006 apr_os_file_get(&hFile, file);
2008 disposition_info.DeleteFile = TRUE;
2010 status = win32_set_file_information_by_handle(hFile, FileDispositionInfo,
2012 sizeof(disposition_info));
2016 return svn_error_wrap_apr(status, _("Can't remove file '%s'"),
2017 svn_dirent_local_style(path, pool));
2020 return SVN_NO_ERROR;
2024 svn_io__win_rename_open_file(apr_file_t *file,
2025 const char *from_path,
2026 const char *to_path,
2029 WCHAR *w_final_abspath;
2032 FILE_RENAME_INFO *rename_info;
2034 apr_status_t status;
2036 apr_os_file_get(&hFile, file);
2038 SVN_ERR(svn_io__utf8_to_unicode_longpath(
2039 &w_final_abspath, svn_dirent_local_style(to_path,pool),
2042 path_len = wcslen(w_final_abspath);
2043 rename_size = sizeof(*rename_info) + sizeof(WCHAR) * path_len;
2045 /* The rename info struct doesn't need hacks for long paths,
2046 so no ugly escaping calls here */
2047 rename_info = apr_pcalloc(pool, rename_size);
2048 rename_info->ReplaceIfExists = TRUE;
2049 rename_info->FileNameLength = path_len;
2050 memcpy(rename_info->FileName, w_final_abspath, path_len * sizeof(WCHAR));
2052 status = win32_set_file_information_by_handle(hFile, FileRenameInfo,
2056 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
2058 /* Set the destination file writable because Windows will not allow
2059 us to rename when final_abspath is read-only. */
2060 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
2062 status = win32_set_file_information_by_handle(hFile,
2068 /* Windows returns Vista+ client accessing network share stored on Windows
2069 Server 2003 returns ERROR_ACCESS_DENIED. The same happens when Vista+
2070 client access Windows Server 2008 with disabled SMBv2 protocol.
2072 So return SVN_ERR_UNSUPPORTED_FEATURE in this case like we do when
2073 SetFileInformationByHandle() is not available and let caller to
2076 See "Access denied error on checkout-commit after updating to 1.9.X"
2077 discussion on dev@s.a.o:
2078 http://svn.haxx.se/dev/archive-2015-09/0054.shtml */
2079 if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED))
2081 status = SVN_ERR_UNSUPPORTED_FEATURE;
2086 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
2087 svn_dirent_local_style(from_path, pool),
2088 svn_dirent_local_style(to_path, pool));
2091 return SVN_NO_ERROR;
2097 svn_io_set_file_read_write_carefully(const char *path,
2098 svn_boolean_t enable_write,
2099 svn_boolean_t ignore_enoent,
2103 return svn_io_set_file_read_write(path, ignore_enoent, pool);
2104 return svn_io_set_file_read_only(path, ignore_enoent, pool);
2108 svn_io_set_file_read_only(const char *path,
2109 svn_boolean_t ignore_enoent,
2112 /* On Windows and OS/2, just set the file attributes -- on unix call
2113 our internal function which attempts to honor the umask. */
2114 #if !defined(WIN32) && !defined(__OS2__)
2115 return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE,
2116 ignore_enoent, pool);
2118 apr_status_t status;
2119 const char *path_apr;
2121 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2123 status = apr_file_attrs_set(path_apr,
2124 APR_FILE_ATTR_READONLY,
2125 APR_FILE_ATTR_READONLY,
2128 if (status && status != APR_ENOTIMPL)
2129 if (!(ignore_enoent && (APR_STATUS_IS_ENOENT(status)
2130 || SVN__APR_STATUS_IS_ENOTDIR(status))))
2131 return svn_error_wrap_apr(status,
2132 _("Can't set file '%s' read-only"),
2133 svn_dirent_local_style(path, pool));
2135 return SVN_NO_ERROR;
2141 svn_io_set_file_read_write(const char *path,
2142 svn_boolean_t ignore_enoent,
2145 /* On Windows and OS/2, just set the file attributes -- on unix call
2146 our internal function which attempts to honor the umask. */
2147 #if !defined(WIN32) && !defined(__OS2__)
2148 return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE,
2149 ignore_enoent, pool);
2151 apr_status_t status;
2152 const char *path_apr;
2154 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2156 status = apr_file_attrs_set(path_apr,
2158 APR_FILE_ATTR_READONLY,
2161 if (status && status != APR_ENOTIMPL)
2162 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
2163 return svn_error_wrap_apr(status,
2164 _("Can't set file '%s' read-write"),
2165 svn_dirent_local_style(path, pool));
2167 return SVN_NO_ERROR;
2172 svn_io_set_file_executable(const char *path,
2173 svn_boolean_t executable,
2174 svn_boolean_t ignore_enoent,
2177 /* On Windows and OS/2, just exit -- on unix call our internal function
2178 which attempts to honor the umask. */
2179 #if (!defined(WIN32) && !defined(__OS2__))
2180 return io_set_file_perms(path, FALSE, FALSE, TRUE, executable,
2181 ignore_enoent, pool);
2183 return SVN_NO_ERROR;
2189 svn_io__is_finfo_read_only(svn_boolean_t *read_only,
2190 apr_finfo_t *file_info,
2193 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2194 apr_status_t apr_err;
2200 apr_err = apr_uid_current(&uid, &gid, pool);
2203 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
2205 /* Check write bit for current user. */
2206 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
2207 *read_only = !(file_info->protection & APR_UWRITE);
2209 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
2210 *read_only = !(file_info->protection & APR_GWRITE);
2213 *read_only = !(file_info->protection & APR_WWRITE);
2215 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
2216 *read_only = (file_info->protection & APR_FREADONLY);
2219 return SVN_NO_ERROR;
2223 svn_io__is_finfo_executable(svn_boolean_t *executable,
2224 apr_finfo_t *file_info,
2227 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2228 apr_status_t apr_err;
2232 *executable = FALSE;
2234 apr_err = apr_uid_current(&uid, &gid, pool);
2237 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
2239 /* Check executable bit for current user. */
2240 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
2241 *executable = (file_info->protection & APR_UEXECUTE);
2243 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
2244 *executable = (file_info->protection & APR_GEXECUTE);
2247 *executable = (file_info->protection & APR_WEXECUTE);
2249 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
2250 *executable = FALSE;
2253 return SVN_NO_ERROR;
2257 svn_io_is_file_executable(svn_boolean_t *executable,
2261 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2262 apr_finfo_t file_info;
2264 SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
2266 SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
2268 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
2269 *executable = FALSE;
2272 return SVN_NO_ERROR;
2276 /*** File locking. ***/
2277 #if !defined(WIN32) && !defined(__OS2__)
2278 /* Clear all outstanding locks on ARG, an open apr_file_t *. */
2280 file_clear_locks(void *arg)
2282 apr_status_t apr_err;
2283 apr_file_t *f = arg;
2286 apr_err = apr_file_unlock(f);
2295 svn_io_lock_open_file(apr_file_t *lockfile_handle,
2296 svn_boolean_t exclusive,
2297 svn_boolean_t nonblocking,
2300 int locktype = APR_FLOCK_SHARED;
2301 apr_status_t apr_err;
2305 locktype = APR_FLOCK_EXCLUSIVE;
2307 locktype |= APR_FLOCK_NONBLOCK;
2309 /* We need this only in case of an error but this is cheap to get -
2310 * so we do it here for clarity. */
2311 apr_err = apr_file_name_get(&fname, lockfile_handle);
2313 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2315 /* Get lock on the filehandle. */
2316 apr_err = apr_file_lock(lockfile_handle, locktype);
2318 /* In deployments with two or more multithreaded servers running on
2319 the same system serving two or more fsfs repositories it is
2320 possible for a deadlock to occur when getting a write lock on
2321 db/txn-current-lock:
2325 thread 1: get lock in repos A
2326 thread 1: get lock in repos B
2327 thread 2: block getting lock in repos A
2328 thread 2: try to get lock in B *** deadlock ***
2330 Retry for a while for the deadlock to clear. */
2331 FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
2335 switch (locktype & APR_FLOCK_TYPEMASK)
2337 case APR_FLOCK_SHARED:
2338 return svn_error_wrap_apr(apr_err,
2339 _("Can't get shared lock on file '%s'"),
2340 try_utf8_from_internal_style(fname, pool));
2341 case APR_FLOCK_EXCLUSIVE:
2342 return svn_error_wrap_apr(apr_err,
2343 _("Can't get exclusive lock on file '%s'"),
2344 try_utf8_from_internal_style(fname, pool));
2346 SVN_ERR_MALFUNCTION();
2350 /* On Windows and OS/2 file locks are automatically released when
2351 the file handle closes */
2352 #if !defined(WIN32) && !defined(__OS2__)
2353 apr_pool_cleanup_register(pool, lockfile_handle,
2355 apr_pool_cleanup_null);
2358 return SVN_NO_ERROR;
2362 svn_io_unlock_open_file(apr_file_t *lockfile_handle,
2366 apr_status_t apr_err;
2368 /* We need this only in case of an error but this is cheap to get -
2369 * so we do it here for clarity. */
2370 apr_err = apr_file_name_get(&fname, lockfile_handle);
2372 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2374 /* The actual unlock attempt. */
2375 apr_err = apr_file_unlock(lockfile_handle);
2377 return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
2378 try_utf8_from_internal_style(fname, pool));
2380 /* On Windows and OS/2 file locks are automatically released when
2381 the file handle closes */
2382 #if !defined(WIN32) && !defined(__OS2__)
2383 apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks);
2386 return SVN_NO_ERROR;
2390 svn_io_file_lock2(const char *lock_file,
2391 svn_boolean_t exclusive,
2392 svn_boolean_t nonblocking,
2395 int locktype = APR_FLOCK_SHARED;
2396 apr_file_t *lockfile_handle;
2400 locktype = APR_FLOCK_EXCLUSIVE;
2403 if (locktype == APR_FLOCK_EXCLUSIVE)
2406 /* locktype is never read after this block, so we don't need to bother
2407 setting it. If that were to ever change, uncomment the following
2410 locktype |= APR_FLOCK_NONBLOCK;
2413 SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
2417 /* Get lock on the filehandle. */
2418 return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
2422 svn_io__file_lock_autocreate(const char *lock_file,
2426 = svn_io_file_lock2(lock_file, TRUE, FALSE, pool);
2427 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
2429 /* No lock file? No big deal; these are just empty files anyway.
2430 Create it and try again. */
2431 svn_error_clear(err);
2433 /* This file creation is racy.
2434 We don't care as long as file gets created at all. */
2435 err = svn_io_file_create_empty(lock_file, pool);
2436 if (err && APR_STATUS_IS_EEXIST(err->apr_err))
2438 svn_error_clear(err);
2442 /* Finally, lock the file - if it exists */
2444 err = svn_io_file_lock2(lock_file, TRUE, FALSE, pool);
2447 return svn_error_trace(err);
2452 /* Data consistency/coherency operations. */
2454 svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
2457 apr_os_file_t filehand;
2459 /* ### In apr 1.4+ we could delegate most of this function to
2460 apr_file_sync(). The only major difference is that this doesn't
2461 contain the retry loop for EINTR on linux. */
2463 /* First make sure that any user-space buffered data is flushed. */
2464 SVN_ERR(svn_io_file_flush(file, pool));
2466 apr_os_file_get(&filehand, file);
2468 /* Call the operating system specific function to actually force the
2473 if (! FlushFileBuffers(filehand))
2474 return svn_error_wrap_apr(apr_get_os_error(),
2475 _("Can't flush file to disk"));
2482 rv = fcntl(filehand, F_FULLFSYNC, 0);
2484 rv = fsync(filehand);
2486 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
2488 /* If the file is in a memory filesystem, fsync() may return
2489 EINVAL. Presumably the user knows the risks, and we can just
2490 ignore the error. */
2491 if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
2492 return SVN_NO_ERROR;
2495 return svn_error_wrap_apr(apr_get_os_error(),
2496 _("Can't flush file to disk"));
2500 return SVN_NO_ERROR;
2505 /* TODO write test for these two functions, then refactor. */
2507 /* Set RESULT to an svn_stringbuf_t containing the contents of FILE.
2508 FILENAME is the FILE's on-disk APR-safe name, or NULL if that name
2509 isn't known. If CHECK_SIZE is TRUE, the function will attempt to
2510 first stat() the file to determine it's size before sucking its
2511 contents into the stringbuf. (Doing so can prevent unnecessary
2512 memory usage, an unwanted side effect of the stringbuf growth and
2513 reallocation mechanism.) */
2514 static svn_error_t *
2515 stringbuf_from_aprfile(svn_stringbuf_t **result,
2516 const char *filename,
2518 svn_boolean_t check_size,
2523 svn_stringbuf_t *res = NULL;
2524 apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE;
2527 /* If our caller wants us to check the size of the file for
2528 efficient memory handling, we'll try to do so. */
2531 apr_finfo_t finfo = { 0 };
2533 /* In some cases we get size 0 and no error for non files,
2534 so we also check for the name. (= cached in apr_file_t) */
2535 if (! apr_file_info_get(&finfo, APR_FINFO_SIZE, file) && finfo.fname)
2537 /* we've got the file length. Now, read it in one go. */
2539 res_initial_len = (apr_size_t)finfo.size;
2540 res = svn_stringbuf_create_ensure(res_initial_len, pool);
2541 SVN_ERR(svn_io_file_read_full2(file, res->data,
2542 res_initial_len, &res->len,
2544 res->data[res->len] = 0;
2547 return SVN_NO_ERROR;
2551 /* XXX: We should check the incoming data for being of type binary. */
2552 buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
2553 res = svn_stringbuf_create_ensure(res_initial_len, pool);
2555 /* apr_file_read will not return data and eof in the same call. So this loop
2556 * is safe from missing read data. */
2557 len = SVN__STREAM_CHUNK_SIZE;
2558 err = svn_io_file_read(file, buf, &len, pool);
2561 svn_stringbuf_appendbytes(res, buf, len);
2562 len = SVN__STREAM_CHUNK_SIZE;
2563 err = svn_io_file_read(file, buf, &len, pool);
2566 /* Having read all the data we *expect* EOF */
2567 if (err && !APR_STATUS_IS_EOF(err->apr_err))
2568 return svn_error_trace(err);
2569 svn_error_clear(err);
2572 return SVN_NO_ERROR;
2576 svn_stringbuf_from_file2(svn_stringbuf_t **result,
2577 const char *filename,
2582 if (filename[0] == '-' && filename[1] == '\0')
2584 apr_status_t apr_err;
2585 if ((apr_err = apr_file_open_stdin(&f, pool)))
2586 return svn_error_wrap_apr(apr_err, _("Can't open stdin"));
2587 SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool));
2591 SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool));
2592 SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool));
2594 return svn_io_file_close(f, pool);
2599 svn_stringbuf_from_file(svn_stringbuf_t **result,
2600 const char *filename,
2603 if (filename[0] == '-' && filename[1] == '\0')
2604 return svn_error_create
2605 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2606 _("Reading from stdin is disallowed"));
2607 return svn_stringbuf_from_file2(result, filename, pool);
2611 svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
2615 return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
2623 svn_io_remove_file2(const char *path,
2624 svn_boolean_t ignore_enoent,
2625 apr_pool_t *scratch_pool)
2627 apr_status_t apr_err;
2628 const char *path_apr;
2630 SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
2632 apr_err = apr_file_remove(path_apr, scratch_pool);
2635 /* If the target is read only NTFS reports EACCESS and FAT/FAT32
2637 if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
2639 /* Set the destination file writable because Windows will not
2640 allow us to delete when path is read-only */
2641 SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool));
2642 apr_err = apr_file_remove(path_apr, scratch_pool);
2645 return SVN_NO_ERROR;
2648 /* Check to make sure we aren't trying to delete a directory */
2649 if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED)
2650 || apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
2654 if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
2655 && finfo.filetype == APR_REG)
2657 WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr, scratch_pool));
2661 /* Just return the delete error */
2666 return SVN_NO_ERROR;
2668 else if (ignore_enoent && (APR_STATUS_IS_ENOENT(apr_err)
2669 || SVN__APR_STATUS_IS_ENOTDIR(apr_err)))
2671 return SVN_NO_ERROR;
2675 return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
2676 svn_dirent_local_style(path, scratch_pool));
2682 svn_io_remove_dir(const char *path, apr_pool_t *pool)
2684 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
2688 Mac OS X has a bug where if you're reading the contents of a
2689 directory via readdir in a loop, and you remove one of the entries in
2690 the directory and the directory has 338 or more files in it you will
2691 skip over some of the entries in the directory. Needless to say,
2692 this causes problems if you are using this kind of loop inside a
2693 function that is recursively deleting a directory, because when you
2694 get around to removing the directory it will still have something in
2695 it. A similar problem has been observed in other BSDs. This bug has
2696 since been fixed. See http://www.vnode.ch/fixing_seekdir for details.
2698 The workaround is to delete the files only _after_ the initial
2699 directory scan. A previous workaround involving rewinddir is
2700 problematic on Win32 and some NFS clients, notably NetBSD.
2702 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
2703 http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
2706 /* Neither windows nor unix allows us to delete a non-empty
2709 This is a function to perform the equivalent of 'rm -rf'. */
2711 svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
2712 svn_cancel_func_t cancel_func, void *cancel_baton,
2716 apr_pool_t *subpool;
2717 apr_hash_t *dirents;
2718 apr_hash_index_t *hi;
2720 /* Check for pending cancellation request.
2721 If we need to bail out, do so early. */
2724 SVN_ERR((*cancel_func)(cancel_baton));
2726 subpool = svn_pool_create(pool);
2728 err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
2731 /* if the directory doesn't exist, our mission is accomplished */
2732 if (ignore_enoent && (APR_STATUS_IS_ENOENT(err->apr_err)
2733 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2735 svn_error_clear(err);
2736 return SVN_NO_ERROR;
2738 return svn_error_trace(err);
2741 for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
2743 const char *name = apr_hash_this_key(hi);
2744 const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
2745 const char *fullpath;
2747 fullpath = svn_dirent_join(path, name, subpool);
2748 if (dirent->kind == svn_node_dir)
2750 /* Don't check for cancellation, the callee will immediately do so */
2751 SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func,
2752 cancel_baton, subpool));
2757 SVN_ERR((*cancel_func)(cancel_baton));
2759 err = svn_io_remove_file2(fullpath, FALSE, subpool);
2761 return svn_error_createf
2762 (err->apr_err, err, _("Can't remove '%s'"),
2763 svn_dirent_local_style(fullpath, subpool));
2767 svn_pool_destroy(subpool);
2769 return svn_io_dir_remove_nonrecursive(path, pool);
2773 svn_io_get_dir_filenames(apr_hash_t **dirents,
2777 return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
2782 svn_io_dirent2_create(apr_pool_t *result_pool)
2784 svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
2786 /*dirent->kind = svn_node_none;
2787 dirent->special = FALSE;*/
2788 dirent->filesize = SVN_INVALID_FILESIZE;
2789 /*dirent->mtime = 0;*/
2795 svn_io_dirent2_dup(const svn_io_dirent2_t *item,
2796 apr_pool_t *result_pool)
2798 return apr_pmemdup(result_pool,
2804 svn_io_get_dirents3(apr_hash_t **dirents,
2806 svn_boolean_t only_check_type,
2807 apr_pool_t *result_pool,
2808 apr_pool_t *scratch_pool)
2810 apr_status_t status;
2811 apr_dir_t *this_dir;
2812 apr_finfo_t this_entry;
2813 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
2815 if (!only_check_type)
2816 flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
2818 *dirents = apr_hash_make(result_pool);
2820 SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
2822 for (status = apr_dir_read(&this_entry, flags, this_dir);
2823 status == APR_SUCCESS;
2824 status = apr_dir_read(&this_entry, flags, this_dir))
2826 if ((this_entry.name[0] == '.')
2827 && ((this_entry.name[1] == '\0')
2828 || ((this_entry.name[1] == '.')
2829 && (this_entry.name[2] == '\0'))))
2836 svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
2838 SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
2840 map_apr_finfo_to_node_kind(&(dirent->kind),
2844 if (!only_check_type)
2846 dirent->filesize = this_entry.size;
2847 dirent->mtime = this_entry.mtime;
2850 svn_hash_sets(*dirents, name, dirent);
2854 if (! (APR_STATUS_IS_ENOENT(status)))
2855 return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
2856 svn_dirent_local_style(path, scratch_pool));
2858 status = apr_dir_close(this_dir);
2860 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
2861 svn_dirent_local_style(path, scratch_pool));
2863 return SVN_NO_ERROR;
2867 svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
2869 svn_boolean_t verify_truename,
2870 svn_boolean_t ignore_enoent,
2871 apr_pool_t *result_pool,
2872 apr_pool_t *scratch_pool)
2875 svn_io_dirent2_t *dirent;
2877 apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
2878 | APR_FINFO_SIZE | APR_FINFO_MTIME;
2880 #if defined(WIN32) || defined(__OS2__)
2881 if (verify_truename)
2882 wanted |= APR_FINFO_NAME;
2885 err = svn_io_stat(&finfo, path, wanted, scratch_pool);
2887 if (err && ignore_enoent &&
2888 (APR_STATUS_IS_ENOENT(err->apr_err)
2889 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2891 svn_error_clear(err);
2892 dirent = svn_io_dirent2_create(result_pool);
2893 SVN_ERR_ASSERT(dirent->kind == svn_node_none);
2896 return SVN_NO_ERROR;
2900 #if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
2901 if (verify_truename)
2903 const char *requested_name = svn_dirent_basename(path, NULL);
2905 if (requested_name[0] == '\0')
2907 /* No parent directory. No need to stat/verify */
2909 #if defined(WIN32) || defined(__OS2__)
2910 else if (finfo.name)
2912 const char *name_on_disk;
2913 SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
2916 if (strcmp(name_on_disk, requested_name) /* != 0 */)
2920 *dirent_p = svn_io_dirent2_create(result_pool);
2921 return SVN_NO_ERROR;
2924 return svn_error_createf(APR_ENOENT, NULL,
2925 _("Path '%s' not found, case obstructed by '%s'"),
2926 svn_dirent_local_style(path, scratch_pool),
2930 #elif defined(DARWIN)
2931 /* Currently apr doesn't set finfo.name on DARWIN, returning
2933 ### Can we optimize this in another way? */
2936 apr_hash_t *dirents;
2938 err = svn_io_get_dirents3(&dirents,
2939 svn_dirent_dirname(path, scratch_pool),
2940 TRUE /* only_check_type */,
2941 scratch_pool, scratch_pool);
2943 if (err && ignore_enoent
2944 && (APR_STATUS_IS_ENOENT(err->apr_err)
2945 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2947 svn_error_clear(err);
2949 *dirent_p = svn_io_dirent2_create(result_pool);
2950 return SVN_NO_ERROR;
2955 if (! svn_hash_gets(dirents, requested_name))
2959 *dirent_p = svn_io_dirent2_create(result_pool);
2960 return SVN_NO_ERROR;
2963 return svn_error_createf(APR_ENOENT, NULL,
2964 _("Path '%s' not found"),
2965 svn_dirent_local_style(path, scratch_pool));
2972 dirent = svn_io_dirent2_create(result_pool);
2973 map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
2975 dirent->filesize = finfo.size;
2976 dirent->mtime = finfo.mtime;
2980 return SVN_NO_ERROR;
2983 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2984 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2986 /* Handle an error from the child process (before command execution) by
2987 printing DESC and the error string corresponding to STATUS to stderr. */
2989 handle_child_process_error(apr_pool_t *pool, apr_status_t status,
2993 apr_file_t *errfile;
2996 /* We can't do anything if we get an error here, so just return. */
2997 if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool))
3002 /* What we get from APR is in native encoding. */
3003 apr_file_printf(errfile, "%s: %s",
3004 desc, apr_strerror(status, errbuf,
3010 svn_io_start_cmd3(apr_proc_t *cmd_proc,
3013 const char *const *args,
3014 const char *const *env,
3015 svn_boolean_t inherit,
3016 svn_boolean_t infile_pipe,
3018 svn_boolean_t outfile_pipe,
3019 apr_file_t *outfile,
3020 svn_boolean_t errfile_pipe,
3021 apr_file_t *errfile,
3024 apr_status_t apr_err;
3025 apr_procattr_t *cmdproc_attr;
3027 const char **args_native;
3028 const char *cmd_apr;
3030 SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
3031 SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
3032 SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
3034 /* Create the process attributes. */
3035 apr_err = apr_procattr_create(&cmdproc_attr, pool);
3037 return svn_error_wrap_apr(apr_err,
3038 _("Can't create process '%s' attributes"),
3041 /* Make sure we invoke cmd directly, not through a shell. */
3042 apr_err = apr_procattr_cmdtype_set(cmdproc_attr,
3043 inherit ? APR_PROGRAM_PATH : APR_PROGRAM);
3045 return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
3048 /* Set the process's working directory. */
3051 const char *path_apr;
3053 /* APR doesn't like our canonical path format for current directory */
3054 if (path[0] == '\0')
3057 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
3058 apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
3060 return svn_error_wrap_apr(apr_err,
3061 _("Can't set process '%s' directory"),
3065 /* Use requested inputs and outputs.
3067 ### Unfortunately each of these apr functions creates a pipe and then
3068 overwrites the pipe file descriptor with the descriptor we pass
3069 in. The pipes can then never be closed. This is an APR bug. */
3072 apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
3074 return svn_error_wrap_apr(apr_err,
3075 _("Can't set process '%s' child input"),
3080 apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
3082 return svn_error_wrap_apr(apr_err,
3083 _("Can't set process '%s' child outfile"),
3088 apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
3090 return svn_error_wrap_apr(apr_err,
3091 _("Can't set process '%s' child errfile"),
3095 /* Forward request for pipes to APR. */
3096 if (infile_pipe || outfile_pipe || errfile_pipe)
3098 apr_err = apr_procattr_io_set(cmdproc_attr,
3099 infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
3100 outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
3101 errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE);
3104 return svn_error_wrap_apr(apr_err,
3105 _("Can't set process '%s' stdio pipes"),
3109 /* Have the child print any problems executing its program to errfile. */
3110 apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
3112 return svn_error_wrap_apr(apr_err,
3113 _("Can't set process '%s' child errfile for "
3116 apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
3117 handle_child_process_error);
3119 return svn_error_wrap_apr(apr_err,
3120 _("Can't set process '%s' error handler"),
3123 /* Convert cmd and args from UTF-8 */
3124 SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool));
3125 for (num_args = 0; args[num_args]; num_args++)
3127 args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
3128 args_native[num_args] = NULL;
3131 /* ### Well, it turns out that on APR on Windows expects all
3132 program args to be in UTF-8. Callers of svn_io_run_cmd
3133 should be aware of that. */
3134 SVN_ERR(cstring_from_utf8(&args_native[num_args],
3135 args[num_args], pool));
3139 /* Start the cmd command. */
3140 apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
3141 inherit ? NULL : env, cmdproc_attr, pool);
3143 return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
3145 return SVN_NO_ERROR;
3151 svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
3154 apr_exit_why_e *exitwhy,
3157 apr_status_t apr_err;
3158 apr_exit_why_e exitwhy_val;
3161 /* The Win32 apr_proc_wait doesn't set this... */
3162 exitwhy_val = APR_PROC_EXIT;
3164 /* Wait for the cmd command to finish. */
3165 apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT);
3166 if (!APR_STATUS_IS_CHILD_DONE(apr_err))
3167 return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"),
3171 *exitwhy = exitwhy_val;
3172 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)
3173 && APR_PROC_CHECK_CORE_DUMP(exitwhy_val))
3174 return svn_error_createf
3175 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3176 _("Process '%s' failed (signal %d, core dumped)"),
3178 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val))
3179 return svn_error_createf
3180 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3181 _("Process '%s' failed (signal %d)"),
3183 else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
3184 /* Don't really know what happened here. */
3185 return svn_error_createf
3186 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3187 _("Process '%s' failed (exitwhy %d, exitcode %d)"),
3188 cmd, exitwhy_val, exitcode_val);
3191 *exitcode = exitcode_val;
3192 else if (exitcode_val != 0)
3193 return svn_error_createf
3194 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3195 _("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
3197 return SVN_NO_ERROR;
3202 svn_io_run_cmd(const char *path,
3204 const char *const *args,
3206 apr_exit_why_e *exitwhy,
3207 svn_boolean_t inherit,
3209 apr_file_t *outfile,
3210 apr_file_t *errfile,
3213 apr_proc_t cmd_proc;
3215 SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
3216 FALSE, infile, FALSE, outfile, FALSE, errfile,
3219 return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
3224 svn_io_run_diff2(const char *dir,
3225 const char *const *user_args,
3232 apr_file_t *outfile,
3233 apr_file_t *errfile,
3234 const char *diff_cmd,
3240 int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
3241 apr_pool_t *subpool = svn_pool_create(pool);
3243 if (pexitcode == NULL)
3244 pexitcode = &exitcode;
3246 if (user_args != NULL)
3247 nargs += num_user_args;
3249 nargs += 1; /* -u */
3252 nargs += 2; /* the -L and the label itself */
3254 nargs += 2; /* the -L and the label itself */
3256 args = apr_palloc(subpool, nargs * sizeof(char *));
3259 args[i++] = diff_cmd;
3261 if (user_args != NULL)
3264 for (j = 0; j < num_user_args; ++j)
3265 args[i++] = user_args[j];
3268 args[i++] = "-u"; /* assume -u if the user didn't give us any args */
3281 args[i++] = svn_dirent_local_style(from, subpool);
3282 args[i++] = svn_dirent_local_style(to, subpool);
3285 SVN_ERR_ASSERT(i == nargs);
3287 SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
3288 NULL, outfile, errfile, subpool));
3290 /* The man page for (GNU) diff describes the return value as:
3292 "An exit status of 0 means no differences were found, 1 means
3293 some differences were found, and 2 means trouble."
3295 A return value of 2 typically occurs when diff cannot read its input
3296 or write to its output, but in any case we probably ought to return an
3297 error for anything other than 0 or 1 as the output is likely to be
3300 if (*pexitcode != 0 && *pexitcode != 1)
3301 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3302 _("'%s' returned %d"),
3303 svn_dirent_local_style(diff_cmd, pool),
3306 svn_pool_destroy(subpool);
3308 return SVN_NO_ERROR;
3313 svn_io_run_diff3_3(int *exitcode,
3318 const char *mine_label,
3319 const char *older_label,
3320 const char *yours_label,
3322 const char *diff3_cmd,
3323 const apr_array_header_t *user_args,
3326 const char **args = apr_palloc(pool,
3336 /* Labels fall back to sensible defaults if not specified. */
3337 if (mine_label == NULL)
3338 mine_label = ".working";
3339 if (older_label == NULL)
3340 older_label = ".old";
3341 if (yours_label == NULL)
3342 yours_label = ".new";
3344 /* Set up diff3 command line. */
3345 args[i++] = diff3_cmd;
3349 for (j = 0; j < user_args->nelts; ++j)
3350 args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
3352 nargs += user_args->nelts;
3357 args[i++] = "-E"; /* We tried "-A" here, but that caused
3358 overlapping identical changes to
3359 conflict. See issue #682. */
3366 args[i++] = mine_label;
3368 args[i++] = older_label; /* note: this label is ignored if
3369 using 2-part markers, which is the
3372 args[i++] = yours_label;
3373 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
3375 svn_boolean_t has_arg;
3377 /* ### FIXME: we really shouldn't be reading the config here;
3378 instead, the necessary bits should be passed in by the caller.
3379 But should we add another parameter to this function, when the
3380 whole external diff3 thing might eventually go away? */
3384 SVN_ERR(svn_config_get_config(&config, pool));
3385 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
3386 SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS,
3387 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG,
3391 const char *diff_cmd, *diff_utf8;
3392 svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
3393 SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF);
3394 SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool));
3395 args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8,
3403 args[i++] = svn_dirent_local_style(mine, pool);
3404 args[i++] = svn_dirent_local_style(older, pool);
3405 args[i++] = svn_dirent_local_style(yours, pool);
3408 SVN_ERR_ASSERT(i == nargs);
3411 /* Run diff3, output the merged text into the scratch file. */
3412 SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
3414 TRUE, /* keep environment */
3418 /* According to the diff3 docs, a '0' means the merge was clean, and
3419 '1' means conflict markers were found. Anything else is real
3421 if ((*exitcode != 0) && (*exitcode != 1))
3422 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3423 _("Error running '%s': exitcode was %d, "
3425 "\nin directory '%s', basenames:\n%s\n%s\n%s"),
3426 svn_dirent_local_style(diff3_cmd, pool),
3428 svn_dirent_local_style(dir, pool),
3429 /* Don't call svn_path_local_style() on
3430 the basenames. We don't want them to
3431 be absolute, and we don't need the
3432 separator conversion. */
3433 mine, older, yours);
3435 return SVN_NO_ERROR;
3439 /* Canonicalize a string for hashing. Modifies KEY in place. */
3440 static APR_INLINE char *
3441 fileext_tolower(char *key)
3444 for (p = key; *p != 0; ++p)
3445 *p = (char)apr_tolower(*p);
3451 svn_io_parse_mimetypes_file(apr_hash_t **type_map,
3452 const char *mimetypes_file,
3455 svn_error_t *err = SVN_NO_ERROR;
3456 apr_hash_t *types = apr_hash_make(pool);
3457 svn_boolean_t eof = FALSE;
3458 svn_stringbuf_t *buf;
3459 apr_pool_t *subpool = svn_pool_create(pool);
3460 apr_file_t *types_file;
3461 svn_stream_t *mimetypes_stream;
3463 SVN_ERR(svn_io_file_open(&types_file, mimetypes_file,
3464 APR_READ, APR_OS_DEFAULT, pool));
3465 mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool);
3469 apr_array_header_t *tokens;
3472 svn_pool_clear(subpool);
3475 if ((err = svn_stream_readline(mimetypes_stream, &buf,
3476 APR_EOL_STR, &eof, subpool)))
3479 /* Only pay attention to non-empty, non-comment lines. */
3484 if (buf->data[0] == '#')
3487 /* Tokenize (into our return pool). */
3488 tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
3489 if (tokens->nelts < 2)
3492 /* The first token in a multi-token line is the media type.
3493 Subsequent tokens are filename extensions associated with
3495 type = APR_ARRAY_IDX(tokens, 0, const char *);
3496 for (i = 1; i < tokens->nelts; i++)
3498 /* We can safely address 'ext' as a non-const string because
3499 * we know svn_cstring_split() allocated it in 'pool' for us. */
3500 char *ext = APR_ARRAY_IDX(tokens, i, char *);
3501 fileext_tolower(ext);
3502 svn_hash_sets(types, ext, type);
3508 svn_pool_destroy(subpool);
3510 /* If there was an error above, close the file (ignoring any error
3511 from *that*) and return the originally error. */
3514 svn_error_clear(svn_stream_close(mimetypes_stream));
3518 /* Close the stream (which closes the underlying file, too). */
3519 SVN_ERR(svn_stream_close(mimetypes_stream));
3522 return SVN_NO_ERROR;
3527 svn_io_detect_mimetype2(const char **mimetype,
3529 apr_hash_t *mimetype_map,
3532 static const char * const generic_binary = "application/octet-stream";
3534 svn_node_kind_t kind;
3537 unsigned char block[1024];
3538 apr_size_t amt_read = sizeof(block);
3540 /* Default return value is NULL. */
3543 /* If there is a mimetype_map provided, we'll first try to look up
3544 our file's extension in the map. Failing that, we'll run the
3548 const char *type_from_map;
3549 char *path_ext; /* Can point to physical const memory but only when
3550 svn_path_splitext sets it to "". */
3551 svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
3552 fileext_tolower(path_ext);
3553 if ((type_from_map = svn_hash_gets(mimetype_map, path_ext)))
3555 *mimetype = type_from_map;
3556 return SVN_NO_ERROR;
3560 /* See if this file even exists, and make sure it really is a file. */
3561 SVN_ERR(svn_io_check_path(file, &kind, pool));
3562 if (kind != svn_node_file)
3563 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
3564 _("Can't detect MIME type of non-file '%s'"),
3565 svn_dirent_local_style(file, pool));
3567 SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
3569 /* Read a block of data from FILE. */
3570 err = svn_io_file_read(fh, block, &amt_read, pool);
3571 if (err && ! APR_STATUS_IS_EOF(err->apr_err))
3573 svn_error_clear(err);
3575 /* Now close the file. No use keeping it open any more. */
3576 SVN_ERR(svn_io_file_close(fh, pool));
3578 if (svn_io_is_binary_data(block, amt_read))
3579 *mimetype = generic_binary;
3581 return SVN_NO_ERROR;
3586 svn_io_is_binary_data(const void *data, apr_size_t len)
3588 const unsigned char *buf = data;
3590 if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
3592 /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
3593 * Treat it as plain text. */
3597 /* Right now, this function is going to be really stupid. It's
3598 going to examine the block of data, and make sure that 15%
3599 of the bytes are such that their value is in the ranges 0x07-0x0D
3600 or 0x20-0x7F, and that none of those bytes is 0x00. If those
3601 criteria are not met, we're calling it binary.
3603 NOTE: Originally, I intended to target 85% of the bytes being in
3604 the specified ranges, but I flubbed the condition. At any rate,
3605 folks aren't complaining, so I'm not sure that it's worth
3606 adjusting this retroactively now. --cmpilato */
3610 apr_size_t binary_count = 0;
3612 /* Run through the data we've read, counting the 'binary-ish'
3613 bytes. HINT: If we see a 0x00 byte, we'll set our count to its
3614 max and stop reading the file. */
3615 for (i = 0; i < len; i++)
3623 || ((buf[i] > 0x0D) && (buf[i] < 0x20))
3630 return (((binary_count * 1000) / len) > 850);
3638 svn_io_detect_mimetype(const char **mimetype,
3642 return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
3647 svn_io_file_open(apr_file_t **new_file, const char *fname,
3648 apr_int32_t flag, apr_fileperms_t perm,
3651 const char *fname_apr;
3652 apr_status_t status;
3654 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3655 status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
3659 return svn_error_wrap_apr(status, _("Can't open file '%s'"),
3660 svn_dirent_local_style(fname, pool));
3662 return SVN_NO_ERROR;
3666 static APR_INLINE svn_error_t *
3667 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
3668 const char *msg, const char *msg_no_name,
3675 return SVN_NO_ERROR;
3677 err = svn_io_file_name_get(&name, file, pool);
3680 svn_error_clear(err);
3682 /* ### Issue #3014: Return a specific error for broken pipes,
3683 * ### with a single element in the error chain. */
3684 if (SVN__APR_STATUS_IS_EPIPE(status))
3685 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
3688 return svn_error_wrap_apr(status, _(msg),
3689 try_utf8_from_internal_style(name, pool));
3691 return svn_error_wrap_apr(status, "%s", _(msg_no_name));
3696 svn_io_file_close(apr_file_t *file, apr_pool_t *pool)
3698 return do_io_file_wrapper_cleanup(file, apr_file_close(file),
3699 N_("Can't close file '%s'"),
3700 N_("Can't close stream"),
3706 svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
3708 return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file),
3709 N_("Can't read file '%s'"),
3710 N_("Can't read stream"),
3716 svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
3718 return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file),
3719 N_("Can't write file '%s'"),
3720 N_("Can't write stream"),
3726 svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
3727 apr_file_t *file, apr_pool_t *pool)
3729 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3730 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3732 return do_io_file_wrapper_cleanup(
3733 file, apr_file_info_get(finfo, wanted, file),
3734 N_("Can't get attribute information from file '%s'"),
3735 N_("Can't get attribute information from stream"),
3741 svn_io_file_read(apr_file_t *file, void *buf,
3742 apr_size_t *nbytes, apr_pool_t *pool)
3744 return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes),
3745 N_("Can't read file '%s'"),
3746 N_("Can't read stream"),
3752 svn_io_file_read_full2(apr_file_t *file, void *buf,
3753 apr_size_t nbytes, apr_size_t *bytes_read,
3754 svn_boolean_t *hit_eof,
3757 apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
3760 if (APR_STATUS_IS_EOF(status))
3763 return SVN_NO_ERROR;
3769 return do_io_file_wrapper_cleanup(file, status,
3770 N_("Can't read file '%s'"),
3771 N_("Can't read stream"),
3777 svn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
3778 apr_off_t *offset, apr_pool_t *pool)
3780 return do_io_file_wrapper_cleanup(
3781 file, apr_file_seek(file, where, offset),
3782 N_("Can't set position pointer in file '%s'"),
3783 N_("Can't set position pointer in stream"),
3788 svn_io_file_aligned_seek(apr_file_t *file,
3789 apr_off_t block_size,
3790 apr_off_t *buffer_start,
3792 apr_pool_t *scratch_pool)
3794 const apr_size_t apr_default_buffer_size = 4096;
3795 apr_size_t file_buffer_size = apr_default_buffer_size;
3796 apr_off_t desired_offset = 0;
3797 apr_off_t current = 0;
3798 apr_off_t aligned_offset = 0;
3799 svn_boolean_t fill_buffer = FALSE;
3801 /* paranoia check: huge blocks on 32 bit machines may cause overflows */
3802 SVN_ERR_ASSERT(block_size == (apr_size_t)block_size);
3804 /* default for invalid block sizes */
3805 if (block_size == 0)
3806 block_size = apr_default_buffer_size;
3808 file_buffer_size = apr_file_buffer_size_get(file);
3810 /* don't try to set a buffer size for non-buffered files! */
3811 if (file_buffer_size == 0)
3813 aligned_offset = offset;
3815 else if (file_buffer_size != (apr_size_t)block_size)
3817 /* FILE has the wrong buffer size. correct it */
3819 file_buffer_size = (apr_size_t)block_size;
3820 buffer = apr_palloc(apr_file_pool_get(file), file_buffer_size);
3821 apr_file_buffer_set(file, buffer, file_buffer_size);
3823 /* seek to the start of the block and cause APR to read 1 block */
3824 aligned_offset = offset - (offset % block_size);
3829 aligned_offset = offset - (offset % file_buffer_size);
3831 /* We have no way to determine the block start of an APR file.
3832 Furthermore, we don't want to throw away the current buffer
3833 contents. Thus, we re-align the buffer only if the CURRENT
3834 offset definitely lies outside the desired, aligned buffer.
3835 This covers the typical case of linear reads getting very
3836 close to OFFSET but reading the previous / following block.
3838 Note that ALIGNED_OFFSET may still be within the current
3839 buffer and no I/O will actually happen in the FILL_BUFFER
3842 SVN_ERR(svn_io_file_seek(file, APR_CUR, ¤t, scratch_pool));
3843 fill_buffer = aligned_offset + file_buffer_size <= current
3844 || current <= aligned_offset;
3850 apr_status_t status;
3852 /* seek to the start of the block and cause APR to read 1 block */
3853 SVN_ERR(svn_io_file_seek(file, APR_SET, &aligned_offset,
3855 status = apr_file_getc(&dummy, file);
3857 /* read may fail if we seek to or behind EOF. That's ok then. */
3858 if (status != APR_SUCCESS && !APR_STATUS_IS_EOF(status))
3859 return do_io_file_wrapper_cleanup(file, status,
3860 N_("Can't read file '%s'"),
3861 N_("Can't read stream"),
3865 /* finally, seek to the OFFSET the caller wants */
3866 desired_offset = offset;
3867 SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, scratch_pool));
3868 if (desired_offset != offset)
3869 return do_io_file_wrapper_cleanup(file, APR_EOF,
3870 N_("Can't seek in file '%s'"),
3871 N_("Can't seek in stream"),
3874 /* return the buffer start that we (probably) enforced */
3876 *buffer_start = aligned_offset;
3878 return SVN_NO_ERROR;
3883 svn_io_file_write(apr_file_t *file, const void *buf,
3884 apr_size_t *nbytes, apr_pool_t *pool)
3886 return svn_error_trace(do_io_file_wrapper_cleanup(
3887 file, apr_file_write(file, buf, nbytes),
3888 N_("Can't write to file '%s'"),
3889 N_("Can't write to stream"),
3894 svn_io_file_flush(apr_file_t *file,
3895 apr_pool_t *scratch_pool)
3897 return svn_error_trace(do_io_file_wrapper_cleanup(
3898 file, apr_file_flush(file),
3899 N_("Can't flush file '%s'"),
3900 N_("Can't flush stream"),
3905 svn_io_file_write_full(apr_file_t *file, const void *buf,
3906 apr_size_t nbytes, apr_size_t *bytes_written,
3909 /* We cannot simply call apr_file_write_full on Win32 as it may fail
3910 for larger values of NBYTES. In that case, we have to emulate the
3911 "_full" part here. Thus, always call apr_file_write directly on
3912 Win32 as this minimizes overhead for small data buffers. */
3914 #define MAXBUFSIZE 30*1024
3915 apr_size_t bw = nbytes;
3916 apr_size_t to_write = nbytes;
3918 /* try a simple "write everything at once" first */
3919 apr_status_t rv = apr_file_write(file, buf, &bw);
3920 buf = (char *)buf + bw;
3923 /* if the OS cannot handle that, use smaller chunks */
3924 if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
3925 && nbytes > MAXBUFSIZE)
3928 bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
3929 rv = apr_file_write(file, buf, &bw);
3930 buf = (char *)buf + bw;
3932 } while (rv == APR_SUCCESS && to_write > 0);
3935 /* bytes_written may actually be NULL */
3937 *bytes_written = nbytes - to_write;
3940 apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
3943 return svn_error_trace(do_io_file_wrapper_cleanup(
3945 N_("Can't write to file '%s'"),
3946 N_("Can't write to stream"),
3952 svn_io_write_unique(const char **tmp_path,
3953 const char *dirpath,
3956 svn_io_file_del_t delete_when,
3959 apr_file_t *new_file;
3962 SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
3963 delete_when, pool, pool));
3965 err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
3969 /* svn_io_file_flush_to_disk() can be very expensive, so use the
3970 cheaper standard flush if the file is created as temporary file
3972 if (delete_when == svn_io_file_del_none)
3973 err = svn_io_file_flush_to_disk(new_file, pool);
3975 err = svn_io_file_flush(new_file, pool);
3978 return svn_error_trace(
3979 svn_error_compose_create(err,
3980 svn_io_file_close(new_file, pool)));
3984 svn_io_write_atomic(const char *final_path,
3987 const char *copy_perms_path,
3988 apr_pool_t *scratch_pool)
3990 apr_file_t *tmp_file;
3991 const char *tmp_path;
3993 const char *dirname = svn_dirent_dirname(final_path, scratch_pool);
3995 SVN_ERR(svn_io_open_unique_file3(&tmp_file, &tmp_path, dirname,
3996 svn_io_file_del_none,
3997 scratch_pool, scratch_pool));
3999 err = svn_io_file_write_full(tmp_file, buf, nbytes, NULL, scratch_pool);
4002 err = svn_io_file_flush_to_disk(tmp_file, scratch_pool);
4004 err = svn_error_compose_create(err,
4005 svn_io_file_close(tmp_file, scratch_pool));
4007 if (!err && copy_perms_path)
4008 err = svn_io_copy_perms(copy_perms_path, tmp_path, scratch_pool);
4011 err = svn_io_file_rename(tmp_path, final_path, scratch_pool);
4015 err = svn_error_compose_create(err,
4016 svn_io_remove_file2(tmp_path, TRUE,
4019 return svn_error_createf(err->apr_err, err,
4020 _("Can't write '%s' atomically"),
4021 svn_dirent_local_style(final_path,
4027 /* Linux has the unusual feature that fsync() on a file is not
4028 enough to ensure that a file's directory entries have been
4029 flushed to disk; you have to fsync the directory as well.
4030 On other operating systems, we'd only be asking for trouble
4031 by trying to open and fsync a directory. */
4034 SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
4036 SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool));
4037 SVN_ERR(svn_io_file_close(file, scratch_pool));
4041 return SVN_NO_ERROR;
4045 svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
4047 /* This is a work-around. APR would flush the write buffer
4048 _after_ truncating the file causing now invalid buffered
4049 data to be written behind OFFSET. */
4050 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
4051 N_("Can't flush file '%s'"),
4052 N_("Can't flush stream"),
4055 return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset),
4056 N_("Can't truncate file '%s'"),
4057 N_("Can't truncate stream"),
4063 svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
4067 apr_size_t total_read = 0;
4068 svn_boolean_t eof = FALSE;
4071 apr_size_t buf_size = *limit;
4073 while (buf_size > 0)
4075 /* read a fair chunk of data at once. But don't get too ambitious
4076 * as that would result in too much waste. Also make sure we can
4077 * put a NUL after the last byte read.
4079 apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
4080 apr_size_t bytes_read = 0;
4086 /* read data block (or just a part of it) */
4087 SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
4088 &bytes_read, &eof, pool));
4090 /* look or a newline char */
4091 buf[bytes_read] = 0;
4092 eol = strchr(buf, '\n');
4095 apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
4098 *limit = total_read + (eol - buf);
4100 /* correct the file pointer:
4101 * appear as though we just had read the newline char
4103 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
4105 return SVN_NO_ERROR;
4109 /* no EOL found but we hit the end of the file.
4110 * Generate a nice EOF error object and return it.
4113 SVN_ERR(svn_io_file_getc(&dummy, file, pool));
4116 /* next data chunk */
4117 buf_size -= bytes_read;
4119 total_read += bytes_read;
4122 /* buffer limit has been exceeded without finding the EOL */
4123 err = svn_io_file_name_get(&name, file, pool);
4126 svn_error_clear(err);
4129 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
4130 _("Can't read length line in file '%s'"),
4131 svn_dirent_local_style(name, pool));
4133 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
4134 _("Can't read length line in stream"));
4139 svn_io_stat(apr_finfo_t *finfo, const char *fname,
4140 apr_int32_t wanted, apr_pool_t *pool)
4142 apr_status_t status;
4143 const char *fname_apr;
4145 /* APR doesn't like "" directories */
4146 if (fname[0] == '\0')
4149 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
4151 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
4152 wanted &= ~SVN__APR_FINFO_MASK_OUT;
4154 status = apr_stat(finfo, fname_apr, wanted, pool);
4156 return svn_error_wrap_apr(status, _("Can't stat '%s'"),
4157 svn_dirent_local_style(fname, pool));
4159 return SVN_NO_ERROR;
4164 svn_io_file_rename(const char *from_path, const char *to_path,
4167 apr_status_t status = APR_SUCCESS;
4168 const char *from_path_apr, *to_path_apr;
4170 SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
4171 SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
4173 status = apr_file_rename(from_path_apr, to_path_apr, pool);
4175 #if defined(WIN32) || defined(__OS2__)
4176 /* If the target file is read only NTFS reports EACCESS and
4177 FAT/FAT32 reports EEXIST */
4178 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
4180 /* Set the destination file writable because Windows will not
4181 allow us to rename when to_path is read-only, but will
4182 allow renaming when from_path is read only. */
4183 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
4185 status = apr_file_rename(from_path_apr, to_path_apr, pool);
4187 WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool));
4188 #endif /* WIN32 || __OS2__ */
4191 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
4192 svn_dirent_local_style(from_path, pool),
4193 svn_dirent_local_style(to_path, pool));
4195 return SVN_NO_ERROR;
4200 svn_io_file_move(const char *from_path, const char *to_path,
4203 svn_error_t *err = svn_io_file_rename(from_path, to_path, pool);
4205 if (err && APR_STATUS_IS_EXDEV(err->apr_err))
4207 const char *tmp_to_path;
4209 svn_error_clear(err);
4211 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_to_path,
4212 svn_dirent_dirname(to_path, pool),
4213 svn_io_file_del_none,
4216 err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool);
4220 err = svn_io_file_rename(tmp_to_path, to_path, pool);
4224 err = svn_io_remove_file2(from_path, FALSE, pool);
4226 return SVN_NO_ERROR;
4228 svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool));
4233 svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool));
4239 /* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
4240 HIDDEN determines if the hidden attribute
4241 should be set on the newly created directory. */
4242 static svn_error_t *
4243 dir_make(const char *path, apr_fileperms_t perm,
4244 svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
4246 apr_status_t status;
4247 const char *path_apr;
4249 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4251 /* APR doesn't like "" directories */
4252 if (path_apr[0] == '\0')
4255 #if (APR_OS_DEFAULT & APR_WSTICKY)
4256 /* The APR shipped with httpd 2.0.50 contains a bug where
4257 APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
4258 There is a special case for file creation, but not directory
4259 creation, so directories wind up getting created with the sticky
4260 bit set. (There is no such thing as a setuid directory, and the
4261 setgid bit is apparently ignored at mkdir() time.) If we detect
4262 this problem, work around it by unsetting those bits if we are
4263 passed APR_OS_DEFAULT. */
4264 if (perm == APR_OS_DEFAULT)
4265 perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY);
4268 status = apr_dir_make(path_apr, perm, pool);
4271 /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a
4273 if (status == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
4274 WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
4278 return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
4279 svn_dirent_local_style(path, pool));
4281 #ifdef APR_FILE_ATTR_HIDDEN
4285 status = apr_file_attrs_set(path_apr,
4286 APR_FILE_ATTR_HIDDEN,
4287 APR_FILE_ATTR_HIDDEN,
4290 return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
4291 svn_dirent_local_style(path, pool));
4293 /* on Windows, use our wrapper so we can also set the
4294 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */
4296 io_win_file_attrs_set(path_apr,
4297 FILE_ATTRIBUTE_HIDDEN |
4298 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
4299 FILE_ATTRIBUTE_HIDDEN |
4300 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
4303 return svn_error_createf(err->apr_err, err,
4304 _("Can't hide directory '%s'"),
4305 svn_dirent_local_style(path, pool));
4308 #endif /* APR_FILE_ATTR_HIDDEN */
4310 /* Windows does not implement sgid. Skip here because retrieving
4311 the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented
4312 to be 'incredibly expensive'. */
4318 /* Per our contract, don't do error-checking. Some filesystems
4319 * don't support the sgid bit, and that's okay. */
4320 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool);
4323 apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
4327 return SVN_NO_ERROR;
4331 svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
4333 return dir_make(path, perm, FALSE, FALSE, pool);
4337 svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
4340 return dir_make(path, perm, TRUE, FALSE, pool);
4344 svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
4347 return dir_make(path, perm, FALSE, TRUE, pool);
4352 svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
4354 apr_status_t status;
4355 const char *dirname_apr;
4357 /* APR doesn't like "" directories */
4358 if (dirname[0] == '\0')
4361 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4363 status = apr_dir_open(new_dir, dirname_apr, pool);
4365 return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
4366 svn_dirent_local_style(dirname, pool));
4368 return SVN_NO_ERROR;
4372 svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
4374 apr_status_t status;
4375 const char *dirname_apr;
4377 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4379 status = apr_dir_remove(dirname_apr, pool);
4383 svn_boolean_t retry = TRUE;
4385 if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY)
4387 apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
4389 if (APR_STATUS_IS_ENOTEMPTY(empty_status))
4395 WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
4400 return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
4401 svn_dirent_local_style(dirname, pool));
4403 return SVN_NO_ERROR;
4408 svn_io_dir_read(apr_finfo_t *finfo,
4413 apr_status_t status;
4415 status = apr_dir_read(finfo, wanted, thedir);
4418 return svn_error_wrap_apr(status, _("Can't read directory"));
4420 /* It would be nice to use entry_name_to_utf8() below, but can we
4421 get the dir's path out of an apr_dir_t? I don't see a reliable
4425 SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
4428 SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
4430 return SVN_NO_ERROR;
4434 svn_io_dir_close(apr_dir_t *thedir)
4436 apr_status_t apr_err = apr_dir_close(thedir);
4438 return svn_error_wrap_apr(apr_err, _("Error closing directory"));
4440 return SVN_NO_ERROR;
4444 svn_io_dir_walk2(const char *dirname,
4446 svn_io_walk_func_t walk_func,
4450 apr_status_t apr_err;
4452 apr_pool_t *subpool;
4453 const char *dirname_apr;
4456 wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
4458 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
4459 wanted &= ~SVN__APR_FINFO_MASK_OUT;
4461 /* The documentation for apr_dir_read used to state that "." and ".."
4462 will be returned as the first two files, but it doesn't
4463 work that way in practice, in particular ext3 on Linux-2.6 doesn't
4464 follow the rules. For details see
4465 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
4467 If APR ever does implement "dot-first" then it would be possible to
4468 remove the svn_io_stat and walk_func calls and use the walk_func
4471 Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
4472 documented to provide it, so we have to do a bit extra. */
4473 SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool));
4474 SVN_ERR(cstring_from_utf8(&finfo.name,
4475 svn_dirent_basename(dirname, pool),
4477 finfo.valid |= APR_FINFO_NAME;
4478 SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
4480 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4482 /* APR doesn't like "" directories */
4483 if (dirname_apr[0] == '\0')
4486 apr_err = apr_dir_open(&handle, dirname_apr, pool);
4488 return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
4489 svn_dirent_local_style(dirname, pool));
4491 /* iteration subpool */
4492 subpool = svn_pool_create(pool);
4496 const char *name_utf8;
4497 const char *full_path;
4499 svn_pool_clear(subpool);
4501 apr_err = apr_dir_read(&finfo, wanted, handle);
4502 if (APR_STATUS_IS_ENOENT(apr_err))
4506 return svn_error_wrap_apr(apr_err,
4507 _("Can't read directory entry in '%s'"),
4508 svn_dirent_local_style(dirname, pool));
4511 if (finfo.filetype == APR_DIR)
4513 if (finfo.name[0] == '.'
4514 && (finfo.name[1] == '\0'
4515 || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
4516 /* skip "." and ".." */
4519 /* some other directory. recurse. it will be passed to the
4520 callback inside the recursion. */
4521 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
4523 full_path = svn_dirent_join(dirname, name_utf8, subpool);
4524 SVN_ERR(svn_io_dir_walk2(full_path,
4530 else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
4532 /* some other directory. pass it to the callback. */
4533 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
4535 full_path = svn_dirent_join(dirname, name_utf8, subpool);
4536 SVN_ERR((*walk_func)(walk_baton,
4542 Some other type of file; skip it for now. We've reserved the
4543 right to expand our coverage here in the future, though,
4544 without revving this API.
4548 svn_pool_destroy(subpool);
4550 apr_err = apr_dir_close(handle);
4552 return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
4553 svn_dirent_local_style(dirname, pool));
4555 return SVN_NO_ERROR;
4561 * Determine if a directory is empty or not.
4562 * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
4563 * @param path The directory.
4564 * @param pool Used for temporary allocation.
4565 * @remark If path is not a directory, or some other error occurs,
4566 * then return the appropriate apr status code.
4568 * (This function is written in APR style, in anticipation of
4569 * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
4572 dir_is_empty(const char *dir, apr_pool_t *pool)
4574 apr_status_t apr_err;
4575 apr_dir_t *dir_handle;
4577 apr_status_t retval = APR_SUCCESS;
4579 /* APR doesn't like "" directories */
4583 apr_err = apr_dir_open(&dir_handle, dir, pool);
4584 if (apr_err != APR_SUCCESS)
4587 for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle);
4588 apr_err == APR_SUCCESS;
4589 apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle))
4591 /* Ignore entries for this dir and its parent, robustly.
4592 (APR promises that they'll come first, so technically
4593 this guard could be moved outside the loop. But Ryan Bloom
4594 says he doesn't believe it, and I believe him. */
4595 if (! (finfo.name[0] == '.'
4596 && (finfo.name[1] == '\0'
4597 || (finfo.name[1] == '.' && finfo.name[2] == '\0'))))
4599 retval = APR_ENOTEMPTY;
4604 /* Make sure we broke out of the loop for the right reason. */
4605 if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
4608 apr_err = apr_dir_close(dir_handle);
4609 if (apr_err != APR_SUCCESS)
4617 svn_io_dir_empty(svn_boolean_t *is_empty_p,
4621 apr_status_t status;
4622 const char *path_apr;
4624 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4626 status = dir_is_empty(path_apr, pool);
4630 else if (APR_STATUS_IS_ENOTEMPTY(status))
4631 *is_empty_p = FALSE;
4633 return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
4634 svn_dirent_local_style(path, pool));
4636 return SVN_NO_ERROR;
4641 /*** Version/format files ***/
4644 svn_io_write_version_file(const char *path,
4648 const char *path_tmp;
4649 const char *format_contents = apr_psprintf(pool, "%d\n", version);
4651 SVN_ERR_ASSERT(version >= 0);
4653 SVN_ERR(svn_io_write_unique(&path_tmp,
4654 svn_dirent_dirname(path, pool),
4655 format_contents, strlen(format_contents),
4656 svn_io_file_del_none, pool));
4658 #if defined(WIN32) || defined(__OS2__)
4659 /* make the destination writable, but only on Windows, because
4660 Windows does not let us replace read-only files. */
4661 SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
4662 #endif /* WIN32 || __OS2__ */
4664 /* rename the temp file as the real destination */
4665 SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
4667 /* And finally remove the perms to make it read only */
4668 return svn_io_set_file_read_only(path, FALSE, pool);
4673 svn_io_read_version_file(int *version,
4677 apr_file_t *format_file;
4682 /* Read a chunk of data from PATH */
4683 SVN_ERR(svn_io_file_open(&format_file, path, APR_READ,
4684 APR_OS_DEFAULT, pool));
4686 err = svn_io_file_read(format_file, buf, &len, pool);
4688 /* Close the file. */
4689 SVN_ERR(svn_error_compose_create(err,
4690 svn_io_file_close(format_file, pool)));
4692 /* If there was no data in PATH, return an error. */
4694 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
4696 svn_dirent_local_style(path, pool));
4698 /* Check that the first line contains only digits. */
4702 for (i = 0; i < len; ++i)
4706 if (i > 0 && (c == '\r' || c == '\n'))
4711 if (! svn_ctype_isdigit(c))
4712 return svn_error_createf
4713 (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
4714 _("First line of '%s' contains non-digit"),
4715 svn_dirent_local_style(path, pool));
4719 /* Convert to integer. */
4720 SVN_ERR(svn_cstring_atoi(version, buf));
4722 return SVN_NO_ERROR;
4726 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
4727 static svn_error_t *
4728 contents_identical_p(svn_boolean_t *identical_p,
4734 apr_size_t bytes_read1, bytes_read2;
4735 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4736 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4737 apr_file_t *file1_h;
4738 apr_file_t *file2_h;
4739 svn_boolean_t eof1 = FALSE;
4740 svn_boolean_t eof2 = FALSE;
4742 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4745 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4749 return svn_error_trace(
4750 svn_error_compose_create(err,
4751 svn_io_file_close(file1_h, pool)));
4753 *identical_p = TRUE; /* assume TRUE, until disproved below */
4754 while (!err && !eof1 && !eof2)
4756 err = svn_io_file_read_full2(file1_h, buf1,
4757 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4762 err = svn_io_file_read_full2(file2_h, buf2,
4763 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4768 if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
4770 *identical_p = FALSE;
4775 /* Special case: one file being a prefix of the other and the shorter
4776 * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */
4777 if (!err && (eof1 != eof2))
4778 *identical_p = FALSE;
4780 return svn_error_trace(
4781 svn_error_compose_create(
4783 svn_error_compose_create(svn_io_file_close(file1_h, pool),
4784 svn_io_file_close(file2_h, pool))));
4789 /* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
4790 static svn_error_t *
4791 contents_three_identical_p(svn_boolean_t *identical_p12,
4792 svn_boolean_t *identical_p23,
4793 svn_boolean_t *identical_p13,
4797 apr_pool_t *scratch_pool)
4800 char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4801 char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4802 char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4803 apr_file_t *file1_h;
4804 apr_file_t *file2_h;
4805 apr_file_t *file3_h;
4806 svn_boolean_t eof1 = FALSE;
4807 svn_boolean_t eof2 = FALSE;
4808 svn_boolean_t eof3 = FALSE;
4810 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4813 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4817 return svn_error_trace(
4818 svn_error_compose_create(err,
4819 svn_io_file_close(file1_h, scratch_pool)));
4821 err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
4825 return svn_error_trace(
4826 svn_error_compose_create(
4828 svn_error_compose_create(svn_io_file_close(file1_h,
4830 svn_io_file_close(file2_h,
4833 /* assume TRUE, until disproved below */
4834 *identical_p12 = *identical_p23 = *identical_p13 = TRUE;
4835 /* We need to read as long as no error occurs, and as long as one of the
4836 * flags could still change due to a read operation */
4838 && ((*identical_p12 && !eof1 && !eof2)
4839 || (*identical_p23 && !eof2 && !eof3)
4840 || (*identical_p13 && !eof1 && !eof3)))
4842 apr_size_t bytes_read1, bytes_read2, bytes_read3;
4843 svn_boolean_t read_1, read_2, read_3;
4845 read_1 = read_2 = read_3 = FALSE;
4847 /* As long as a file is not at the end yet, and it is still
4848 * potentially identical to another file, we read the next chunk.*/
4849 if (!eof1 && (*identical_p12 || *identical_p13))
4851 err = svn_io_file_read_full2(file1_h, buf1,
4852 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4853 &eof1, scratch_pool);
4859 if (!eof2 && (*identical_p12 || *identical_p23))
4861 err = svn_io_file_read_full2(file2_h, buf2,
4862 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4863 &eof2, scratch_pool);
4869 if (!eof3 && (*identical_p13 || *identical_p23))
4871 err = svn_io_file_read_full2(file3_h, buf3,
4872 SVN__STREAM_CHUNK_SIZE, &bytes_read3,
4873 &eof3, scratch_pool);
4879 /* If the files are still marked identical, and at least one of them
4880 * is not at the end of file, we check whether they differ, and set
4881 * their flag to false then. */
4883 && (read_1 || read_2)
4885 || (bytes_read1 != bytes_read2)
4886 || memcmp(buf1, buf2, bytes_read1)))
4888 *identical_p12 = FALSE;
4892 && (read_2 || read_3)
4894 || (bytes_read2 != bytes_read3)
4895 || memcmp(buf2, buf3, bytes_read2)))
4897 *identical_p23 = FALSE;
4901 && (read_1 || read_3)
4903 || (bytes_read1 != bytes_read3)
4904 || memcmp(buf1, buf3, bytes_read3)))
4906 *identical_p13 = FALSE;
4910 return svn_error_trace(
4911 svn_error_compose_create(
4913 svn_error_compose_create(
4914 svn_io_file_close(file1_h, scratch_pool),
4915 svn_error_compose_create(
4916 svn_io_file_close(file2_h, scratch_pool),
4917 svn_io_file_close(file3_h, scratch_pool)))));
4923 svn_io_files_contents_same_p(svn_boolean_t *same,
4930 SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
4935 return SVN_NO_ERROR;
4938 SVN_ERR(contents_identical_p(&q, file1, file2, pool));
4945 return SVN_NO_ERROR;
4949 svn_io_files_contents_three_same_p(svn_boolean_t *same12,
4950 svn_boolean_t *same23,
4951 svn_boolean_t *same13,
4955 apr_pool_t *scratch_pool)
4957 svn_boolean_t diff_size12, diff_size23, diff_size13;
4959 SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
4967 if (diff_size12 && diff_size23 && diff_size13)
4969 *same12 = *same23 = *same13 = FALSE;
4971 else if (diff_size12 && diff_size23)
4973 *same12 = *same23 = FALSE;
4974 SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
4976 else if (diff_size23 && diff_size13)
4978 *same23 = *same13 = FALSE;
4979 SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
4981 else if (diff_size12 && diff_size13)
4983 *same12 = *same13 = FALSE;
4984 SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
4988 SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
4989 SVN_ERR(contents_three_identical_p(same12, same23, same13,
4990 file1, file2, file3,
4994 return SVN_NO_ERROR;
4998 /* Counter value of file_mktemp request (used in a threadsafe way), to make
4999 sure that a single process normally never generates the same tempname
5001 static volatile apr_uint32_t tempname_counter = 0;
5004 /* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
5005 Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name.
5006 Perform temporary allocations in SCRATCH_POOL and the result in
5008 static svn_error_t *
5009 temp_file_create(apr_file_t **new_file,
5010 const char **new_file_name,
5011 const char *directory,
5013 apr_pool_t *result_pool,
5014 apr_pool_t *scratch_pool)
5017 const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
5018 const char *templ_apr;
5019 apr_status_t status;
5021 SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
5023 /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
5024 data available in POOL and we need a non-const pointer here,
5025 as apr changes the template to return the new filename. */
5026 status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool);
5029 return svn_error_wrap_apr(status, _("Can't create temporary file from "
5030 "template '%s'"), templ);
5032 /* Translate the returned path back to utf-8 before returning it */
5033 return svn_error_trace(svn_path_cstring_to_utf8(new_file_name,
5037 /* The Windows implementation of apr_file_mktemp doesn't handle access
5038 denied errors correctly. Therefore we implement our own temp file
5039 creation function here. */
5041 /* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
5042 ### the function we used before. But we try to guess a more unique
5043 ### name before trying if it exists. */
5045 /* Offset by some time value and a unique request nr to make the number
5046 +- unique for both this process and on the computer */
5047 int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter)
5048 + GetCurrentProcessId();
5051 /* ### Maybe use an iterpool? */
5052 for (i = 0; i <= 99999; i++)
5054 apr_uint32_t unique_nr;
5055 const char *unique_name;
5056 const char *unique_name_apr;
5057 apr_file_t *try_file;
5058 apr_status_t apr_err;
5060 /* Generate a number that should be unique for this application and
5061 usually for the entire computer to reduce the number of cycles
5062 through this loop. (A bit of calculation is much cheaper then
5064 unique_nr = baseNr + 3 * i;
5066 unique_name = svn_dirent_join(directory,
5067 apr_psprintf(scratch_pool, "svn-%X",
5071 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
5073 apr_err = file_open(&try_file, unique_name_apr, flags,
5074 APR_OS_DEFAULT, FALSE, scratch_pool);
5076 if (APR_STATUS_IS_EEXIST(apr_err))
5080 /* On Win32, CreateFile fails with an "Access Denied" error
5081 code, rather than "File Already Exists", if the colliding
5082 name belongs to a directory. */
5084 if (APR_STATUS_IS_EACCES(apr_err))
5087 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
5088 APR_FINFO_TYPE, scratch_pool);
5090 if (!apr_err_2 && finfo.filetype == APR_DIR)
5093 apr_err_2 = APR_TO_OS_ERROR(apr_err);
5095 if (apr_err_2 == ERROR_ACCESS_DENIED ||
5096 apr_err_2 == ERROR_SHARING_VIOLATION)
5098 /* The file is in use by another process or is hidden;
5099 create a new name, but don't do this 99999 times in
5100 case the folder is not writable */
5105 /* Else fall through and return the original error. */
5108 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
5109 svn_dirent_local_style(unique_name,
5114 /* Move file to the right pool */
5115 apr_err = apr_file_setaside(new_file, try_file, result_pool);
5118 return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
5119 svn_dirent_local_style(unique_name,
5122 *new_file_name = apr_pstrdup(result_pool, unique_name);
5124 return SVN_NO_ERROR;
5128 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
5130 _("Unable to make name in '%s'"),
5131 svn_dirent_local_style(directory, scratch_pool));
5135 /* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
5137 svn_io_file_name_get(const char **filename,
5141 const char *fname_apr;
5142 apr_status_t status;
5144 status = apr_file_name_get(&fname_apr, file);
5146 return svn_error_wrap_apr(status, _("Can't get file name"));
5149 SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
5153 return SVN_NO_ERROR;
5158 svn_io_open_unique_file3(apr_file_t **file,
5159 const char **unique_path,
5160 const char *dirpath,
5161 svn_io_file_del_t delete_when,
5162 apr_pool_t *result_pool,
5163 apr_pool_t *scratch_pool)
5165 apr_file_t *tempfile;
5166 const char *tempname;
5167 struct temp_file_cleanup_s *baton = NULL;
5168 apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
5169 APR_BUFFERED | APR_BINARY);
5170 #if !defined(WIN32) && !defined(__OS2__)
5171 apr_fileperms_t perms;
5172 svn_boolean_t using_system_temp_dir = FALSE;
5175 SVN_ERR_ASSERT(file || unique_path);
5179 *unique_path = NULL;
5181 if (dirpath == NULL)
5183 #if !defined(WIN32) && !defined(__OS2__)
5184 using_system_temp_dir = TRUE;
5186 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
5189 switch (delete_when)
5191 case svn_io_file_del_on_pool_cleanup:
5192 baton = apr_palloc(result_pool, sizeof(*baton));
5193 baton->pool = result_pool;
5194 baton->fname_apr = NULL;
5196 /* Because cleanups are run LIFO, we need to make sure to register
5197 our cleanup before the apr_file_close cleanup:
5199 On Windows, you can't remove an open file.
5201 apr_pool_cleanup_register(result_pool, baton,
5202 temp_file_plain_cleanup_handler,
5203 temp_file_child_cleanup_handler);
5206 case svn_io_file_del_on_close:
5207 flags |= APR_DELONCLOSE;
5213 SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
5214 result_pool, scratch_pool));
5216 #if !defined(WIN32) && !defined(__OS2__)
5217 /* apr_file_mktemp() creates files with mode 0600.
5218 * This is appropriate if we're using a system temp dir since we don't
5219 * want to leak sensitive data into temp files other users can read.
5220 * If we're not using a system temp dir we're probably using the
5221 * .svn/tmp area and it's likely that the tempfile will end up being
5222 * copied or renamed into the working copy.
5223 * This would cause working files having mode 0600 while users might
5224 * expect to see 0644 or 0664. So we tweak perms of the tempfile in this
5225 * case, but only if the umask allows it. */
5226 if (!using_system_temp_dir)
5230 SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
5231 err = file_perms_set2(tempfile, perms, scratch_pool);
5234 if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
5235 APR_STATUS_IS_ENOTIMPL(err->apr_err))
5236 svn_error_clear(err);
5239 return svn_error_quick_wrapf(
5240 err, _("Can't set permissions on '%s'"),
5241 svn_dirent_local_style(tempname, scratch_pool));
5250 SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
5253 *unique_path = tempname; /* Was allocated in result_pool */
5256 SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
5258 return SVN_NO_ERROR;
5262 svn_io_file_readline(apr_file_t *file,
5263 svn_stringbuf_t **stringbuf,
5267 apr_pool_t *result_pool,
5268 apr_pool_t *scratch_pool)
5270 svn_stringbuf_t *str;
5271 const char *eol_str;
5272 apr_size_t numbytes;
5275 svn_boolean_t found_eof;
5277 str = svn_stringbuf_create_ensure(80, result_pool);
5279 /* Read bytes into STR up to and including, but not storing,
5280 * the next EOL sequence. */
5288 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
5289 &found_eof, scratch_pool));
5291 if (numbytes != 1 || len > max_len)
5305 if (!found_eof && len < max_len)
5309 /* Check for "\r\n" by peeking at the next byte. */
5311 SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool));
5312 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
5313 &found_eof, scratch_pool));
5314 if (numbytes == 1 && c == '\n')
5321 /* Pretend we never peeked. */
5322 SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
5329 svn_stringbuf_appendbyte(str, c);
5341 return SVN_NO_ERROR;