Merge branch 'fix-warnings'
[TortoiseGit.git] / src / TortoiseMerge / libsvn_diff / io.c
blob36bcf157369c446fb0628113f7e41c28248ee8d3
1 /*
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
20 * under the License.
21 * ====================================================================
26 #include <stdio.h>
28 #ifndef WIN32
29 #include <unistd.h>
30 #endif
32 #ifndef APR_STATUS_IS_EPERM
33 #include <errno.h>
34 #ifdef EPERM
35 #define APR_STATUS_IS_EPERM(s) ((s) == EPERM)
36 #else
37 #define APR_STATUS_IS_EPERM(s) (0)
38 #endif
39 #endif
41 #include <apr_lib.h>
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>
48 #include <apr_md5.h>
50 #ifdef WIN32
51 #include <arch/win32/apr_arch_file_io.h>
52 #endif
54 #include "svn_hash.h"
55 #include "svn_types.h"
56 #include "svn_dirent_uri.h"
57 #include "svn_path.h"
58 #include "svn_string.h"
59 #include "svn_error.h"
60 #include "svn_io.h"
61 #include "svn_pools.h"
62 #include "svn_utf.h"
63 #include "svn_config.h"
64 #include "svn_private_config.h"
65 #include "svn_ctype.h"
67 #include "private/svn_atomic.h"
68 #include "private/svn_io_private.h"
70 #define SVN_SLEEP_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS"
73 Windows is 'aided' by a number of types of applications that
74 follow other applications around and open up files they have
75 changed for various reasons (the most intrusive are virus
76 scanners). So, if one of these other apps has glommed onto
77 our file we may get an 'access denied' error.
79 This retry loop does not completely solve the problem (who
80 knows how long the other app is going to hold onto it for), but
81 goes a long way towards minimizing it. It is not an infinite
82 loop because there might really be an error.
84 Another reason for retrying delete operations on Windows
85 is that they are asynchronous -- the file or directory is not
86 actually deleted until the last handle to it is closed. The
87 retry loop cannot completely solve this problem either, but can
88 help mitigate it.
90 #define RETRY_MAX_ATTEMPTS 100
91 #define RETRY_INITIAL_SLEEP 1000
92 #define RETRY_MAX_SLEEP 128000
94 #define RETRY_LOOP(err, expr, retry_test, sleep_test) \
95 do \
96 { \
97 apr_status_t os_err = APR_TO_OS_ERROR(err); \
98 int sleep_count = RETRY_INITIAL_SLEEP; \
99 int retries; \
100 for (retries = 0; \
101 retries < RETRY_MAX_ATTEMPTS && (retry_test); \
102 os_err = APR_TO_OS_ERROR(err)) \
104 if (sleep_test) \
106 ++retries; \
107 apr_sleep(sleep_count); \
108 if (sleep_count < RETRY_MAX_SLEEP) \
109 sleep_count *= 2; \
111 (err) = (expr); \
114 while (0)
116 #if defined(EDEADLK) && APR_HAS_THREADS
117 #define FILE_LOCK_RETRY_LOOP(err, expr) \
118 RETRY_LOOP(err, \
119 expr, \
120 (APR_STATUS_IS_EINTR(err) || os_err == EDEADLK), \
121 (!APR_STATUS_IS_EINTR(err)))
122 #else
123 #define FILE_LOCK_RETRY_LOOP(err, expr) \
124 RETRY_LOOP(err, \
125 expr, \
126 (APR_STATUS_IS_EINTR(err)), \
128 #endif
130 #ifndef WIN32_RETRY_LOOP
131 #if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP)
132 #define WIN32_RETRY_LOOP(err, expr) \
133 RETRY_LOOP(err, expr, (os_err == ERROR_ACCESS_DENIED \
134 || os_err == ERROR_SHARING_VIOLATION \
135 || os_err == ERROR_DIR_NOT_EMPTY), \
137 #else
138 #define WIN32_RETRY_LOOP(err, expr) ((void)0)
139 #endif
140 #endif
142 /* Forward declaration */
143 static apr_status_t
144 dir_is_empty(const char *dir, apr_pool_t *pool);
145 static APR_INLINE svn_error_t *
146 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
147 const char *msg, const char *msg_no_name,
148 apr_pool_t *pool);
150 /* Local wrapper of svn_path_cstring_to_utf8() that does no copying on
151 * operating systems where APR always uses utf-8 as native path format */
152 static svn_error_t *
153 cstring_to_utf8(const char **path_utf8,
154 const char *path_apr,
155 apr_pool_t *pool)
157 #if defined(WIN32) || defined(DARWIN)
158 *path_utf8 = path_apr;
159 return SVN_NO_ERROR;
160 #else
161 return svn_path_cstring_to_utf8(path_utf8, path_apr, pool);
162 #endif
165 /* Local wrapper of svn_path_cstring_from_utf8() that does no copying on
166 * operating systems where APR always uses utf-8 as native path format */
167 static svn_error_t *
168 cstring_from_utf8(const char **path_apr,
169 const char *path_utf8,
170 apr_pool_t *pool)
172 #if defined(WIN32) || defined(DARWIN)
173 *path_apr = path_utf8;
174 return SVN_NO_ERROR;
175 #else
176 return svn_path_cstring_from_utf8(path_apr, path_utf8, pool);
177 #endif
180 /* Helper function that allows to convert an APR-level PATH to something
181 * that we can pass the svn_error_wrap_apr. Since we use it in context
182 * of error reporting, having *some* path info may be more useful than
183 * having none. Therefore, we use a best effort approach here.
185 * This is different from svn_io_file_name_get in that it uses a different
186 * signature style and will never fail.
188 static const char *
189 try_utf8_from_internal_style(const char *path, apr_pool_t *pool)
191 svn_error_t *error;
192 const char *path_utf8;
194 /* Special case. */
195 if (path == NULL)
196 return "(NULL)";
198 /* (try to) convert PATH to UTF-8. If that fails, continue with the plain
199 * PATH because it is the best we have. It may actually be UTF-8 already.
201 error = cstring_to_utf8(&path_utf8, path, pool);
202 if (error)
204 /* fallback to best representation we have */
206 svn_error_clear(error);
207 path_utf8 = path;
210 /* Toggle (back-)slashes etc. as necessary.
212 return svn_dirent_local_style(path_utf8, pool);
216 /* Set *NAME_P to the UTF-8 representation of directory entry NAME.
217 * NAME is in the internal encoding used by APR; PARENT is in
218 * UTF-8 and in internal (not local) style.
220 * Use PARENT only for generating an error string if the conversion
221 * fails because NAME could not be represented in UTF-8. In that
222 * case, return a two-level error in which the outer error's message
223 * mentions PARENT, but the inner error's message does not mention
224 * NAME (except possibly in hex) since NAME may not be printable.
225 * Such a compound error at least allows the user to go looking in the
226 * right directory for the problem.
228 * If there is any other error, just return that error directly.
230 * If there is any error, the effect on *NAME_P is undefined.
232 * *NAME_P and NAME may refer to the same storage.
234 static svn_error_t *
235 entry_name_to_utf8(const char **name_p,
236 const char *name,
237 const char *parent,
238 apr_pool_t *pool)
240 #if defined(WIN32) || defined(DARWIN)
241 *name_p = apr_pstrdup(pool, name);
242 return SVN_NO_ERROR;
243 #else
244 svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool);
245 if (err && err->apr_err == APR_EINVAL)
247 return svn_error_createf(err->apr_err, err,
248 _("Error converting entry "
249 "in directory '%s' to UTF-8"),
250 svn_dirent_local_style(parent, pool));
252 return err;
253 #endif
258 static void
259 map_apr_finfo_to_node_kind(svn_node_kind_t *kind,
260 svn_boolean_t *is_special,
261 apr_finfo_t *finfo)
263 *is_special = FALSE;
265 if (finfo->filetype == APR_REG)
266 *kind = svn_node_file;
267 else if (finfo->filetype == APR_DIR)
268 *kind = svn_node_dir;
269 else if (finfo->filetype == APR_LNK)
271 *is_special = TRUE;
272 *kind = svn_node_file;
274 else
275 *kind = svn_node_unknown;
278 /* Helper for svn_io_check_path() and svn_io_check_resolved_path();
279 essentially the same semantics as those two, with the obvious
280 interpretation for RESOLVE_SYMLINKS. */
281 static svn_error_t *
282 io_check_path(const char *path,
283 svn_boolean_t resolve_symlinks,
284 svn_boolean_t *is_special_p,
285 svn_node_kind_t *kind,
286 apr_pool_t *pool)
288 apr_int32_t flags;
289 apr_finfo_t finfo;
290 apr_status_t apr_err;
291 const char *path_apr;
292 svn_boolean_t is_special = FALSE;
294 if (path[0] == '\0')
295 path = ".";
297 /* Not using svn_io_stat() here because we want to check the
298 apr_err return explicitly. */
299 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
301 flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK);
302 apr_err = apr_stat(&finfo, path_apr, flags, pool);
304 if (APR_STATUS_IS_ENOENT(apr_err))
305 *kind = svn_node_none;
306 else if (SVN__APR_STATUS_IS_ENOTDIR(apr_err))
307 *kind = svn_node_none;
308 else if (apr_err)
309 return svn_error_wrap_apr(apr_err, _("Can't check path '%s'"),
310 svn_dirent_local_style(path, pool));
311 else
312 map_apr_finfo_to_node_kind(kind, &is_special, &finfo);
314 *is_special_p = is_special;
316 return SVN_NO_ERROR;
320 /* Wrapper for apr_file_open(), taking an APR-encoded filename. */
321 static apr_status_t
322 file_open(apr_file_t **f,
323 const char *fname_apr,
324 apr_int32_t flag,
325 apr_fileperms_t perm,
326 svn_boolean_t retry_on_failure,
327 apr_pool_t *pool)
329 apr_status_t status = apr_file_open(f, fname_apr, flag, perm, pool);
331 if (retry_on_failure)
333 WIN32_RETRY_LOOP(status, apr_file_open(f, fname_apr, flag, perm, pool));
335 return status;
339 svn_error_t *
340 svn_io_check_resolved_path(const char *path,
341 svn_node_kind_t *kind,
342 apr_pool_t *pool)
344 svn_boolean_t ignored;
345 return io_check_path(path, TRUE, &ignored, kind, pool);
348 svn_error_t *
349 svn_io_check_path(const char *path,
350 svn_node_kind_t *kind,
351 apr_pool_t *pool)
353 svn_boolean_t ignored;
354 return io_check_path(path, FALSE, &ignored, kind, pool);
357 svn_error_t *
358 svn_io_check_special_path(const char *path,
359 svn_node_kind_t *kind,
360 svn_boolean_t *is_special,
361 apr_pool_t *pool)
363 return io_check_path(path, FALSE, is_special, kind, pool);
366 struct temp_file_cleanup_s
368 apr_pool_t *pool;
369 /* The (APR-encoded) full path of the file to be removed, or NULL if
370 * nothing to do. */
371 const char *fname_apr;
375 static apr_status_t
376 temp_file_plain_cleanup_handler(void *baton)
378 struct temp_file_cleanup_s *b = baton;
379 apr_status_t apr_err = APR_SUCCESS;
381 if (b->fname_apr)
383 apr_err = apr_file_remove(b->fname_apr, b->pool);
384 WIN32_RETRY_LOOP(apr_err, apr_file_remove(b->fname_apr, b->pool));
387 return apr_err;
391 static apr_status_t
392 temp_file_child_cleanup_handler(void *baton)
394 struct temp_file_cleanup_s *b = baton;
396 apr_pool_cleanup_kill(b->pool, b,
397 temp_file_plain_cleanup_handler);
399 return APR_SUCCESS;
403 svn_error_t *
404 svn_io_open_uniquely_named(apr_file_t **file,
405 const char **unique_path,
406 const char *dirpath,
407 const char *filename,
408 const char *suffix,
409 svn_io_file_del_t delete_when,
410 apr_pool_t *result_pool,
411 apr_pool_t *scratch_pool)
413 const char *path;
414 unsigned int i;
415 struct temp_file_cleanup_s *baton = NULL;
417 /* At the beginning, we don't know whether unique_path will need
418 UTF8 conversion */
419 svn_boolean_t needs_utf8_conversion = TRUE;
421 SVN_ERR_ASSERT(file || unique_path);
423 if (dirpath == NULL)
424 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
425 if (filename == NULL)
426 filename = "tempfile";
427 if (suffix == NULL)
428 suffix = ".tmp";
430 path = svn_dirent_join(dirpath, filename, scratch_pool);
432 if (delete_when == svn_io_file_del_on_pool_cleanup)
434 baton = apr_palloc(result_pool, sizeof(*baton));
436 baton->pool = result_pool;
437 baton->fname_apr = NULL;
439 /* Because cleanups are run LIFO, we need to make sure to register
440 our cleanup before the apr_file_close cleanup:
442 On Windows, you can't remove an open file.
444 apr_pool_cleanup_register(result_pool, baton,
445 temp_file_plain_cleanup_handler,
446 temp_file_child_cleanup_handler);
449 for (i = 1; i <= 99999; i++)
451 const char *unique_name;
452 const char *unique_name_apr;
453 apr_file_t *try_file;
454 apr_status_t apr_err;
455 apr_int32_t flag = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL
456 | APR_BUFFERED | APR_BINARY);
458 if (delete_when == svn_io_file_del_on_close)
459 flag |= APR_DELONCLOSE;
461 /* Special case the first attempt -- if we can avoid having a
462 generated numeric portion at all, that's best. So first we
463 try with just the suffix; then future tries add a number
464 before the suffix. (A do-while loop could avoid the repeated
465 conditional, but it's not worth the clarity loss.)
467 If the first attempt fails, the first number will be "2".
468 This is good, since "1" would misleadingly imply that
469 the second attempt was actually the first... and if someone's
470 got conflicts on their conflicts, we probably don't want to
471 add to their confusion :-). */
472 if (i == 1)
473 unique_name = apr_psprintf(scratch_pool, "%s%s", path, suffix);
474 else
475 unique_name = apr_psprintf(scratch_pool, "%s.%u%s", path, i, suffix);
477 /* Hmmm. Ideally, we would append to a native-encoding buf
478 before starting iteration, then convert back to UTF-8 for
479 return. But I suppose that would make the appending code
480 sensitive to i18n in a way it shouldn't be... Oh well. */
481 if (needs_utf8_conversion)
483 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name,
484 scratch_pool));
485 if (i == 1)
487 /* The variable parts of unique_name will not require UTF8
488 conversion. Therefore, if UTF8 conversion had no effect
489 on it in the first iteration, it won't require conversion
490 in any future iteration. */
491 needs_utf8_conversion = strcmp(unique_name_apr, unique_name);
494 else
495 unique_name_apr = unique_name;
497 apr_err = file_open(&try_file, unique_name_apr, flag,
498 APR_OS_DEFAULT, FALSE, result_pool);
500 if (APR_STATUS_IS_EEXIST(apr_err))
501 continue;
502 else if (apr_err)
504 /* On Win32, CreateFile fails with an "Access Denied" error
505 code, rather than "File Already Exists", if the colliding
506 name belongs to a directory. */
507 if (APR_STATUS_IS_EACCES(apr_err))
509 apr_finfo_t finfo;
510 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
511 APR_FINFO_TYPE, scratch_pool);
513 if (!apr_err_2 && finfo.filetype == APR_DIR)
514 continue;
516 #ifdef WIN32
517 apr_err_2 = APR_TO_OS_ERROR(apr_err);
519 if (apr_err_2 == ERROR_ACCESS_DENIED ||
520 apr_err_2 == ERROR_SHARING_VIOLATION)
522 /* The file is in use by another process or is hidden;
523 create a new name, but don't do this 99999 times in
524 case the folder is not writable */
525 i += 797;
526 continue;
528 #endif
530 /* Else fall through and return the original error. */
533 if (file)
534 *file = NULL;
535 if (unique_path)
536 *unique_path = NULL;
537 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
538 svn_dirent_local_style(unique_name,
539 scratch_pool));
541 else
543 if (delete_when == svn_io_file_del_on_pool_cleanup)
544 baton->fname_apr = apr_pstrdup(result_pool, unique_name_apr);
546 if (file)
547 *file = try_file;
548 else
549 apr_file_close(try_file);
550 if (unique_path)
551 *unique_path = apr_pstrdup(result_pool, unique_name);
553 return SVN_NO_ERROR;
557 if (file)
558 *file = NULL;
559 if (unique_path)
560 *unique_path = NULL;
561 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
562 NULL,
563 _("Unable to make name for '%s'"),
564 svn_dirent_local_style(path, scratch_pool));
567 svn_error_t *
568 svn_io_create_unique_link(const char **unique_name_p,
569 const char *path,
570 const char *dest,
571 const char *suffix,
572 apr_pool_t *pool)
574 #ifdef HAVE_SYMLINK
575 unsigned int i;
576 const char *unique_name;
577 const char *unique_name_apr;
578 const char *dest_apr;
579 int rv;
581 SVN_ERR(cstring_from_utf8(&dest_apr, dest, pool));
582 for (i = 1; i <= 99999; i++)
584 apr_status_t apr_err;
586 /* Special case the first attempt -- if we can avoid having a
587 generated numeric portion at all, that's best. So first we
588 try with just the suffix; then future tries add a number
589 before the suffix. (A do-while loop could avoid the repeated
590 conditional, but it's not worth the clarity loss.)
592 If the first attempt fails, the first number will be "2".
593 This is good, since "1" would misleadingly imply that
594 the second attempt was actually the first... and if someone's
595 got conflicts on their conflicts, we probably don't want to
596 add to their confusion :-). */
597 if (i == 1)
598 unique_name = apr_psprintf(pool, "%s%s", path, suffix);
599 else
600 unique_name = apr_psprintf(pool, "%s.%u%s", path, i, suffix);
602 /* Hmmm. Ideally, we would append to a native-encoding buf
603 before starting iteration, then convert back to UTF-8 for
604 return. But I suppose that would make the appending code
605 sensitive to i18n in a way it shouldn't be... Oh well. */
606 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, pool));
607 do {
608 rv = symlink(dest_apr, unique_name_apr);
609 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
611 apr_err = apr_get_os_error();
613 if (rv == -1 && APR_STATUS_IS_EEXIST(apr_err))
614 continue;
615 else if (rv == -1 && apr_err)
617 /* On Win32, CreateFile fails with an "Access Denied" error
618 code, rather than "File Already Exists", if the colliding
619 name belongs to a directory. */
620 if (APR_STATUS_IS_EACCES(apr_err))
622 apr_finfo_t finfo;
623 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
624 APR_FINFO_TYPE, pool);
626 if (!apr_err_2
627 && (finfo.filetype == APR_DIR))
628 continue;
630 /* Else ignore apr_err_2; better to fall through and
631 return the original error. */
634 *unique_name_p = NULL;
635 return svn_error_wrap_apr(apr_err,
636 _("Can't create symbolic link '%s'"),
637 svn_dirent_local_style(unique_name, pool));
639 else
641 *unique_name_p = unique_name;
642 return SVN_NO_ERROR;
646 *unique_name_p = NULL;
647 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
648 NULL,
649 _("Unable to make name for '%s'"),
650 svn_dirent_local_style(path, pool));
651 #else
652 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
653 _("Symbolic links are not supported on this "
654 "platform"));
655 #endif
658 svn_error_t *
659 svn_io_read_link(svn_string_t **dest,
660 const char *path,
661 apr_pool_t *pool)
663 #ifdef HAVE_READLINK
664 svn_string_t dest_apr;
665 const char *path_apr;
666 char buf[1025];
667 ssize_t rv;
669 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
670 do {
671 rv = readlink(path_apr, buf, sizeof(buf) - 1);
672 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
674 if (rv == -1)
675 return svn_error_wrap_apr(apr_get_os_error(),
676 _("Can't read contents of link"));
678 buf[rv] = '\0';
679 dest_apr.data = buf;
680 dest_apr.len = rv;
682 /* ### Cast needed, one of these interfaces is wrong */
683 return svn_utf_string_to_utf8((const svn_string_t **)dest, &dest_apr, pool);
684 #else
685 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
686 _("Symbolic links are not supported on this "
687 "platform"));
688 #endif
692 svn_error_t *
693 svn_io_copy_link(const char *src,
694 const char *dst,
695 apr_pool_t *pool)
698 #ifdef HAVE_READLINK
699 svn_string_t *link_dest;
700 const char *dst_tmp;
702 /* Notice what the link is pointing at... */
703 SVN_ERR(svn_io_read_link(&link_dest, src, pool));
705 /* Make a tmp-link pointing at the same thing. */
706 SVN_ERR(svn_io_create_unique_link(&dst_tmp, dst, link_dest->data,
707 ".tmp", pool));
709 /* Move the tmp-link to link. */
710 return svn_io_file_rename(dst_tmp, dst, pool);
712 #else
713 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
714 _("Symbolic links are not supported on this "
715 "platform"));
716 #endif
719 /* Temporary directory name cache for svn_io_temp_dir() */
720 static volatile svn_atomic_t temp_dir_init_state = 0;
721 static const char *temp_dir;
723 /* Helper function to initialize temp dir. Passed to svn_atomic__init_once */
724 static svn_error_t *
725 init_temp_dir(void *baton, apr_pool_t *scratch_pool)
727 /* Global pool for the temp path */
728 apr_pool_t *global_pool = svn_pool_create(NULL);
729 const char *dir;
731 apr_status_t apr_err = apr_temp_dir_get(&dir, scratch_pool);
733 if (apr_err)
734 return svn_error_wrap_apr(apr_err, _("Can't find a temporary directory"));
736 SVN_ERR(cstring_to_utf8(&dir, dir, scratch_pool));
738 dir = svn_dirent_internal_style(dir, scratch_pool);
740 SVN_ERR(svn_dirent_get_absolute(&temp_dir, dir, global_pool));
742 return SVN_NO_ERROR;
746 svn_error_t *
747 svn_io_temp_dir(const char **dir,
748 apr_pool_t *pool)
750 SVN_ERR(svn_atomic__init_once(&temp_dir_init_state,
751 init_temp_dir, NULL, pool));
753 *dir = apr_pstrdup(pool, temp_dir);
755 return SVN_NO_ERROR;
761 /*** Creating, copying and appending files. ***/
763 /* Transfer the contents of FROM_FILE to TO_FILE, using POOL for temporary
764 * allocations.
766 * NOTE: We don't use apr_copy_file() for this, since it takes filenames
767 * as parameters. Since we want to copy to a temporary file
768 * and rename for atomicity (see below), this would require an extra
769 * close/open pair, which can be expensive, especially on
770 * remote file systems.
772 static apr_status_t
773 copy_contents(apr_file_t *from_file,
774 apr_file_t *to_file,
775 apr_pool_t *pool)
777 /* Copy bytes till the cows come home. */
778 while (1)
780 char buf[SVN__STREAM_CHUNK_SIZE];
781 apr_size_t bytes_this_time = sizeof(buf);
782 apr_status_t read_err;
783 apr_status_t write_err;
785 /* Read 'em. */
786 read_err = apr_file_read(from_file, buf, &bytes_this_time);
787 if (read_err && !APR_STATUS_IS_EOF(read_err))
789 return read_err;
792 /* Write 'em. */
793 write_err = apr_file_write_full(to_file, buf, bytes_this_time, NULL);
794 if (write_err)
796 return write_err;
799 if (read_err && APR_STATUS_IS_EOF(read_err))
801 /* Return the results of this close: an error, or success. */
802 return APR_SUCCESS;
805 /* NOTREACHED */
809 svn_error_t *
810 svn_io_copy_file(const char *src,
811 const char *dst,
812 svn_boolean_t copy_perms,
813 apr_pool_t *pool)
815 apr_file_t *from_file, *to_file;
816 apr_status_t apr_err;
817 const char *dst_tmp;
818 svn_error_t *err;
820 /* ### NOTE: sometimes src == dst. In this case, because we copy to a
821 ### temporary file, and then rename over the top of the destination,
822 ### the net result is resetting the permissions on src/dst.
824 ### Note: specifically, this can happen during a switch when the desired
825 ### permissions for a file change from one branch to another. See
826 ### switch_tests 17.
828 ### ... yes, we should avoid copying to the same file, and we should
829 ### make the "reset perms" explicit. The switch *happens* to work
830 ### because of this copy-to-temp-then-rename implementation. If it
831 ### weren't for that, the switch would break.
833 #ifdef CHECK_FOR_SAME_FILE
834 if (strcmp(src, dst) == 0)
835 return SVN_NO_ERROR;
836 #endif
838 SVN_ERR(svn_io_file_open(&from_file, src, APR_READ,
839 APR_OS_DEFAULT, pool));
841 /* For atomicity, we copy to a tmp file and then rename the tmp
842 file over the real destination. */
844 SVN_ERR(svn_io_open_unique_file3(&to_file, &dst_tmp,
845 svn_dirent_dirname(dst, pool),
846 svn_io_file_del_none, pool, pool));
848 apr_err = copy_contents(from_file, to_file, pool);
850 if (apr_err)
852 err = svn_error_wrap_apr(apr_err, _("Can't copy '%s' to '%s'"),
853 svn_dirent_local_style(src, pool),
854 svn_dirent_local_style(dst_tmp, pool));
856 else
857 err = NULL;
859 err = svn_error_compose_create(err,
860 svn_io_file_close(from_file, pool));
862 err = svn_error_compose_create(err,
863 svn_io_file_close(to_file, pool));
865 if (err)
867 return svn_error_compose_create(
868 err,
869 svn_io_remove_file2(dst_tmp, TRUE, pool));
872 /* If copying perms, set the perms on dst_tmp now, so they will be
873 atomically inherited in the upcoming rename. But note that we
874 had to wait until now to set perms, because if they say
875 read-only, then we'd have failed filling dst_tmp's contents. */
876 if (copy_perms)
877 SVN_ERR(svn_io_copy_perms(src, dst_tmp, pool));
879 return svn_error_trace(svn_io_file_rename(dst_tmp, dst, pool));
882 #if !defined(WIN32) && !defined(__OS2__)
883 /* Wrapper for apr_file_perms_set(), taking a UTF8-encoded filename. */
884 static svn_error_t *
885 file_perms_set(const char *fname, apr_fileperms_t perms,
886 apr_pool_t *pool)
888 const char *fname_apr;
889 apr_status_t status;
891 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
893 status = apr_file_perms_set(fname_apr, perms);
894 if (status)
895 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
896 fname);
897 else
898 return SVN_NO_ERROR;
901 /* Set permissions PERMS on the FILE. This is a cheaper variant of the
902 * file_perms_set wrapper() function because no locale-dependent string
903 * conversion is required. POOL will be used for allocations.
905 static svn_error_t *
906 file_perms_set2(apr_file_t* file, apr_fileperms_t perms, apr_pool_t *pool)
908 const char *fname_apr;
909 apr_status_t status;
911 status = apr_file_name_get(&fname_apr, file);
912 if (status)
913 return svn_error_wrap_apr(status, _("Can't get file name"));
915 status = apr_file_perms_set(fname_apr, perms);
916 if (status)
917 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
918 try_utf8_from_internal_style(fname_apr, pool));
919 else
920 return SVN_NO_ERROR;
923 #endif /* !WIN32 && !__OS2__ */
925 svn_error_t *
926 svn_io_copy_perms(const char *src,
927 const char *dst,
928 apr_pool_t *pool)
930 /* ### On Windows or OS/2, apr_file_perms_set always returns APR_ENOTIMPL,
931 and the path passed to apr_file_perms_set must be encoded
932 in the platform-specific path encoding; not necessary UTF-8.
933 We need a platform-specific implementation to get the
934 permissions right. */
936 #if !defined(WIN32) && !defined(__OS2__)
938 apr_finfo_t finfo;
939 svn_node_kind_t kind;
940 svn_boolean_t is_special;
941 svn_error_t *err;
943 /* If DST is a symlink, don't bother copying permissions. */
944 SVN_ERR(svn_io_check_special_path(dst, &kind, &is_special, pool));
945 if (is_special)
946 return SVN_NO_ERROR;
948 SVN_ERR(svn_io_stat(&finfo, src, APR_FINFO_PROT, pool));
949 err = file_perms_set(dst, finfo.protection, pool);
950 if (err)
952 /* We shouldn't be able to get APR_INCOMPLETE or APR_ENOTIMPL
953 here under normal circumstances, because the perms themselves
954 came from a call to apr_file_info_get(), and we already know
955 this is the non-Win32 case. But if it does happen, it's not
956 an error. */
957 if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
958 APR_STATUS_IS_ENOTIMPL(err->apr_err))
959 svn_error_clear(err);
960 else
962 const char *message;
963 message = apr_psprintf(pool, _("Can't set permissions on '%s'"),
964 svn_dirent_local_style(dst, pool));
965 return svn_error_quick_wrap(err, message);
969 #endif /* !WIN32 && !__OS2__ */
971 return SVN_NO_ERROR;
975 svn_error_t *
976 svn_io_append_file(const char *src, const char *dst, apr_pool_t *pool)
978 apr_status_t apr_err;
979 const char *src_apr, *dst_apr;
981 SVN_ERR(cstring_from_utf8(&src_apr, src, pool));
982 SVN_ERR(cstring_from_utf8(&dst_apr, dst, pool));
984 apr_err = apr_file_append(src_apr, dst_apr, APR_OS_DEFAULT, pool);
986 if (apr_err)
987 return svn_error_wrap_apr(apr_err, _("Can't append '%s' to '%s'"),
988 svn_dirent_local_style(src, pool),
989 svn_dirent_local_style(dst, pool));
991 return SVN_NO_ERROR;
995 svn_error_t *svn_io_copy_dir_recursively(const char *src,
996 const char *dst_parent,
997 const char *dst_basename,
998 svn_boolean_t copy_perms,
999 svn_cancel_func_t cancel_func,
1000 void *cancel_baton,
1001 apr_pool_t *pool)
1003 svn_node_kind_t kind;
1004 apr_status_t status;
1005 const char *dst_path;
1006 apr_dir_t *this_dir;
1007 apr_finfo_t this_entry;
1008 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
1010 /* Make a subpool for recursion */
1011 apr_pool_t *subpool = svn_pool_create(pool);
1013 /* The 'dst_path' is simply dst_parent/dst_basename */
1014 dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
1016 /* Sanity checks: SRC and DST_PARENT are directories, and
1017 DST_BASENAME doesn't already exist in DST_PARENT. */
1018 SVN_ERR(svn_io_check_path(src, &kind, subpool));
1019 if (kind != svn_node_dir)
1020 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1021 _("Source '%s' is not a directory"),
1022 svn_dirent_local_style(src, pool));
1024 SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool));
1025 if (kind != svn_node_dir)
1026 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1027 _("Destination '%s' is not a directory"),
1028 svn_dirent_local_style(dst_parent, pool));
1030 SVN_ERR(svn_io_check_path(dst_path, &kind, subpool));
1031 if (kind != svn_node_none)
1032 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
1033 _("Destination '%s' already exists"),
1034 svn_dirent_local_style(dst_path, pool));
1036 /* Create the new directory. */
1037 /* ### TODO: copy permissions (needs apr_file_attrs_get()) */
1038 SVN_ERR(svn_io_dir_make(dst_path, APR_OS_DEFAULT, pool));
1040 /* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */
1041 SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
1043 for (status = apr_dir_read(&this_entry, flags, this_dir);
1044 status == APR_SUCCESS;
1045 status = apr_dir_read(&this_entry, flags, this_dir))
1047 if ((this_entry.name[0] == '.')
1048 && ((this_entry.name[1] == '\0')
1049 || ((this_entry.name[1] == '.')
1050 && (this_entry.name[2] == '\0'))))
1052 continue;
1054 else
1056 const char *src_target, *entryname_utf8;
1058 if (cancel_func)
1059 SVN_ERR(cancel_func(cancel_baton));
1061 SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
1062 src, subpool));
1063 src_target = svn_dirent_join(src, entryname_utf8, subpool);
1065 if (this_entry.filetype == APR_REG) /* regular file */
1067 const char *dst_target = svn_dirent_join(dst_path,
1068 entryname_utf8,
1069 subpool);
1070 SVN_ERR(svn_io_copy_file(src_target, dst_target,
1071 copy_perms, subpool));
1073 else if (this_entry.filetype == APR_LNK) /* symlink */
1075 const char *dst_target = svn_dirent_join(dst_path,
1076 entryname_utf8,
1077 subpool);
1078 SVN_ERR(svn_io_copy_link(src_target, dst_target,
1079 subpool));
1081 else if (this_entry.filetype == APR_DIR) /* recurse */
1083 /* Prevent infinite recursion by filtering off our
1084 newly created destination path. */
1085 if (strcmp(src, dst_parent) == 0
1086 && strcmp(entryname_utf8, dst_basename) == 0)
1087 continue;
1089 SVN_ERR(svn_io_copy_dir_recursively
1090 (src_target,
1091 dst_path,
1092 entryname_utf8,
1093 copy_perms,
1094 cancel_func,
1095 cancel_baton,
1096 subpool));
1098 /* ### support other APR node types someday?? */
1103 if (! (APR_STATUS_IS_ENOENT(status)))
1104 return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
1105 svn_dirent_local_style(src, pool));
1107 status = apr_dir_close(this_dir);
1108 if (status)
1109 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
1110 svn_dirent_local_style(src, pool));
1112 /* Free any memory used by recursion */
1113 svn_pool_destroy(subpool);
1115 return SVN_NO_ERROR;
1119 svn_error_t *
1120 svn_io_make_dir_recursively(const char *path, apr_pool_t *pool)
1122 const char *path_apr;
1123 apr_status_t apr_err;
1125 if (svn_path_is_empty(path))
1126 /* Empty path (current dir) is assumed to always exist,
1127 so we do nothing, per docs. */
1128 return SVN_NO_ERROR;
1130 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1132 apr_err = apr_dir_make_recursive(path_apr, APR_OS_DEFAULT, pool);
1133 WIN32_RETRY_LOOP(apr_err, apr_dir_make_recursive(path_apr,
1134 APR_OS_DEFAULT, pool));
1136 if (apr_err)
1137 return svn_error_wrap_apr(apr_err, _("Can't make directory '%s'"),
1138 svn_dirent_local_style(path, pool));
1140 return SVN_NO_ERROR;
1143 svn_error_t *svn_io_file_create(const char *file,
1144 const char *contents,
1145 apr_pool_t *pool)
1147 apr_file_t *f;
1148 apr_size_t written;
1149 svn_error_t *err = SVN_NO_ERROR;
1151 SVN_ERR(svn_io_file_open(&f, file,
1152 (APR_WRITE | APR_CREATE | APR_EXCL),
1153 APR_OS_DEFAULT,
1154 pool));
1155 if (contents && *contents)
1156 err = svn_io_file_write_full(f, contents, strlen(contents),
1157 &written, pool);
1160 return svn_error_trace(
1161 svn_error_compose_create(err,
1162 svn_io_file_close(f, pool)));
1165 svn_error_t *svn_io_dir_file_copy(const char *src_path,
1166 const char *dest_path,
1167 const char *file,
1168 apr_pool_t *pool)
1170 const char *file_dest_path = svn_dirent_join(dest_path, file, pool);
1171 const char *file_src_path = svn_dirent_join(src_path, file, pool);
1173 return svn_io_copy_file(file_src_path, file_dest_path, TRUE, pool);
1177 /*** Modtime checking. ***/
1179 svn_error_t *
1180 svn_io_file_affected_time(apr_time_t *apr_time,
1181 const char *path,
1182 apr_pool_t *pool)
1184 apr_finfo_t finfo;
1186 SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_MIN | APR_FINFO_LINK, pool));
1188 *apr_time = finfo.mtime;
1190 return SVN_NO_ERROR;
1194 svn_error_t *
1195 svn_io_set_file_affected_time(apr_time_t apr_time,
1196 const char *path,
1197 apr_pool_t *pool)
1199 apr_status_t status;
1200 const char *native_path;
1202 SVN_ERR(cstring_from_utf8(&native_path, path, pool));
1203 status = apr_file_mtime_set(native_path, apr_time, pool);
1205 if (status)
1206 return svn_error_wrap_apr(status, _("Can't set access time of '%s'"),
1207 svn_dirent_local_style(path, pool));
1209 return SVN_NO_ERROR;
1213 void
1214 svn_io_sleep_for_timestamps(const char *path, apr_pool_t *pool)
1216 apr_time_t now, then;
1217 svn_error_t *err;
1218 char *sleep_env_var;
1220 sleep_env_var = getenv(SVN_SLEEP_ENV_VAR);
1222 if (sleep_env_var && apr_strnatcasecmp(sleep_env_var, "yes") == 0)
1223 return; /* Allow skipping for testing */
1225 now = apr_time_now();
1227 /* Calculate 0.02 seconds after the next second wallclock tick. */
1228 then = apr_time_make(apr_time_sec(now) + 1, APR_USEC_PER_SEC / 50);
1230 /* Worst case is waiting one second, so we can use that time to determine
1231 if we can sleep shorter than that */
1232 if (path)
1234 apr_finfo_t finfo;
1236 err = svn_io_stat(&finfo, path, APR_FINFO_MTIME | APR_FINFO_LINK, pool);
1238 if (err)
1240 svn_error_clear(err); /* Fall back on original behavior */
1242 else if (finfo.mtime % APR_USEC_PER_SEC)
1244 /* Very simplistic but safe approach:
1245 If the filesystem has < sec mtime we can be reasonably sure
1246 that the filesystem has <= millisecond precision.
1248 ## Perhaps find a better algorithm here. This will fail once
1249 in every 1000 cases on a millisecond precision filesystem.
1251 But better to fail once in every thousand cases than every
1252 time, like we did before.
1253 (All tested filesystems I know have at least microsecond precision.)
1255 Note for further research on algorithm:
1256 FAT32 has < 1 sec precision on ctime, but 2 sec on mtime */
1258 /* Sleep for at least 1 millisecond.
1259 (t < 1000 will be round to 0 in apr) */
1260 apr_sleep(1000);
1262 return;
1265 now = apr_time_now(); /* Extract the time used for the path stat */
1267 if (now >= then)
1268 return; /* Passing negative values may suspend indefinitely (Windows) */
1271 apr_sleep(then - now);
1275 svn_error_t *
1276 svn_io_filesizes_different_p(svn_boolean_t *different_p,
1277 const char *file1,
1278 const char *file2,
1279 apr_pool_t *pool)
1281 apr_finfo_t finfo1;
1282 apr_finfo_t finfo2;
1283 apr_status_t status;
1284 const char *file1_apr, *file2_apr;
1286 /* Not using svn_io_stat() because don't want to generate
1287 svn_error_t objects for non-error conditions. */
1289 SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool));
1290 SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool));
1292 /* Stat both files */
1293 status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool);
1294 if (status)
1296 /* If we got an error stat'ing a file, it could be because the
1297 file was removed... or who knows. Whatever the case, we
1298 don't know if the filesizes are definitely different, so
1299 assume that they're not. */
1300 *different_p = FALSE;
1301 return SVN_NO_ERROR;
1304 status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool);
1305 if (status)
1307 /* See previous comment. */
1308 *different_p = FALSE;
1309 return SVN_NO_ERROR;
1312 /* Examine file sizes */
1313 if (finfo1.size == finfo2.size)
1314 *different_p = FALSE;
1315 else
1316 *different_p = TRUE;
1318 return SVN_NO_ERROR;
1322 svn_error_t *
1323 svn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
1324 svn_boolean_t *different_p23,
1325 svn_boolean_t *different_p13,
1326 const char *file1,
1327 const char *file2,
1328 const char *file3,
1329 apr_pool_t *scratch_pool)
1331 apr_finfo_t finfo1, finfo2, finfo3;
1332 apr_status_t status1, status2, status3;
1333 const char *file1_apr, *file2_apr, *file3_apr;
1335 /* Not using svn_io_stat() because don't want to generate
1336 svn_error_t objects for non-error conditions. */
1338 SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool));
1339 SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool));
1340 SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool));
1342 /* Stat all three files */
1343 status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool);
1344 status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool);
1345 status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool);
1347 /* If we got an error stat'ing a file, it could be because the
1348 file was removed... or who knows. Whatever the case, we
1349 don't know if the filesizes are definitely different, so
1350 assume that they're not. */
1351 *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size;
1352 *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size;
1353 *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size;
1355 return SVN_NO_ERROR;
1359 svn_error_t *
1360 svn_io_file_checksum2(svn_checksum_t **checksum,
1361 const char *file,
1362 svn_checksum_kind_t kind,
1363 apr_pool_t *pool)
1365 svn_stream_t *file_stream;
1366 svn_stream_t *checksum_stream;
1367 apr_file_t* f;
1369 SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool));
1370 file_stream = svn_stream_from_aprfile2(f, FALSE, pool);
1371 checksum_stream = svn_stream_checksummed2(file_stream, checksum, NULL, kind,
1372 TRUE, pool);
1374 /* Because the checksummed stream will force the reading (and
1375 checksumming) of all the file's bytes, we can just close the stream
1376 and let its magic work. */
1377 return svn_stream_close(checksum_stream);
1381 svn_error_t *
1382 svn_io_file_checksum(unsigned char digest[],
1383 const char *file,
1384 apr_pool_t *pool)
1386 svn_checksum_t *checksum;
1388 SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool));
1389 memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE);
1391 return SVN_NO_ERROR;
1396 /*** Permissions and modes. ***/
1398 #if !defined(WIN32) && !defined(__OS2__)
1399 /* Given the file specified by PATH, attempt to create an
1400 identical version of it owned by the current user. This is done by
1401 moving it to a temporary location, copying the file back to its old
1402 path, then deleting the temporarily moved version. All temporary
1403 allocations are done in POOL. */
1404 static svn_error_t *
1405 reown_file(const char *path,
1406 apr_pool_t *pool)
1408 const char *unique_name;
1410 SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name,
1411 svn_dirent_dirname(path, pool),
1412 svn_io_file_del_none, pool, pool));
1413 SVN_ERR(svn_io_file_rename(path, unique_name, pool));
1414 SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool));
1415 return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool));
1418 /* Determine what the PERMS for a new file should be by looking at the
1419 permissions of a temporary file that we create.
1420 Unfortunately, umask() as defined in POSIX provides no thread-safe way
1421 to get at the current value of the umask, so what we're doing here is
1422 the only way we have to determine which combination of write bits
1423 (User/Group/World) should be set by default.
1424 Make temporary allocations in SCRATCH_POOL. */
1425 static svn_error_t *
1426 get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool)
1428 /* the default permissions as read from the temp folder */
1429 static apr_fileperms_t default_perms = 0;
1431 /* Technically, this "racy": Multiple threads may use enter here and
1432 try to figure out the default permission concurrently. That's fine
1433 since they will end up with the same results. Even more technical,
1434 apr_fileperms_t is an atomic type on 32+ bit machines.
1436 if (default_perms == 0)
1438 apr_finfo_t finfo;
1439 apr_file_t *fd;
1440 const char *fname_base, *fname;
1441 apr_uint32_t randomish;
1442 svn_error_t *err;
1444 /* Get the perms for a newly created file to find out what bits
1445 should be set.
1447 Explictly delete the file because we want this file to be as
1448 short-lived as possible since its presence means other
1449 processes may have to try multiple names.
1451 Using svn_io_open_uniquely_named() here because other tempfile
1452 creation functions tweak the permission bits of files they create.
1454 randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool
1455 + (apr_uint32_t)apr_time_now());
1456 fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish);
1458 SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, NULL, fname_base,
1459 NULL, svn_io_file_del_none,
1460 scratch_pool, scratch_pool));
1461 err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool);
1462 err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool));
1463 err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE,
1464 scratch_pool));
1465 SVN_ERR(err);
1466 *perms = finfo.protection;
1467 default_perms = finfo.protection;
1469 else
1470 *perms = default_perms;
1472 return SVN_NO_ERROR;
1475 /* OR together permission bits of the file FD and the default permissions
1476 of a file as determined by get_default_file_perms(). Do temporary
1477 allocations in SCRATCH_POOL. */
1478 static svn_error_t *
1479 merge_default_file_perms(apr_file_t *fd, apr_fileperms_t *perms,
1480 apr_pool_t *scratch_pool)
1482 apr_finfo_t finfo;
1483 apr_fileperms_t default_perms;
1485 SVN_ERR(get_default_file_perms(&default_perms, scratch_pool));
1486 SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool));
1488 /* Glom the perms together. */
1489 *perms = default_perms | finfo.protection;
1490 return SVN_NO_ERROR;
1493 /* This is a helper function for the svn_io_set_file_read* functions
1494 that attempts to honor the users umask when dealing with
1495 permission changes. It is a no-op when invoked on a symlink. */
1496 static svn_error_t *
1497 io_set_file_perms(const char *path,
1498 svn_boolean_t change_readwrite,
1499 svn_boolean_t enable_write,
1500 svn_boolean_t change_executable,
1501 svn_boolean_t executable,
1502 svn_boolean_t ignore_enoent,
1503 apr_pool_t *pool)
1505 apr_status_t status;
1506 const char *path_apr;
1507 apr_finfo_t finfo;
1508 apr_fileperms_t perms_to_set;
1510 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1512 /* Try to change only a minimal amount of the perms first
1513 by getting the current perms and adding bits
1514 only on where read perms are granted. If this fails
1515 fall through to just setting file attributes. */
1516 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool);
1517 if (status)
1519 if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1520 return SVN_NO_ERROR;
1521 else if (status != APR_ENOTIMPL)
1522 return svn_error_wrap_apr(status,
1523 _("Can't change perms of file '%s'"),
1524 svn_dirent_local_style(path, pool));
1525 return SVN_NO_ERROR;
1528 if (finfo.filetype == APR_LNK)
1529 return SVN_NO_ERROR;
1531 perms_to_set = finfo.protection;
1532 if (change_readwrite)
1534 if (enable_write) /* Make read-write. */
1536 apr_file_t *fd;
1538 /* Get the perms for the original file so we'll have any other bits
1539 * that were already set (like the execute bits, for example). */
1540 SVN_ERR(svn_io_file_open(&fd, path, APR_READ,
1541 APR_OS_DEFAULT, pool));
1542 SVN_ERR(merge_default_file_perms(fd, &perms_to_set, pool));
1543 SVN_ERR(svn_io_file_close(fd, pool));
1545 else
1547 if (finfo.protection & APR_UREAD)
1548 perms_to_set &= ~APR_UWRITE;
1549 if (finfo.protection & APR_GREAD)
1550 perms_to_set &= ~APR_GWRITE;
1551 if (finfo.protection & APR_WREAD)
1552 perms_to_set &= ~APR_WWRITE;
1556 if (change_executable)
1558 if (executable)
1560 if (finfo.protection & APR_UREAD)
1561 perms_to_set |= APR_UEXECUTE;
1562 if (finfo.protection & APR_GREAD)
1563 perms_to_set |= APR_GEXECUTE;
1564 if (finfo.protection & APR_WREAD)
1565 perms_to_set |= APR_WEXECUTE;
1567 else
1569 if (finfo.protection & APR_UREAD)
1570 perms_to_set &= ~APR_UEXECUTE;
1571 if (finfo.protection & APR_GREAD)
1572 perms_to_set &= ~APR_GEXECUTE;
1573 if (finfo.protection & APR_WREAD)
1574 perms_to_set &= ~APR_WEXECUTE;
1578 /* If we aren't changing anything then just return, this saves
1579 some system calls and helps with shared working copies */
1580 if (perms_to_set == finfo.protection)
1581 return SVN_NO_ERROR;
1583 status = apr_file_perms_set(path_apr, perms_to_set);
1584 if (!status)
1585 return SVN_NO_ERROR;
1587 if (APR_STATUS_IS_EPERM(status))
1589 /* We don't have permissions to change the
1590 permissions! Try a move, copy, and delete
1591 workaround to see if we can get the file owned by
1592 us. If these succeed, try the permissions set
1593 again.
1595 Note that we only attempt this in the
1596 stat-available path. This assumes that the
1597 move-copy workaround will only be helpful on
1598 platforms that implement apr_stat. */
1599 SVN_ERR(reown_file(path, pool));
1600 status = apr_file_perms_set(path_apr, perms_to_set);
1603 if (!status)
1604 return SVN_NO_ERROR;
1606 if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1607 return SVN_NO_ERROR;
1608 else if (status == APR_ENOTIMPL)
1610 /* At least try to set the attributes. */
1611 apr_fileattrs_t attrs = 0;
1612 apr_fileattrs_t attrs_values = 0;
1614 if (change_readwrite)
1616 attrs = APR_FILE_ATTR_READONLY;
1617 if (!enable_write)
1618 attrs_values = APR_FILE_ATTR_READONLY;
1620 if (change_executable)
1622 attrs = APR_FILE_ATTR_EXECUTABLE;
1623 if (executable)
1624 attrs_values = APR_FILE_ATTR_EXECUTABLE;
1626 status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
1629 return svn_error_wrap_apr(status,
1630 _("Can't change perms of file '%s'"),
1631 svn_dirent_local_style(path, pool));
1633 #endif /* !WIN32 && !__OS2__ */
1635 #ifdef WIN32
1636 #if APR_HAS_UNICODE_FS
1637 /* copy of the apr function utf8_to_unicode_path since apr doesn't export this one */
1638 static apr_status_t io_utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen,
1639 const char* srcstr)
1641 /* TODO: The computations could preconvert the string to determine
1642 * the true size of the retstr, but that's a memory over speed
1643 * tradeoff that isn't appropriate this early in development.
1645 * Allocate the maximum string length based on leading 4
1646 * characters of \\?\ (allowing nearly unlimited path lengths)
1647 * plus the trailing null, then transform /'s into \\'s since
1648 * the \\?\ form doesn't allow '/' path separators.
1650 * Note that the \\?\ form only works for local drive paths, and
1651 * \\?\UNC\ is needed UNC paths.
1653 apr_size_t srcremains = strlen(srcstr) + 1;
1654 apr_wchar_t *t = retstr;
1655 apr_status_t rv;
1657 /* This is correct, we don't twist the filename if it will
1658 * definitely be shorter than 248 characters. It merits some
1659 * performance testing to see if this has any effect, but there
1660 * seem to be applications that get confused by the resulting
1661 * Unicode \\?\ style file names, especially if they use argv[0]
1662 * or call the Win32 API functions such as GetModuleName, etc.
1663 * Not every application is prepared to handle such names.
1665 * Note also this is shorter than MAX_PATH, as directory paths
1666 * are actually limited to 248 characters.
1668 * Note that a utf-8 name can never result in more wide chars
1669 * than the original number of utf-8 narrow chars.
1671 if (srcremains > 248) {
1672 if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
1673 wcscpy (retstr, L"\\\\?\\");
1674 retlen -= 4;
1675 t += 4;
1677 else if ((srcstr[0] == '/' || srcstr[0] == '\\')
1678 && (srcstr[1] == '/' || srcstr[1] == '\\')
1679 && (srcstr[2] != '?')) {
1680 /* Skip the slashes */
1681 srcstr += 2;
1682 srcremains -= 2;
1683 wcscpy (retstr, L"\\\\?\\UNC\\");
1684 retlen -= 8;
1685 t += 8;
1689 if (rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen)) {
1690 return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
1692 if (srcremains) {
1693 return APR_ENAMETOOLONG;
1695 for (; *t; ++t)
1696 if (*t == L'/')
1697 *t = L'\\';
1698 return APR_SUCCESS;
1700 #endif
1702 static apr_status_t io_win_file_attrs_set(const char *fname,
1703 DWORD attributes,
1704 DWORD attr_mask,
1705 apr_pool_t *pool)
1707 /* this is an implementation of apr_file_attrs_set() but one
1708 that uses the proper Windows attributes instead of the apr
1709 attributes. This way, we can apply any Windows file and
1710 folder attributes even if apr doesn't implement them */
1711 DWORD flags;
1712 apr_status_t rv;
1713 #if APR_HAS_UNICODE_FS
1714 apr_wchar_t wfname[APR_PATH_MAX];
1715 #endif
1717 #if APR_HAS_UNICODE_FS
1718 IF_WIN_OS_IS_UNICODE
1720 if (rv = io_utf8_to_unicode_path(wfname,
1721 sizeof(wfname) / sizeof(wfname[0]),
1722 fname))
1723 return rv;
1724 flags = GetFileAttributesW(wfname);
1726 #endif
1727 #if APR_HAS_ANSI_FS
1728 ELSE_WIN_OS_IS_ANSI
1730 flags = GetFileAttributesA(fname);
1732 #endif
1734 if (flags == 0xFFFFFFFF)
1735 return apr_get_os_error();
1737 flags &= ~attr_mask;
1738 flags |= (attributes & attr_mask);
1740 #if APR_HAS_UNICODE_FS
1741 IF_WIN_OS_IS_UNICODE
1743 rv = SetFileAttributesW(wfname, flags);
1745 #endif
1746 #if APR_HAS_ANSI_FS
1747 ELSE_WIN_OS_IS_ANSI
1749 rv = SetFileAttributesA(fname, flags);
1751 #endif
1753 if (rv == 0)
1754 return apr_get_os_error();
1756 return APR_SUCCESS;
1759 #endif
1761 svn_error_t *
1762 svn_io_set_file_read_write_carefully(const char *path,
1763 svn_boolean_t enable_write,
1764 svn_boolean_t ignore_enoent,
1765 apr_pool_t *pool)
1767 if (enable_write)
1768 return svn_io_set_file_read_write(path, ignore_enoent, pool);
1769 return svn_io_set_file_read_only(path, ignore_enoent, pool);
1772 svn_error_t *
1773 svn_io_set_file_read_only(const char *path,
1774 svn_boolean_t ignore_enoent,
1775 apr_pool_t *pool)
1777 /* On Windows and OS/2, just set the file attributes -- on unix call
1778 our internal function which attempts to honor the umask. */
1779 #if !defined(WIN32) && !defined(__OS2__)
1780 return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE,
1781 ignore_enoent, pool);
1782 #else
1783 apr_status_t status;
1784 const char *path_apr;
1786 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1788 status = apr_file_attrs_set(path_apr,
1789 APR_FILE_ATTR_READONLY,
1790 APR_FILE_ATTR_READONLY,
1791 pool);
1793 if (status && status != APR_ENOTIMPL)
1794 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
1795 return svn_error_wrap_apr(status,
1796 _("Can't set file '%s' read-only"),
1797 svn_dirent_local_style(path, pool));
1799 return SVN_NO_ERROR;
1800 #endif
1804 svn_error_t *
1805 svn_io_set_file_read_write(const char *path,
1806 svn_boolean_t ignore_enoent,
1807 apr_pool_t *pool)
1809 /* On Windows and OS/2, just set the file attributes -- on unix call
1810 our internal function which attempts to honor the umask. */
1811 #if !defined(WIN32) && !defined(__OS2__)
1812 return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE,
1813 ignore_enoent, pool);
1814 #else
1815 apr_status_t status;
1816 const char *path_apr;
1818 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1820 status = apr_file_attrs_set(path_apr,
1822 APR_FILE_ATTR_READONLY,
1823 pool);
1825 if (status && status != APR_ENOTIMPL)
1826 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
1827 return svn_error_wrap_apr(status,
1828 _("Can't set file '%s' read-write"),
1829 svn_dirent_local_style(path, pool));
1831 return SVN_NO_ERROR;
1832 #endif
1835 svn_error_t *
1836 svn_io_set_file_executable(const char *path,
1837 svn_boolean_t executable,
1838 svn_boolean_t ignore_enoent,
1839 apr_pool_t *pool)
1841 /* On Windows and OS/2, just exit -- on unix call our internal function
1842 which attempts to honor the umask. */
1843 #if (!defined(WIN32) && !defined(__OS2__))
1844 return io_set_file_perms(path, FALSE, FALSE, TRUE, executable,
1845 ignore_enoent, pool);
1846 #else
1847 return SVN_NO_ERROR;
1848 #endif
1852 svn_error_t *
1853 svn_io__is_finfo_read_only(svn_boolean_t *read_only,
1854 apr_finfo_t *file_info,
1855 apr_pool_t *pool)
1857 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1858 apr_status_t apr_err;
1859 apr_uid_t uid;
1860 apr_gid_t gid;
1862 *read_only = FALSE;
1864 apr_err = apr_uid_current(&uid, &gid, pool);
1866 if (apr_err)
1867 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
1869 /* Check write bit for current user. */
1870 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
1871 *read_only = !(file_info->protection & APR_UWRITE);
1873 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1874 *read_only = !(file_info->protection & APR_GWRITE);
1876 else
1877 *read_only = !(file_info->protection & APR_WWRITE);
1879 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1880 *read_only = (file_info->protection & APR_FREADONLY);
1881 #endif
1883 return SVN_NO_ERROR;
1886 svn_error_t *
1887 svn_io__is_finfo_executable(svn_boolean_t *executable,
1888 apr_finfo_t *file_info,
1889 apr_pool_t *pool)
1891 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1892 apr_status_t apr_err;
1893 apr_uid_t uid;
1894 apr_gid_t gid;
1896 *executable = FALSE;
1898 apr_err = apr_uid_current(&uid, &gid, pool);
1900 if (apr_err)
1901 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
1903 /* Check executable bit for current user. */
1904 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
1905 *executable = (file_info->protection & APR_UEXECUTE);
1907 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1908 *executable = (file_info->protection & APR_GEXECUTE);
1910 else
1911 *executable = (file_info->protection & APR_WEXECUTE);
1913 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1914 *executable = FALSE;
1915 #endif
1917 return SVN_NO_ERROR;
1920 svn_error_t *
1921 svn_io_is_file_executable(svn_boolean_t *executable,
1922 const char *path,
1923 apr_pool_t *pool)
1925 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1926 apr_finfo_t file_info;
1928 SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
1929 pool));
1930 SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
1932 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
1933 *executable = FALSE;
1934 #endif
1936 return SVN_NO_ERROR;
1940 /*** File locking. ***/
1941 #if !defined(WIN32) && !defined(__OS2__)
1942 /* Clear all outstanding locks on ARG, an open apr_file_t *. */
1943 static apr_status_t
1944 file_clear_locks(void *arg)
1946 apr_status_t apr_err;
1947 apr_file_t *f = arg;
1949 /* Remove locks. */
1950 apr_err = apr_file_unlock(f);
1951 if (apr_err)
1952 return apr_err;
1954 return 0;
1956 #endif
1958 svn_error_t *
1959 svn_io_lock_open_file(apr_file_t *lockfile_handle,
1960 svn_boolean_t exclusive,
1961 svn_boolean_t nonblocking,
1962 apr_pool_t *pool)
1964 int locktype = APR_FLOCK_SHARED;
1965 apr_status_t apr_err;
1966 const char *fname;
1968 if (exclusive)
1969 locktype = APR_FLOCK_EXCLUSIVE;
1970 if (nonblocking)
1971 locktype |= APR_FLOCK_NONBLOCK;
1973 /* We need this only in case of an error but this is cheap to get -
1974 * so we do it here for clarity. */
1975 apr_err = apr_file_name_get(&fname, lockfile_handle);
1976 if (apr_err)
1977 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
1979 /* Get lock on the filehandle. */
1980 apr_err = apr_file_lock(lockfile_handle, locktype);
1982 /* In deployments with two or more multithreaded servers running on
1983 the same system serving two or more fsfs repositories it is
1984 possible for a deadlock to occur when getting a write lock on
1985 db/txn-current-lock:
1987 Process 1 Process 2
1988 --------- ---------
1989 thread 1: get lock in repos A
1990 thread 1: get lock in repos B
1991 thread 2: block getting lock in repos A
1992 thread 2: try to get lock in B *** deadlock ***
1994 Retry for a while for the deadlock to clear. */
1995 FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
1997 if (apr_err)
1999 switch (locktype & APR_FLOCK_TYPEMASK)
2001 case APR_FLOCK_SHARED:
2002 return svn_error_wrap_apr(apr_err,
2003 _("Can't get shared lock on file '%s'"),
2004 try_utf8_from_internal_style(fname, pool));
2005 case APR_FLOCK_EXCLUSIVE:
2006 return svn_error_wrap_apr(apr_err,
2007 _("Can't get exclusive lock on file '%s'"),
2008 try_utf8_from_internal_style(fname, pool));
2009 default:
2010 SVN_ERR_MALFUNCTION();
2014 /* On Windows and OS/2 file locks are automatically released when
2015 the file handle closes */
2016 #if !defined(WIN32) && !defined(__OS2__)
2017 apr_pool_cleanup_register(pool, lockfile_handle,
2018 file_clear_locks,
2019 apr_pool_cleanup_null);
2020 #endif
2022 return SVN_NO_ERROR;
2025 svn_error_t *
2026 svn_io_unlock_open_file(apr_file_t *lockfile_handle,
2027 apr_pool_t *pool)
2029 const char *fname;
2030 apr_status_t apr_err;
2032 /* We need this only in case of an error but this is cheap to get -
2033 * so we do it here for clarity. */
2034 apr_err = apr_file_name_get(&fname, lockfile_handle);
2035 if (apr_err)
2036 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2038 /* The actual unlock attempt. */
2039 apr_err = apr_file_unlock(lockfile_handle);
2040 if (apr_err)
2041 return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
2042 try_utf8_from_internal_style(fname, pool));
2044 /* On Windows and OS/2 file locks are automatically released when
2045 the file handle closes */
2046 #if !defined(WIN32) && !defined(__OS2__)
2047 apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks);
2048 #endif
2050 return SVN_NO_ERROR;
2053 svn_error_t *
2054 svn_io_file_lock2(const char *lock_file,
2055 svn_boolean_t exclusive,
2056 svn_boolean_t nonblocking,
2057 apr_pool_t *pool)
2059 int locktype = APR_FLOCK_SHARED;
2060 apr_file_t *lockfile_handle;
2061 apr_int32_t flags;
2063 if (exclusive)
2064 locktype = APR_FLOCK_EXCLUSIVE;
2066 flags = APR_READ;
2067 if (locktype == APR_FLOCK_EXCLUSIVE)
2068 flags |= APR_WRITE;
2070 /* locktype is never read after this block, so we don't need to bother
2071 setting it. If that were to ever change, uncomment the following
2072 block.
2073 if (nonblocking)
2074 locktype |= APR_FLOCK_NONBLOCK;
2077 SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
2078 APR_OS_DEFAULT,
2079 pool));
2081 /* Get lock on the filehandle. */
2082 return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
2087 /* Data consistency/coherency operations. */
2089 svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
2090 apr_pool_t *pool)
2092 apr_os_file_t filehand;
2094 /* First make sure that any user-space buffered data is flushed. */
2095 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
2096 N_("Can't flush file '%s'"),
2097 N_("Can't flush stream"),
2098 pool));
2100 apr_os_file_get(&filehand, file);
2102 /* Call the operating system specific function to actually force the
2103 data to disk. */
2105 #ifdef WIN32
2107 if (! FlushFileBuffers(filehand))
2108 return svn_error_wrap_apr(apr_get_os_error(),
2109 _("Can't flush file to disk"));
2111 #else
2112 int rv;
2114 do {
2115 rv = fsync(filehand);
2116 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
2118 /* If the file is in a memory filesystem, fsync() may return
2119 EINVAL. Presumably the user knows the risks, and we can just
2120 ignore the error. */
2121 if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
2122 return SVN_NO_ERROR;
2124 if (rv == -1)
2125 return svn_error_wrap_apr(apr_get_os_error(),
2126 _("Can't flush file to disk"));
2128 #endif
2130 return SVN_NO_ERROR;
2135 /* TODO write test for these two functions, then refactor. */
2137 /* Set RESULT to an svn_stringbuf_t containing the contents of FILE.
2138 FILENAME is the FILE's on-disk APR-safe name, or NULL if that name
2139 isn't known. If CHECK_SIZE is TRUE, the function will attempt to
2140 first stat() the file to determine it's size before sucking its
2141 contents into the stringbuf. (Doing so can prevent unnecessary
2142 memory usage, an unwanted side effect of the stringbuf growth and
2143 reallocation mechanism.) */
2144 static svn_error_t *
2145 stringbuf_from_aprfile(svn_stringbuf_t **result,
2146 const char *filename,
2147 apr_file_t *file,
2148 svn_boolean_t check_size,
2149 apr_pool_t *pool)
2151 apr_size_t len;
2152 svn_error_t *err;
2153 svn_stringbuf_t *res = NULL;
2154 apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE;
2155 char *buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
2157 /* If our caller wants us to check the size of the file for
2158 efficient memory handling, we'll try to do so. */
2159 if (check_size)
2161 apr_status_t status;
2163 /* If our caller didn't tell us the file's name, we'll ask APR
2164 if it knows the name. No problem if we can't figure it out. */
2165 if (! filename)
2167 const char *filename_apr;
2168 if (! (status = apr_file_name_get(&filename_apr, file)))
2169 filename = filename_apr;
2172 /* If we now know the filename, try to stat(). If we succeed,
2173 we know how to allocate our stringbuf. */
2174 if (filename)
2176 apr_finfo_t finfo;
2177 if (! (status = apr_stat(&finfo, filename, APR_FINFO_MIN, pool)))
2178 res_initial_len = (apr_size_t)finfo.size;
2183 /* XXX: We should check the incoming data for being of type binary. */
2185 res = svn_stringbuf_create_ensure(res_initial_len, pool);
2187 /* apr_file_read will not return data and eof in the same call. So this loop
2188 * is safe from missing read data. */
2189 len = SVN__STREAM_CHUNK_SIZE;
2190 err = svn_io_file_read(file, buf, &len, pool);
2191 while (! err)
2193 svn_stringbuf_appendbytes(res, buf, len);
2194 len = SVN__STREAM_CHUNK_SIZE;
2195 err = svn_io_file_read(file, buf, &len, pool);
2198 /* Having read all the data we *expect* EOF */
2199 if (err && !APR_STATUS_IS_EOF(err->apr_err))
2200 return err;
2201 svn_error_clear(err);
2203 *result = res;
2204 return SVN_NO_ERROR;
2207 svn_error_t *
2208 svn_stringbuf_from_file2(svn_stringbuf_t **result,
2209 const char *filename,
2210 apr_pool_t *pool)
2212 apr_file_t *f;
2214 if (filename[0] == '-' && filename[1] == '\0')
2216 apr_status_t apr_err;
2217 if ((apr_err = apr_file_open_stdin(&f, pool)))
2218 return svn_error_wrap_apr(apr_err, _("Can't open stdin"));
2219 SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool));
2221 else
2223 SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool));
2224 SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool));
2226 return svn_io_file_close(f, pool);
2230 svn_error_t *
2231 svn_stringbuf_from_file(svn_stringbuf_t **result,
2232 const char *filename,
2233 apr_pool_t *pool)
2235 if (filename[0] == '-' && filename[1] == '\0')
2236 return svn_error_create
2237 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2238 _("Reading from stdin is disallowed"));
2239 return svn_stringbuf_from_file2(result, filename, pool);
2242 svn_error_t *
2243 svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
2244 apr_file_t *file,
2245 apr_pool_t *pool)
2247 return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
2252 /* Deletion. */
2254 svn_error_t *
2255 svn_io_remove_file2(const char *path,
2256 svn_boolean_t ignore_enoent,
2257 apr_pool_t *scratch_pool)
2259 apr_status_t apr_err;
2260 const char *path_apr;
2262 SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
2264 apr_err = apr_file_remove(path_apr, scratch_pool);
2265 if (!apr_err
2266 || (ignore_enoent
2267 && (APR_STATUS_IS_ENOENT(apr_err)
2268 || SVN__APR_STATUS_IS_ENOTDIR(apr_err))))
2269 return SVN_NO_ERROR;
2271 #ifdef WIN32
2272 /* If the target is read only NTFS reports EACCESS and FAT/FAT32
2273 reports EEXIST */
2274 if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
2276 /* Set the destination file writable because Windows will not
2277 allow us to delete when path is read-only */
2278 SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool));
2279 apr_err = apr_file_remove(path_apr, scratch_pool);
2281 if (!apr_err)
2282 return SVN_NO_ERROR;
2286 apr_status_t os_err = APR_TO_OS_ERROR(apr_err);
2287 /* Check to make sure we aren't trying to delete a directory */
2288 if (os_err == ERROR_ACCESS_DENIED || os_err == ERROR_SHARING_VIOLATION)
2290 apr_finfo_t finfo;
2292 if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
2293 && finfo.filetype == APR_REG)
2295 WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr,
2296 scratch_pool));
2300 /* Just return the delete error */
2302 #endif
2304 if (apr_err)
2305 return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
2306 svn_dirent_local_style(path, scratch_pool));
2308 return SVN_NO_ERROR;
2312 svn_error_t *
2313 svn_io_remove_dir(const char *path, apr_pool_t *pool)
2315 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
2319 Mac OS X has a bug where if you're reading the contents of a
2320 directory via readdir in a loop, and you remove one of the entries in
2321 the directory and the directory has 338 or more files in it you will
2322 skip over some of the entries in the directory. Needless to say,
2323 this causes problems if you are using this kind of loop inside a
2324 function that is recursively deleting a directory, because when you
2325 get around to removing the directory it will still have something in
2326 it. A similar problem has been observed in other BSDs. This bug has
2327 since been fixed. See http://www.vnode.ch/fixing_seekdir for details.
2329 The workaround is to delete the files only _after_ the initial
2330 directory scan. A previous workaround involving rewinddir is
2331 problematic on Win32 and some NFS clients, notably NetBSD.
2333 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
2334 http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
2337 /* Neither windows nor unix allows us to delete a non-empty
2338 directory.
2340 This is a function to perform the equivalent of 'rm -rf'. */
2341 svn_error_t *
2342 svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
2343 svn_cancel_func_t cancel_func, void *cancel_baton,
2344 apr_pool_t *pool)
2346 svn_error_t *err;
2347 apr_pool_t *subpool;
2348 apr_hash_t *dirents;
2349 apr_hash_index_t *hi;
2351 /* Check for pending cancellation request.
2352 If we need to bail out, do so early. */
2354 if (cancel_func)
2355 SVN_ERR((*cancel_func)(cancel_baton));
2357 subpool = svn_pool_create(pool);
2359 err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
2360 if (err)
2362 /* if the directory doesn't exist, our mission is accomplished */
2363 if (ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err))
2365 svn_error_clear(err);
2366 return SVN_NO_ERROR;
2368 return svn_error_trace(err);
2371 for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
2373 const char *name = svn__apr_hash_index_key(hi);
2374 const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
2375 const char *fullpath;
2377 fullpath = svn_dirent_join(path, name, subpool);
2378 if (dirent->kind == svn_node_dir)
2380 /* Don't check for cancellation, the callee will immediately do so */
2381 SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func,
2382 cancel_baton, subpool));
2384 else
2386 if (cancel_func)
2387 SVN_ERR((*cancel_func)(cancel_baton));
2389 err = svn_io_remove_file2(fullpath, FALSE, subpool);
2390 if (err)
2391 return svn_error_createf
2392 (err->apr_err, err, _("Can't remove '%s'"),
2393 svn_dirent_local_style(fullpath, subpool));
2397 svn_pool_destroy(subpool);
2399 return svn_io_dir_remove_nonrecursive(path, pool);
2402 svn_error_t *
2403 svn_io_get_dir_filenames(apr_hash_t **dirents,
2404 const char *path,
2405 apr_pool_t *pool)
2407 return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
2408 pool, pool));
2411 svn_io_dirent2_t *
2412 svn_io_dirent2_create(apr_pool_t *result_pool)
2414 svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
2416 /*dirent->kind = svn_node_none;
2417 dirent->special = FALSE;*/
2418 dirent->filesize = SVN_INVALID_FILESIZE;
2419 /*dirent->mtime = 0;*/
2421 return dirent;
2424 svn_io_dirent2_t *
2425 svn_io_dirent2_dup(const svn_io_dirent2_t *item,
2426 apr_pool_t *result_pool)
2428 return apr_pmemdup(result_pool,
2429 item,
2430 sizeof(*item));
2433 svn_error_t *
2434 svn_io_get_dirents3(apr_hash_t **dirents,
2435 const char *path,
2436 svn_boolean_t only_check_type,
2437 apr_pool_t *result_pool,
2438 apr_pool_t *scratch_pool)
2440 apr_status_t status;
2441 apr_dir_t *this_dir;
2442 apr_finfo_t this_entry;
2443 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
2445 if (!only_check_type)
2446 flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
2448 *dirents = apr_hash_make(result_pool);
2450 SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
2452 for (status = apr_dir_read(&this_entry, flags, this_dir);
2453 status == APR_SUCCESS;
2454 status = apr_dir_read(&this_entry, flags, this_dir))
2456 if ((this_entry.name[0] == '.')
2457 && ((this_entry.name[1] == '\0')
2458 || ((this_entry.name[1] == '.')
2459 && (this_entry.name[2] == '\0'))))
2461 continue;
2463 else
2465 const char *name;
2466 svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
2468 SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
2470 map_apr_finfo_to_node_kind(&(dirent->kind),
2471 &(dirent->special),
2472 &this_entry);
2474 if (!only_check_type)
2476 dirent->filesize = this_entry.size;
2477 dirent->mtime = this_entry.mtime;
2480 svn_hash_sets(*dirents, name, dirent);
2484 if (! (APR_STATUS_IS_ENOENT(status)))
2485 return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
2486 svn_dirent_local_style(path, scratch_pool));
2488 status = apr_dir_close(this_dir);
2489 if (status)
2490 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
2491 svn_dirent_local_style(path, scratch_pool));
2493 return SVN_NO_ERROR;
2496 svn_error_t *
2497 svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
2498 const char *path,
2499 svn_boolean_t verify_truename,
2500 svn_boolean_t ignore_enoent,
2501 apr_pool_t *result_pool,
2502 apr_pool_t *scratch_pool)
2504 apr_finfo_t finfo;
2505 svn_io_dirent2_t *dirent;
2506 svn_error_t *err;
2507 apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
2508 | APR_FINFO_SIZE | APR_FINFO_MTIME;
2510 #if defined(WIN32) || defined(__OS2__)
2511 if (verify_truename)
2512 wanted |= APR_FINFO_NAME;
2513 #endif
2515 err = svn_io_stat(&finfo, path, wanted, scratch_pool);
2517 if (err && ignore_enoent &&
2518 (APR_STATUS_IS_ENOENT(err->apr_err)
2519 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2521 svn_error_clear(err);
2522 dirent = svn_io_dirent2_create(result_pool);
2523 SVN_ERR_ASSERT(dirent->kind == svn_node_none);
2525 *dirent_p = dirent;
2526 return SVN_NO_ERROR;
2528 SVN_ERR(err);
2530 #if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
2531 if (verify_truename)
2533 const char *requested_name = svn_dirent_basename(path, NULL);
2535 if (requested_name[0] == '\0')
2537 /* No parent directory. No need to stat/verify */
2539 #if defined(WIN32) || defined(__OS2__)
2540 else if (finfo.name)
2542 const char *name_on_disk;
2543 SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
2544 scratch_pool));
2546 if (strcmp(name_on_disk, requested_name) /* != 0 */)
2548 if (ignore_enoent)
2550 *dirent_p = svn_io_dirent2_create(result_pool);
2551 return SVN_NO_ERROR;
2553 else
2554 return svn_error_createf(APR_ENOENT, NULL,
2555 _("Path '%s' not found, case obstructed by '%s'"),
2556 svn_dirent_local_style(path, scratch_pool),
2557 name_on_disk);
2560 #elif defined(DARWIN)
2561 /* Currently apr doesn't set finfo.name on DARWIN, returning
2562 APR_INCOMPLETE.
2563 ### Can we optimize this in another way? */
2564 else
2566 apr_hash_t *dirents;
2568 err = svn_io_get_dirents3(&dirents,
2569 svn_dirent_dirname(path, scratch_pool),
2570 TRUE /* only_check_type */,
2571 scratch_pool, scratch_pool);
2573 if (err && ignore_enoent
2574 && (APR_STATUS_IS_ENOENT(err->apr_err)
2575 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2577 svn_error_clear(err);
2579 *dirent_p = svn_io_dirent2_create(result_pool);
2580 return SVN_NO_ERROR;
2582 else
2583 SVN_ERR(err);
2585 if (! svn_hash_gets(dirents, requested_name))
2587 if (ignore_enoent)
2589 *dirent_p = svn_io_dirent2_create(result_pool);
2590 return SVN_NO_ERROR;
2592 else
2593 return svn_error_createf(APR_ENOENT, NULL,
2594 _("Path '%s' not found"),
2595 svn_dirent_local_style(path, scratch_pool));
2598 #endif
2600 #endif
2602 dirent = svn_io_dirent2_create(result_pool);
2603 map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
2605 dirent->filesize = finfo.size;
2606 dirent->mtime = finfo.mtime;
2608 *dirent_p = dirent;
2610 return SVN_NO_ERROR;
2613 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2614 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2616 /* Handle an error from the child process (before command execution) by
2617 printing DESC and the error string corresponding to STATUS to stderr. */
2618 static void
2619 handle_child_process_error(apr_pool_t *pool, apr_status_t status,
2620 const char *desc)
2622 char errbuf[256];
2623 apr_file_t *errfile;
2624 void *p;
2626 /* We can't do anything if we get an error here, so just return. */
2627 if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool))
2628 return;
2629 errfile = p;
2631 if (errfile)
2632 /* What we get from APR is in native encoding. */
2633 apr_file_printf(errfile, "%s: %s",
2634 desc, apr_strerror(status, errbuf,
2635 sizeof(errbuf)));
2639 svn_error_t *
2640 svn_io_start_cmd3(apr_proc_t *cmd_proc,
2641 const char *path,
2642 const char *cmd,
2643 const char *const *args,
2644 const char *const *env,
2645 svn_boolean_t inherit,
2646 svn_boolean_t infile_pipe,
2647 apr_file_t *infile,
2648 svn_boolean_t outfile_pipe,
2649 apr_file_t *outfile,
2650 svn_boolean_t errfile_pipe,
2651 apr_file_t *errfile,
2652 apr_pool_t *pool)
2654 apr_status_t apr_err;
2655 apr_procattr_t *cmdproc_attr;
2656 int num_args;
2657 const char **args_native;
2658 const char *cmd_apr;
2660 SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
2661 SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
2662 SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
2664 /* Create the process attributes. */
2665 apr_err = apr_procattr_create(&cmdproc_attr, pool);
2666 if (apr_err)
2667 return svn_error_wrap_apr(apr_err,
2668 _("Can't create process '%s' attributes"),
2669 cmd);
2671 /* Make sure we invoke cmd directly, not through a shell. */
2672 apr_err = apr_procattr_cmdtype_set(cmdproc_attr,
2673 inherit ? APR_PROGRAM_PATH : APR_PROGRAM);
2674 if (apr_err)
2675 return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
2676 cmd);
2678 /* Set the process's working directory. */
2679 if (path)
2681 const char *path_apr;
2683 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2684 apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
2685 if (apr_err)
2686 return svn_error_wrap_apr(apr_err,
2687 _("Can't set process '%s' directory"),
2688 cmd);
2691 /* Use requested inputs and outputs.
2693 ### Unfortunately each of these apr functions creates a pipe and then
2694 overwrites the pipe file descriptor with the descriptor we pass
2695 in. The pipes can then never be closed. This is an APR bug. */
2696 if (infile)
2698 apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
2699 if (apr_err)
2700 return svn_error_wrap_apr(apr_err,
2701 _("Can't set process '%s' child input"),
2702 cmd);
2704 if (outfile)
2706 apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
2707 if (apr_err)
2708 return svn_error_wrap_apr(apr_err,
2709 _("Can't set process '%s' child outfile"),
2710 cmd);
2712 if (errfile)
2714 apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
2715 if (apr_err)
2716 return svn_error_wrap_apr(apr_err,
2717 _("Can't set process '%s' child errfile"),
2718 cmd);
2721 /* Forward request for pipes to APR. */
2722 if (infile_pipe || outfile_pipe || errfile_pipe)
2724 apr_err = apr_procattr_io_set(cmdproc_attr,
2725 infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
2726 outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
2727 errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE);
2729 if (apr_err)
2730 return svn_error_wrap_apr(apr_err,
2731 _("Can't set process '%s' stdio pipes"),
2732 cmd);
2735 /* Have the child print any problems executing its program to errfile. */
2736 apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
2737 if (apr_err)
2738 return svn_error_wrap_apr(apr_err,
2739 _("Can't set process '%s' child errfile for "
2740 "error handler"),
2741 cmd);
2742 apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
2743 handle_child_process_error);
2744 if (apr_err)
2745 return svn_error_wrap_apr(apr_err,
2746 _("Can't set process '%s' error handler"),
2747 cmd);
2749 /* Convert cmd and args from UTF-8 */
2750 SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool));
2751 for (num_args = 0; args[num_args]; num_args++)
2753 args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
2754 args_native[num_args] = NULL;
2755 while (num_args--)
2757 /* ### Well, it turns out that on APR on Windows expects all
2758 program args to be in UTF-8. Callers of svn_io_run_cmd
2759 should be aware of that. */
2760 SVN_ERR(cstring_from_utf8(&args_native[num_args],
2761 args[num_args], pool));
2765 /* Start the cmd command. */
2766 apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
2767 inherit ? NULL : env, cmdproc_attr, pool);
2768 if (apr_err)
2769 return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
2771 return SVN_NO_ERROR;
2774 #undef ERRFILE_KEY
2776 svn_error_t *
2777 svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
2778 const char *cmd,
2779 int *exitcode,
2780 apr_exit_why_e *exitwhy,
2781 apr_pool_t *pool)
2783 apr_status_t apr_err;
2784 apr_exit_why_e exitwhy_val;
2785 int exitcode_val;
2787 /* The Win32 apr_proc_wait doesn't set this... */
2788 exitwhy_val = APR_PROC_EXIT;
2790 /* Wait for the cmd command to finish. */
2791 apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT);
2792 if (!APR_STATUS_IS_CHILD_DONE(apr_err))
2793 return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"),
2794 cmd);
2796 if (exitwhy)
2797 *exitwhy = exitwhy_val;
2798 else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
2799 return svn_error_createf
2800 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2801 _("Process '%s' failed (exitwhy %d)"), cmd, exitwhy_val);
2803 if (exitcode)
2804 *exitcode = exitcode_val;
2805 else if (exitcode_val != 0)
2806 return svn_error_createf
2807 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2808 _("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
2810 return SVN_NO_ERROR;
2814 svn_error_t *
2815 svn_io_run_cmd(const char *path,
2816 const char *cmd,
2817 const char *const *args,
2818 int *exitcode,
2819 apr_exit_why_e *exitwhy,
2820 svn_boolean_t inherit,
2821 apr_file_t *infile,
2822 apr_file_t *outfile,
2823 apr_file_t *errfile,
2824 apr_pool_t *pool)
2826 apr_proc_t cmd_proc;
2828 SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
2829 FALSE, infile, FALSE, outfile, FALSE, errfile,
2830 pool));
2832 return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
2836 svn_error_t *
2837 svn_io_run_diff2(const char *dir,
2838 const char *const *user_args,
2839 int num_user_args,
2840 const char *label1,
2841 const char *label2,
2842 const char *from,
2843 const char *to,
2844 int *pexitcode,
2845 apr_file_t *outfile,
2846 apr_file_t *errfile,
2847 const char *diff_cmd,
2848 apr_pool_t *pool)
2850 const char **args;
2851 int i;
2852 int exitcode;
2853 int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
2854 apr_pool_t *subpool = svn_pool_create(pool);
2856 if (pexitcode == NULL)
2857 pexitcode = &exitcode;
2859 if (user_args != NULL)
2860 nargs += num_user_args;
2861 else
2862 nargs += 1; /* -u */
2864 if (label1 != NULL)
2865 nargs += 2; /* the -L and the label itself */
2866 if (label2 != NULL)
2867 nargs += 2; /* the -L and the label itself */
2869 args = apr_palloc(subpool, nargs * sizeof(char *));
2871 i = 0;
2872 args[i++] = diff_cmd;
2874 if (user_args != NULL)
2876 int j;
2877 for (j = 0; j < num_user_args; ++j)
2878 args[i++] = user_args[j];
2880 else
2881 args[i++] = "-u"; /* assume -u if the user didn't give us any args */
2883 if (label1 != NULL)
2885 args[i++] = "-L";
2886 args[i++] = label1;
2888 if (label2 != NULL)
2890 args[i++] = "-L";
2891 args[i++] = label2;
2894 args[i++] = svn_dirent_local_style(from, subpool);
2895 args[i++] = svn_dirent_local_style(to, subpool);
2896 args[i++] = NULL;
2898 SVN_ERR_ASSERT(i == nargs);
2900 SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
2901 NULL, outfile, errfile, subpool));
2903 /* The man page for (GNU) diff describes the return value as:
2905 "An exit status of 0 means no differences were found, 1 means
2906 some differences were found, and 2 means trouble."
2908 A return value of 2 typically occurs when diff cannot read its input
2909 or write to its output, but in any case we probably ought to return an
2910 error for anything other than 0 or 1 as the output is likely to be
2911 corrupt.
2913 if (*pexitcode != 0 && *pexitcode != 1)
2914 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
2915 _("'%s' returned %d"),
2916 svn_dirent_local_style(diff_cmd, pool),
2917 *pexitcode);
2919 svn_pool_destroy(subpool);
2921 return SVN_NO_ERROR;
2925 svn_error_t *
2926 svn_io_run_diff3_3(int *exitcode,
2927 const char *dir,
2928 const char *mine,
2929 const char *older,
2930 const char *yours,
2931 const char *mine_label,
2932 const char *older_label,
2933 const char *yours_label,
2934 apr_file_t *merged,
2935 const char *diff3_cmd,
2936 const apr_array_header_t *user_args,
2937 apr_pool_t *pool)
2939 const char **args = apr_palloc(pool,
2940 sizeof(char*) * (13
2941 + (user_args
2942 ? user_args->nelts
2943 : 1)));
2944 #ifndef NDEBUG
2945 int nargs = 12;
2946 #endif
2947 int i = 0;
2949 /* Labels fall back to sensible defaults if not specified. */
2950 if (mine_label == NULL)
2951 mine_label = ".working";
2952 if (older_label == NULL)
2953 older_label = ".old";
2954 if (yours_label == NULL)
2955 yours_label = ".new";
2957 /* Set up diff3 command line. */
2958 args[i++] = diff3_cmd;
2959 if (user_args)
2961 int j;
2962 for (j = 0; j < user_args->nelts; ++j)
2963 args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
2964 #ifndef NDEBUG
2965 nargs += user_args->nelts;
2966 #endif
2968 else
2970 args[i++] = "-E"; /* We tried "-A" here, but that caused
2971 overlapping identical changes to
2972 conflict. See issue #682. */
2973 #ifndef NDEBUG
2974 ++nargs;
2975 #endif
2977 args[i++] = "-m";
2978 args[i++] = "-L";
2979 args[i++] = mine_label;
2980 args[i++] = "-L";
2981 args[i++] = older_label; /* note: this label is ignored if
2982 using 2-part markers, which is the
2983 case with "-E". */
2984 args[i++] = "-L";
2985 args[i++] = yours_label;
2986 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
2988 svn_boolean_t has_arg;
2990 /* ### FIXME: we really shouldn't be reading the config here;
2991 instead, the necessary bits should be passed in by the caller.
2992 But should we add another parameter to this function, when the
2993 whole external diff3 thing might eventually go away? */
2994 apr_hash_t *config;
2995 svn_config_t *cfg;
2997 SVN_ERR(svn_config_get_config(&config, pool));
2998 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
2999 SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS,
3000 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG,
3001 TRUE));
3002 if (has_arg)
3004 const char *diff_cmd, *diff_utf8;
3005 svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
3006 SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF);
3007 SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool));
3008 args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8, NULL);
3009 #ifndef NDEBUG
3010 ++nargs;
3011 #endif
3014 #endif
3015 args[i++] = svn_dirent_local_style(mine, pool);
3016 args[i++] = svn_dirent_local_style(older, pool);
3017 args[i++] = svn_dirent_local_style(yours, pool);
3018 args[i++] = NULL;
3019 #ifndef NDEBUG
3020 SVN_ERR_ASSERT(i == nargs);
3021 #endif
3023 /* Run diff3, output the merged text into the scratch file. */
3024 SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
3025 exitcode, NULL,
3026 TRUE, /* keep environment */
3027 NULL, merged, NULL,
3028 pool));
3030 /* According to the diff3 docs, a '0' means the merge was clean, and
3031 '1' means conflict markers were found. Anything else is real
3032 error. */
3033 if ((*exitcode != 0) && (*exitcode != 1))
3034 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3035 _("Error running '%s': exitcode was %d, "
3036 "args were:"
3037 "\nin directory '%s', basenames:\n%s\n%s\n%s"),
3038 svn_dirent_local_style(diff3_cmd, pool),
3039 *exitcode,
3040 svn_dirent_local_style(dir, pool),
3041 /* Don't call svn_path_local_style() on
3042 the basenames. We don't want them to
3043 be absolute, and we don't need the
3044 separator conversion. */
3045 mine, older, yours);
3047 return SVN_NO_ERROR;
3051 /* Canonicalize a string for hashing. Modifies KEY in place. */
3052 static APR_INLINE char *
3053 fileext_tolower(char *key)
3055 register char *p;
3056 for (p = key; *p != 0; ++p)
3057 *p = (char)apr_tolower(*p);
3058 return key;
3062 svn_error_t *
3063 svn_io_parse_mimetypes_file(apr_hash_t **type_map,
3064 const char *mimetypes_file,
3065 apr_pool_t *pool)
3067 svn_error_t *err = SVN_NO_ERROR;
3068 apr_hash_t *types = apr_hash_make(pool);
3069 svn_boolean_t eof = FALSE;
3070 svn_stringbuf_t *buf;
3071 apr_pool_t *subpool = svn_pool_create(pool);
3072 apr_file_t *types_file;
3073 svn_stream_t *mimetypes_stream;
3075 SVN_ERR(svn_io_file_open(&types_file, mimetypes_file,
3076 APR_READ, APR_OS_DEFAULT, pool));
3077 mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool);
3079 while (1)
3081 apr_array_header_t *tokens;
3082 const char *type;
3084 svn_pool_clear(subpool);
3086 /* Read a line. */
3087 if ((err = svn_stream_readline(mimetypes_stream, &buf,
3088 APR_EOL_STR, &eof, subpool)))
3089 break;
3091 /* Only pay attention to non-empty, non-comment lines. */
3092 if (buf->len)
3094 int i;
3096 if (buf->data[0] == '#')
3097 continue;
3099 /* Tokenize (into our return pool). */
3100 tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
3101 if (tokens->nelts < 2)
3102 continue;
3104 /* The first token in a multi-token line is the media type.
3105 Subsequent tokens are filename extensions associated with
3106 that media type. */
3107 type = APR_ARRAY_IDX(tokens, 0, const char *);
3108 for (i = 1; i < tokens->nelts; i++)
3110 /* We can safely address 'ext' as a non-const string because
3111 * we know svn_cstring_split() allocated it in 'pool' for us. */
3112 char *ext = APR_ARRAY_IDX(tokens, i, char *);
3113 fileext_tolower(ext);
3114 svn_hash_sets(types, ext, type);
3117 if (eof)
3118 break;
3120 svn_pool_destroy(subpool);
3122 /* If there was an error above, close the file (ignoring any error
3123 from *that*) and return the originally error. */
3124 if (err)
3126 svn_error_clear(svn_stream_close(mimetypes_stream));
3127 return err;
3130 /* Close the stream (which closes the underlying file, too). */
3131 SVN_ERR(svn_stream_close(mimetypes_stream));
3133 *type_map = types;
3134 return SVN_NO_ERROR;
3138 svn_error_t *
3139 svn_io_detect_mimetype2(const char **mimetype,
3140 const char *file,
3141 apr_hash_t *mimetype_map,
3142 apr_pool_t *pool)
3144 static const char * const generic_binary = "application/octet-stream";
3146 svn_node_kind_t kind;
3147 apr_file_t *fh;
3148 svn_error_t *err;
3149 unsigned char block[1024];
3150 apr_size_t amt_read = sizeof(block);
3152 /* Default return value is NULL. */
3153 *mimetype = NULL;
3155 /* If there is a mimetype_map provided, we'll first try to look up
3156 our file's extension in the map. Failing that, we'll run the
3157 heuristic. */
3158 if (mimetype_map)
3160 const char *type_from_map;
3161 char *path_ext; /* Can point to physical const memory but only when
3162 svn_path_splitext sets it to "". */
3163 svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
3164 fileext_tolower(path_ext);
3165 if ((type_from_map = svn_hash_gets(mimetype_map, path_ext)))
3167 *mimetype = type_from_map;
3168 return SVN_NO_ERROR;
3172 /* See if this file even exists, and make sure it really is a file. */
3173 SVN_ERR(svn_io_check_path(file, &kind, pool));
3174 if (kind != svn_node_file)
3175 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
3176 _("Can't detect MIME type of non-file '%s'"),
3177 svn_dirent_local_style(file, pool));
3179 SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
3181 /* Read a block of data from FILE. */
3182 err = svn_io_file_read(fh, block, &amt_read, pool);
3183 if (err && ! APR_STATUS_IS_EOF(err->apr_err))
3184 return err;
3185 svn_error_clear(err);
3187 /* Now close the file. No use keeping it open any more. */
3188 SVN_ERR(svn_io_file_close(fh, pool));
3190 if (svn_io_is_binary_data(block, amt_read))
3191 *mimetype = generic_binary;
3193 return SVN_NO_ERROR;
3197 svn_boolean_t
3198 svn_io_is_binary_data(const void *data, apr_size_t len)
3200 const unsigned char *buf = data;
3202 if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
3204 /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
3205 * Treat it as plain text. */
3206 return FALSE;
3209 /* Right now, this function is going to be really stupid. It's
3210 going to examine the block of data, and make sure that 15%
3211 of the bytes are such that their value is in the ranges 0x07-0x0D
3212 or 0x20-0x7F, and that none of those bytes is 0x00. If those
3213 criteria are not met, we're calling it binary.
3215 NOTE: Originally, I intended to target 85% of the bytes being in
3216 the specified ranges, but I flubbed the condition. At any rate,
3217 folks aren't complaining, so I'm not sure that it's worth
3218 adjusting this retroactively now. --cmpilato */
3219 if (len > 0)
3221 apr_size_t i;
3222 apr_size_t binary_count = 0;
3224 /* Run through the data we've read, counting the 'binary-ish'
3225 bytes. HINT: If we see a 0x00 byte, we'll set our count to its
3226 max and stop reading the file. */
3227 for (i = 0; i < len; i++)
3229 if (buf[i] == 0)
3231 binary_count = len;
3232 break;
3234 if ((buf[i] < 0x07)
3235 || ((buf[i] > 0x0D) && (buf[i] < 0x20))
3236 || (buf[i] > 0x7F))
3238 binary_count++;
3242 return (((binary_count * 1000) / len) > 850);
3245 return FALSE;
3249 svn_error_t *
3250 svn_io_detect_mimetype(const char **mimetype,
3251 const char *file,
3252 apr_pool_t *pool)
3254 return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
3258 svn_error_t *
3259 svn_io_file_open(apr_file_t **new_file, const char *fname,
3260 apr_int32_t flag, apr_fileperms_t perm,
3261 apr_pool_t *pool)
3263 const char *fname_apr;
3264 apr_status_t status;
3266 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3267 status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
3268 pool);
3270 if (status)
3271 return svn_error_wrap_apr(status, _("Can't open file '%s'"),
3272 svn_dirent_local_style(fname, pool));
3273 else
3274 return SVN_NO_ERROR;
3278 static APR_INLINE svn_error_t *
3279 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
3280 const char *msg, const char *msg_no_name,
3281 apr_pool_t *pool)
3283 const char *name;
3284 svn_error_t *err;
3286 if (! status)
3287 return SVN_NO_ERROR;
3289 err = svn_io_file_name_get(&name, file, pool);
3290 if (err)
3291 name = NULL;
3292 svn_error_clear(err);
3294 /* ### Issue #3014: Return a specific error for broken pipes,
3295 * ### with a single element in the error chain. */
3296 if (APR_STATUS_IS_EPIPE(status))
3297 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
3299 if (name)
3300 return svn_error_wrap_apr(status, _(msg),
3301 try_utf8_from_internal_style(name, pool));
3302 else
3303 return svn_error_wrap_apr(status, "%s", _(msg_no_name));
3307 svn_error_t *
3308 svn_io_file_close(apr_file_t *file, apr_pool_t *pool)
3310 return do_io_file_wrapper_cleanup(file, apr_file_close(file),
3311 N_("Can't close file '%s'"),
3312 N_("Can't close stream"),
3313 pool);
3317 svn_error_t *
3318 svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
3320 return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file),
3321 N_("Can't read file '%s'"),
3322 N_("Can't read stream"),
3323 pool);
3327 svn_error_t *
3328 svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
3330 return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file),
3331 N_("Can't write file '%s'"),
3332 N_("Can't write stream"),
3333 pool);
3337 svn_error_t *
3338 svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
3339 apr_file_t *file, apr_pool_t *pool)
3341 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3342 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3344 return do_io_file_wrapper_cleanup(
3345 file, apr_file_info_get(finfo, wanted, file),
3346 N_("Can't get attribute information from file '%s'"),
3347 N_("Can't get attribute information from stream"),
3348 pool);
3352 svn_error_t *
3353 svn_io_file_read(apr_file_t *file, void *buf,
3354 apr_size_t *nbytes, apr_pool_t *pool)
3356 return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes),
3357 N_("Can't read file '%s'"),
3358 N_("Can't read stream"),
3359 pool);
3363 svn_error_t *
3364 svn_io_file_read_full2(apr_file_t *file, void *buf,
3365 apr_size_t nbytes, apr_size_t *bytes_read,
3366 svn_boolean_t *hit_eof,
3367 apr_pool_t *pool)
3369 apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
3370 if (hit_eof)
3372 if (APR_STATUS_IS_EOF(status))
3374 *hit_eof = TRUE;
3375 return SVN_NO_ERROR;
3377 else
3378 *hit_eof = FALSE;
3381 return do_io_file_wrapper_cleanup(file, status,
3382 N_("Can't read file '%s'"),
3383 N_("Can't read stream"),
3384 pool);
3388 svn_error_t *
3389 svn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
3390 apr_off_t *offset, apr_pool_t *pool)
3392 return do_io_file_wrapper_cleanup(
3393 file, apr_file_seek(file, where, offset),
3394 N_("Can't set position pointer in file '%s'"),
3395 N_("Can't set position pointer in stream"),
3396 pool);
3400 svn_error_t *
3401 svn_io_file_write(apr_file_t *file, const void *buf,
3402 apr_size_t *nbytes, apr_pool_t *pool)
3404 return svn_error_trace(do_io_file_wrapper_cleanup(
3405 file, apr_file_write(file, buf, nbytes),
3406 N_("Can't write to file '%s'"),
3407 N_("Can't write to stream"),
3408 pool));
3412 svn_error_t *
3413 svn_io_file_write_full(apr_file_t *file, const void *buf,
3414 apr_size_t nbytes, apr_size_t *bytes_written,
3415 apr_pool_t *pool)
3417 /* We cannot simply call apr_file_write_full on Win32 as it may fail
3418 for larger values of NBYTES. In that case, we have to emulate the
3419 "_full" part here. Thus, always call apr_file_write directly on
3420 Win32 as this minimizes overhead for small data buffers. */
3421 #ifdef WIN32
3422 #define MAXBUFSIZE 30*1024
3423 apr_size_t bw = nbytes;
3424 apr_size_t to_write = nbytes;
3426 /* try a simple "write everything at once" first */
3427 apr_status_t rv = apr_file_write(file, buf, &bw);
3428 buf = (char *)buf + bw;
3429 to_write -= bw;
3431 /* if the OS cannot handle that, use smaller chunks */
3432 if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
3433 && nbytes > MAXBUFSIZE)
3435 do {
3436 bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
3437 rv = apr_file_write(file, buf, &bw);
3438 buf = (char *)buf + bw;
3439 to_write -= bw;
3440 } while (rv == APR_SUCCESS && to_write > 0);
3443 /* bytes_written may actually be NULL */
3444 if (bytes_written)
3445 *bytes_written = nbytes - to_write;
3446 #undef MAXBUFSIZE
3447 #else
3448 apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
3449 #endif
3451 return svn_error_trace(do_io_file_wrapper_cleanup(
3452 file, rv,
3453 N_("Can't write to file '%s'"),
3454 N_("Can't write to stream"),
3455 pool));
3459 svn_error_t *
3460 svn_io_write_unique(const char **tmp_path,
3461 const char *dirpath,
3462 const void *buf,
3463 apr_size_t nbytes,
3464 svn_io_file_del_t delete_when,
3465 apr_pool_t *pool)
3467 apr_file_t *new_file;
3468 svn_error_t *err;
3470 SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
3471 delete_when, pool, pool));
3473 err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
3475 /* ### BH: Windows doesn't have the race condition between the write and the
3476 ### rename that other operating systems might have. So allow windows
3477 ### to decide when it wants to perform the disk synchronization using
3478 ### the normal file locking and journaling filesystem rules.
3480 ### Note that this function doesn't handle the rename, so we aren't even
3481 ### sure that we really have to sync. */
3482 #ifndef WIN32
3483 if (!err && nbytes > 0)
3484 err = svn_io_file_flush_to_disk(new_file, pool);
3485 #endif
3487 return svn_error_trace(
3488 svn_error_compose_create(err,
3489 svn_io_file_close(new_file, pool)));
3493 svn_error_t *
3494 svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
3496 /* This is a work-around. APR would flush the write buffer
3497 _after_ truncating the file causing now invalid buffered
3498 data to be written behind OFFSET. */
3499 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
3500 N_("Can't flush file '%s'"),
3501 N_("Can't flush stream"),
3502 pool));
3504 return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset),
3505 N_("Can't truncate file '%s'"),
3506 N_("Can't truncate stream"),
3507 pool);
3511 svn_error_t *
3512 svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
3513 apr_pool_t *pool)
3515 /* variables */
3516 apr_size_t total_read = 0;
3517 svn_boolean_t eof = FALSE;
3518 const char *name;
3519 svn_error_t *err;
3520 apr_size_t buf_size = *limit;
3522 while (buf_size > 0)
3524 /* read a fair chunk of data at once. But don't get too ambitious
3525 * as that would result in too much waste. Also make sure we can
3526 * put a NUL after the last byte read.
3528 apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
3529 apr_size_t bytes_read = 0;
3530 char *eol;
3532 /* read data block (or just a part of it) */
3533 SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
3534 &bytes_read, &eof, pool));
3536 /* look or a newline char */
3537 buf[bytes_read] = 0;
3538 eol = strchr(buf, '\n');
3539 if (eol)
3541 apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
3543 *eol = 0;
3544 *limit = total_read + (eol - buf);
3546 /* correct the file pointer:
3547 * appear as though we just had read the newline char
3549 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
3551 return SVN_NO_ERROR;
3553 else if (eof)
3555 /* no EOL found but we hit the end of the file.
3556 * Generate a nice EOF error object and return it.
3558 char dummy;
3559 SVN_ERR(svn_io_file_getc(&dummy, file, pool));
3562 /* next data chunk */
3563 buf_size -= bytes_read;
3564 buf += bytes_read;
3565 total_read += bytes_read;
3568 /* buffer limit has been exceeded without finding the EOL */
3569 err = svn_io_file_name_get(&name, file, pool);
3570 if (err)
3571 name = NULL;
3572 svn_error_clear(err);
3574 if (name)
3575 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
3576 _("Can't read length line in file '%s'"),
3577 svn_dirent_local_style(name, pool));
3578 else
3579 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
3580 _("Can't read length line in stream"));
3584 svn_error_t *
3585 svn_io_stat(apr_finfo_t *finfo, const char *fname,
3586 apr_int32_t wanted, apr_pool_t *pool)
3588 apr_status_t status;
3589 const char *fname_apr;
3591 /* APR doesn't like "" directories */
3592 if (fname[0] == '\0')
3593 fname = ".";
3595 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3597 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3598 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3600 status = apr_stat(finfo, fname_apr, wanted, pool);
3601 if (status)
3602 return svn_error_wrap_apr(status, _("Can't stat '%s'"),
3603 svn_dirent_local_style(fname, pool));
3605 return SVN_NO_ERROR;
3609 svn_error_t *
3610 svn_io_file_rename(const char *from_path, const char *to_path,
3611 apr_pool_t *pool)
3613 apr_status_t status = APR_SUCCESS;
3614 const char *from_path_apr, *to_path_apr;
3616 SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
3617 SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
3619 status = apr_file_rename(from_path_apr, to_path_apr, pool);
3621 #if defined(WIN32) || defined(__OS2__)
3622 /* If the target file is read only NTFS reports EACCESS and
3623 FAT/FAT32 reports EEXIST */
3624 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
3626 /* Set the destination file writable because Windows will not
3627 allow us to rename when to_path is read-only, but will
3628 allow renaming when from_path is read only. */
3629 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
3631 status = apr_file_rename(from_path_apr, to_path_apr, pool);
3633 WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool));
3634 #endif /* WIN32 || __OS2__ */
3636 if (status)
3637 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
3638 svn_dirent_local_style(from_path, pool),
3639 svn_dirent_local_style(to_path, pool));
3641 return SVN_NO_ERROR;
3645 svn_error_t *
3646 svn_io_file_move(const char *from_path, const char *to_path,
3647 apr_pool_t *pool)
3649 svn_error_t *err = svn_io_file_rename(from_path, to_path, pool);
3651 if (err && APR_STATUS_IS_EXDEV(err->apr_err))
3653 const char *tmp_to_path;
3655 svn_error_clear(err);
3657 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_to_path,
3658 svn_dirent_dirname(to_path, pool),
3659 svn_io_file_del_none,
3660 pool, pool));
3662 err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool);
3663 if (err)
3664 goto failed_tmp;
3666 err = svn_io_file_rename(tmp_to_path, to_path, pool);
3667 if (err)
3668 goto failed_tmp;
3670 err = svn_io_remove_file2(from_path, FALSE, pool);
3671 if (! err)
3672 return SVN_NO_ERROR;
3674 svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool));
3676 return err;
3678 failed_tmp:
3679 svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool));
3682 return err;
3685 /* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
3686 HIDDEN determines if the hidden attribute
3687 should be set on the newly created directory. */
3688 static svn_error_t *
3689 dir_make(const char *path, apr_fileperms_t perm,
3690 svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
3692 apr_status_t status;
3693 const char *path_apr;
3695 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
3697 /* APR doesn't like "" directories */
3698 if (path_apr[0] == '\0')
3699 path_apr = ".";
3701 #if (APR_OS_DEFAULT & APR_WSTICKY)
3702 /* The APR shipped with httpd 2.0.50 contains a bug where
3703 APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
3704 There is a special case for file creation, but not directory
3705 creation, so directories wind up getting created with the sticky
3706 bit set. (There is no such thing as a setuid directory, and the
3707 setgid bit is apparently ignored at mkdir() time.) If we detect
3708 this problem, work around it by unsetting those bits if we are
3709 passed APR_OS_DEFAULT. */
3710 if (perm == APR_OS_DEFAULT)
3711 perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY);
3712 #endif
3714 status = apr_dir_make(path_apr, perm, pool);
3715 WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
3717 if (status)
3718 return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
3719 svn_dirent_local_style(path, pool));
3721 #ifdef APR_FILE_ATTR_HIDDEN
3722 if (hidden)
3724 #ifndef WIN32
3725 status = apr_file_attrs_set(path_apr,
3726 APR_FILE_ATTR_HIDDEN,
3727 APR_FILE_ATTR_HIDDEN,
3728 pool);
3729 #else
3730 /* on Windows, use our wrapper so we can also set the
3731 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */
3732 status = io_win_file_attrs_set(path_apr,
3733 FILE_ATTRIBUTE_HIDDEN |
3734 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
3735 FILE_ATTRIBUTE_HIDDEN |
3736 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
3737 pool);
3739 #endif
3740 if (status)
3741 return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
3742 svn_dirent_local_style(path, pool));
3744 #endif
3746 /* Windows does not implement sgid. Skip here because retrieving
3747 the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented
3748 to be 'incredibly expensive'. */
3749 #ifndef WIN32
3750 if (sgid)
3752 apr_finfo_t finfo;
3754 /* Per our contract, don't do error-checking. Some filesystems
3755 * don't support the sgid bit, and that's okay. */
3756 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool);
3758 if (!status)
3759 apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
3761 #endif
3763 return SVN_NO_ERROR;
3766 svn_error_t *
3767 svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
3769 return dir_make(path, perm, FALSE, FALSE, pool);
3772 svn_error_t *
3773 svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
3774 apr_pool_t *pool)
3776 return dir_make(path, perm, TRUE, FALSE, pool);
3779 svn_error_t *
3780 svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
3781 apr_pool_t *pool)
3783 return dir_make(path, perm, FALSE, TRUE, pool);
3787 svn_error_t *
3788 svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
3790 apr_status_t status;
3791 const char *dirname_apr;
3793 /* APR doesn't like "" directories */
3794 if (dirname[0] == '\0')
3795 dirname = ".";
3797 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3799 status = apr_dir_open(new_dir, dirname_apr, pool);
3800 if (status)
3801 return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
3802 svn_dirent_local_style(dirname, pool));
3804 return SVN_NO_ERROR;
3807 svn_error_t *
3808 svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
3810 apr_status_t status;
3811 const char *dirname_apr;
3813 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3815 status = apr_dir_remove(dirname_apr, pool);
3817 #ifdef WIN32
3819 svn_boolean_t retry = TRUE;
3821 if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY)
3823 apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
3825 if (APR_STATUS_IS_ENOTEMPTY(empty_status))
3826 retry = FALSE;
3829 if (retry)
3831 WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
3834 #endif
3835 if (status)
3836 return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
3837 svn_dirent_local_style(dirname, pool));
3839 return SVN_NO_ERROR;
3843 svn_error_t *
3844 svn_io_dir_read(apr_finfo_t *finfo,
3845 apr_int32_t wanted,
3846 apr_dir_t *thedir,
3847 apr_pool_t *pool)
3849 apr_status_t status;
3851 status = apr_dir_read(finfo, wanted, thedir);
3853 if (status)
3854 return svn_error_wrap_apr(status, _("Can't read directory"));
3856 /* It would be nice to use entry_name_to_utf8() below, but can we
3857 get the dir's path out of an apr_dir_t? I don't see a reliable
3858 way to do it. */
3860 if (finfo->fname)
3861 SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
3863 if (finfo->name)
3864 SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
3866 return SVN_NO_ERROR;
3869 svn_error_t *
3870 svn_io_dir_close(apr_dir_t *thedir)
3872 apr_status_t apr_err = apr_dir_close(thedir);
3873 if (apr_err)
3874 return svn_error_wrap_apr(apr_err, _("Error closing directory"));
3876 return SVN_NO_ERROR;
3879 svn_error_t *
3880 svn_io_dir_walk2(const char *dirname,
3881 apr_int32_t wanted,
3882 svn_io_walk_func_t walk_func,
3883 void *walk_baton,
3884 apr_pool_t *pool)
3886 apr_status_t apr_err;
3887 apr_dir_t *handle;
3888 apr_pool_t *subpool;
3889 const char *dirname_apr;
3890 apr_finfo_t finfo;
3892 wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
3894 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3895 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3897 /* The documentation for apr_dir_read used to state that "." and ".."
3898 will be returned as the first two files, but it doesn't
3899 work that way in practice, in particular ext3 on Linux-2.6 doesn't
3900 follow the rules. For details see
3901 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
3903 If APR ever does implement "dot-first" then it would be possible to
3904 remove the svn_io_stat and walk_func calls and use the walk_func
3905 inside the loop.
3907 Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
3908 documented to provide it, so we have to do a bit extra. */
3909 SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool));
3910 SVN_ERR(cstring_from_utf8(&finfo.name,
3911 svn_dirent_basename(dirname, pool),
3912 pool));
3913 finfo.valid |= APR_FINFO_NAME;
3914 SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
3916 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3918 /* APR doesn't like "" directories */
3919 if (dirname_apr[0] == '\0')
3920 dirname_apr = ".";
3922 apr_err = apr_dir_open(&handle, dirname_apr, pool);
3923 if (apr_err)
3924 return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
3925 svn_dirent_local_style(dirname, pool));
3927 /* iteration subpool */
3928 subpool = svn_pool_create(pool);
3930 while (1)
3932 const char *name_utf8;
3933 const char *full_path;
3935 svn_pool_clear(subpool);
3937 apr_err = apr_dir_read(&finfo, wanted, handle);
3938 if (APR_STATUS_IS_ENOENT(apr_err))
3939 break;
3940 else if (apr_err)
3942 return svn_error_wrap_apr(apr_err,
3943 _("Can't read directory entry in '%s'"),
3944 svn_dirent_local_style(dirname, pool));
3947 if (finfo.filetype == APR_DIR)
3949 if (finfo.name[0] == '.'
3950 && (finfo.name[1] == '\0'
3951 || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
3952 /* skip "." and ".." */
3953 continue;
3955 /* some other directory. recurse. it will be passed to the
3956 callback inside the recursion. */
3957 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
3958 subpool));
3959 full_path = svn_dirent_join(dirname, name_utf8, subpool);
3960 SVN_ERR(svn_io_dir_walk2(full_path,
3961 wanted,
3962 walk_func,
3963 walk_baton,
3964 subpool));
3966 else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
3968 /* some other directory. pass it to the callback. */
3969 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
3970 subpool));
3971 full_path = svn_dirent_join(dirname, name_utf8, subpool);
3972 SVN_ERR((*walk_func)(walk_baton,
3973 full_path,
3974 &finfo,
3975 subpool));
3977 /* else:
3978 Some other type of file; skip it for now. We've reserved the
3979 right to expand our coverage here in the future, though,
3980 without revving this API.
3984 svn_pool_destroy(subpool);
3986 apr_err = apr_dir_close(handle);
3987 if (apr_err)
3988 return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
3989 svn_dirent_local_style(dirname, pool));
3991 return SVN_NO_ERROR;
3997 * Determine if a directory is empty or not.
3998 * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
3999 * @param path The directory.
4000 * @param pool Used for temporary allocation.
4001 * @remark If path is not a directory, or some other error occurs,
4002 * then return the appropriate apr status code.
4004 * (This function is written in APR style, in anticipation of
4005 * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
4007 static apr_status_t
4008 dir_is_empty(const char *dir, apr_pool_t *pool)
4010 apr_status_t apr_err;
4011 apr_dir_t *dir_handle;
4012 apr_finfo_t finfo;
4013 apr_status_t retval = APR_SUCCESS;
4015 /* APR doesn't like "" directories */
4016 if (dir[0] == '\0')
4017 dir = ".";
4019 apr_err = apr_dir_open(&dir_handle, dir, pool);
4020 if (apr_err != APR_SUCCESS)
4021 return apr_err;
4023 for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle);
4024 apr_err == APR_SUCCESS;
4025 apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle))
4027 /* Ignore entries for this dir and its parent, robustly.
4028 (APR promises that they'll come first, so technically
4029 this guard could be moved outside the loop. But Ryan Bloom
4030 says he doesn't believe it, and I believe him. */
4031 if (! (finfo.name[0] == '.'
4032 && (finfo.name[1] == '\0'
4033 || (finfo.name[1] == '.' && finfo.name[2] == '\0'))))
4035 retval = APR_ENOTEMPTY;
4036 break;
4040 /* Make sure we broke out of the loop for the right reason. */
4041 if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
4042 return apr_err;
4044 apr_err = apr_dir_close(dir_handle);
4045 if (apr_err != APR_SUCCESS)
4046 return apr_err;
4048 return retval;
4052 svn_error_t *
4053 svn_io_dir_empty(svn_boolean_t *is_empty_p,
4054 const char *path,
4055 apr_pool_t *pool)
4057 apr_status_t status;
4058 const char *path_apr;
4060 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4062 status = dir_is_empty(path_apr, pool);
4064 if (!status)
4065 *is_empty_p = TRUE;
4066 else if (APR_STATUS_IS_ENOTEMPTY(status))
4067 *is_empty_p = FALSE;
4068 else
4069 return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
4070 svn_dirent_local_style(path, pool));
4072 return SVN_NO_ERROR;
4077 /*** Version/format files ***/
4079 svn_error_t *
4080 svn_io_write_version_file(const char *path,
4081 int version,
4082 apr_pool_t *pool)
4084 const char *path_tmp;
4085 const char *format_contents = apr_psprintf(pool, "%d\n", version);
4087 SVN_ERR_ASSERT(version >= 0);
4089 SVN_ERR(svn_io_write_unique(&path_tmp,
4090 svn_dirent_dirname(path, pool),
4091 format_contents, strlen(format_contents),
4092 svn_io_file_del_none, pool));
4094 #if defined(WIN32) || defined(__OS2__)
4095 /* make the destination writable, but only on Windows, because
4096 Windows does not let us replace read-only files. */
4097 SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
4098 #endif /* WIN32 || __OS2__ */
4100 /* rename the temp file as the real destination */
4101 SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
4103 /* And finally remove the perms to make it read only */
4104 return svn_io_set_file_read_only(path, FALSE, pool);
4108 svn_error_t *
4109 svn_io_read_version_file(int *version,
4110 const char *path,
4111 apr_pool_t *pool)
4113 apr_file_t *format_file;
4114 char buf[80];
4115 apr_size_t len;
4116 svn_error_t *err;
4118 /* Read a chunk of data from PATH */
4119 SVN_ERR(svn_io_file_open(&format_file, path, APR_READ,
4120 APR_OS_DEFAULT, pool));
4121 len = sizeof(buf);
4122 err = svn_io_file_read(format_file, buf, &len, pool);
4124 /* Close the file. */
4125 SVN_ERR(svn_error_compose_create(err,
4126 svn_io_file_close(format_file, pool)));
4128 /* If there was no data in PATH, return an error. */
4129 if (len == 0)
4130 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
4131 _("Reading '%s'"),
4132 svn_dirent_local_style(path, pool));
4134 /* Check that the first line contains only digits. */
4136 apr_size_t i;
4138 for (i = 0; i < len; ++i)
4140 char c = buf[i];
4142 if (i > 0 && (c == '\r' || c == '\n'))
4144 buf[i] = '\0';
4145 break;
4147 if (! svn_ctype_isdigit(c))
4148 return svn_error_createf
4149 (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
4150 _("First line of '%s' contains non-digit"),
4151 svn_dirent_local_style(path, pool));
4155 /* Convert to integer. */
4156 SVN_ERR(svn_cstring_atoi(version, buf));
4158 return SVN_NO_ERROR;
4163 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
4164 static svn_error_t *
4165 contents_identical_p(svn_boolean_t *identical_p,
4166 const char *file1,
4167 const char *file2,
4168 apr_pool_t *pool)
4170 svn_error_t *err;
4171 apr_size_t bytes_read1, bytes_read2;
4172 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4173 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4174 apr_file_t *file1_h;
4175 apr_file_t *file2_h;
4176 svn_boolean_t eof1 = FALSE;
4177 svn_boolean_t eof2 = FALSE;
4179 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4180 pool));
4182 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4183 pool);
4185 if (err)
4186 return svn_error_trace(
4187 svn_error_compose_create(err,
4188 svn_io_file_close(file1_h, pool)));
4190 *identical_p = TRUE; /* assume TRUE, until disproved below */
4191 while (!err && !eof1 && !eof2)
4193 err = svn_io_file_read_full2(file1_h, buf1,
4194 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4195 &eof1, pool);
4196 if (err)
4197 break;
4199 err = svn_io_file_read_full2(file2_h, buf2,
4200 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4201 &eof2, pool);
4202 if (err)
4203 break;
4205 if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
4207 *identical_p = FALSE;
4208 break;
4212 /* Special case: one file being a prefix of the other and the shorter
4213 * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */
4214 if (!err && (eof1 != eof2))
4215 *identical_p = FALSE;
4217 return svn_error_trace(
4218 svn_error_compose_create(
4219 err,
4220 svn_error_compose_create(svn_io_file_close(file1_h, pool),
4221 svn_io_file_close(file2_h, pool))));
4226 /* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
4227 static svn_error_t *
4228 contents_three_identical_p(svn_boolean_t *identical_p12,
4229 svn_boolean_t *identical_p23,
4230 svn_boolean_t *identical_p13,
4231 const char *file1,
4232 const char *file2,
4233 const char *file3,
4234 apr_pool_t *scratch_pool)
4236 svn_error_t *err;
4237 apr_size_t bytes_read1, bytes_read2, bytes_read3;
4238 char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4239 char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4240 char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4241 apr_file_t *file1_h;
4242 apr_file_t *file2_h;
4243 apr_file_t *file3_h;
4244 svn_boolean_t eof1 = FALSE;
4245 svn_boolean_t eof2 = FALSE;
4246 svn_boolean_t eof3 = FALSE;
4247 svn_boolean_t read_1, read_2, read_3;
4249 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4250 scratch_pool));
4252 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4253 scratch_pool);
4255 if (err)
4256 return svn_error_trace(
4257 svn_error_compose_create(err,
4258 svn_io_file_close(file1_h, scratch_pool)));
4260 err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
4261 scratch_pool);
4263 if (err)
4264 return svn_error_trace(
4265 svn_error_compose_create(
4266 err,
4267 svn_error_compose_create(svn_io_file_close(file1_h,
4268 scratch_pool),
4269 svn_io_file_close(file2_h,
4270 scratch_pool))));
4272 /* assume TRUE, until disproved below */
4273 *identical_p12 = *identical_p23 = *identical_p13 = TRUE;
4274 /* We need to read as long as no error occurs, and as long as one of the
4275 * flags could still change due to a read operation */
4276 while (!err
4277 && ((*identical_p12 && !eof1 && !eof2)
4278 || (*identical_p23 && !eof2 && !eof3)
4279 || (*identical_p13 && !eof1 && !eof3)))
4281 read_1 = read_2 = read_3 = FALSE;
4283 /* As long as a file is not at the end yet, and it is still
4284 * potentially identical to another file, we read the next chunk.*/
4285 if (!eof1 && (identical_p12 || identical_p13))
4287 err = svn_io_file_read_full2(file1_h, buf1,
4288 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4289 &eof1, scratch_pool);
4290 if (err)
4291 break;
4292 read_1 = TRUE;
4295 if (!eof2 && (identical_p12 || identical_p23))
4297 err = svn_io_file_read_full2(file2_h, buf2,
4298 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4299 &eof2, scratch_pool);
4300 if (err)
4301 break;
4302 read_2 = TRUE;
4305 if (!eof3 && (identical_p13 || identical_p23))
4307 err = svn_io_file_read_full2(file3_h, buf3,
4308 SVN__STREAM_CHUNK_SIZE, &bytes_read3,
4309 &eof3, scratch_pool);
4310 if (err)
4311 break;
4312 read_3 = TRUE;
4315 /* If the files are still marked identical, and at least one of them
4316 * is not at the end of file, we check whether they differ, and set
4317 * their flag to false then. */
4318 if (*identical_p12
4319 && (read_1 || read_2)
4320 && ((eof1 != eof2)
4321 || (bytes_read1 != bytes_read2)
4322 || memcmp(buf1, buf2, bytes_read1)))
4324 *identical_p12 = FALSE;
4327 if (*identical_p23
4328 && (read_2 || read_3)
4329 && ((eof2 != eof3)
4330 || (bytes_read2 != bytes_read3)
4331 || memcmp(buf2, buf3, bytes_read2)))
4333 *identical_p23 = FALSE;
4336 if (*identical_p13
4337 && (read_1 || read_3)
4338 && ((eof1 != eof3)
4339 || (bytes_read1 != bytes_read3)
4340 || memcmp(buf1, buf3, bytes_read3)))
4342 *identical_p13 = FALSE;
4346 return svn_error_trace(
4347 svn_error_compose_create(
4348 err,
4349 svn_error_compose_create(
4350 svn_io_file_close(file1_h, scratch_pool),
4351 svn_error_compose_create(
4352 svn_io_file_close(file2_h, scratch_pool),
4353 svn_io_file_close(file3_h, scratch_pool)))));
4358 svn_error_t *
4359 svn_io_files_contents_same_p(svn_boolean_t *same,
4360 const char *file1,
4361 const char *file2,
4362 apr_pool_t *pool)
4364 svn_boolean_t q;
4366 SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
4368 if (q)
4370 *same = FALSE;
4371 return SVN_NO_ERROR;
4374 SVN_ERR(contents_identical_p(&q, file1, file2, pool));
4376 if (q)
4377 *same = TRUE;
4378 else
4379 *same = FALSE;
4381 return SVN_NO_ERROR;
4384 svn_error_t *
4385 svn_io_files_contents_three_same_p(svn_boolean_t *same12,
4386 svn_boolean_t *same23,
4387 svn_boolean_t *same13,
4388 const char *file1,
4389 const char *file2,
4390 const char *file3,
4391 apr_pool_t *scratch_pool)
4393 svn_boolean_t diff_size12, diff_size23, diff_size13;
4395 SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
4396 &diff_size23,
4397 &diff_size13,
4398 file1,
4399 file2,
4400 file3,
4401 scratch_pool));
4403 if (diff_size12 && diff_size23 && diff_size13)
4405 *same12 = *same23 = *same13 = FALSE;
4407 else if (diff_size12 && diff_size23)
4409 *same12 = *same23 = FALSE;
4410 SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
4412 else if (diff_size23 && diff_size13)
4414 *same23 = *same13 = FALSE;
4415 SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
4417 else if (diff_size12 && diff_size13)
4419 *same12 = *same13 = FALSE;
4420 SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
4422 else
4424 SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
4425 SVN_ERR(contents_three_identical_p(same12, same23, same13,
4426 file1, file2, file3,
4427 scratch_pool));
4430 return SVN_NO_ERROR;
4433 #ifdef WIN32
4434 /* Counter value of file_mktemp request (used in a threadsafe way), to make
4435 sure that a single process normally never generates the same tempname
4436 twice */
4437 static volatile apr_uint32_t tempname_counter = 0;
4438 #endif
4440 /* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
4441 Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name.
4442 Perform temporary allocations in SCRATCH_POOL and the result in
4443 RESULT_POOL. */
4444 static svn_error_t *
4445 temp_file_create(apr_file_t **new_file,
4446 const char **new_file_name,
4447 const char *directory,
4448 apr_int32_t flags,
4449 apr_pool_t *result_pool,
4450 apr_pool_t *scratch_pool)
4452 #ifndef WIN32
4453 const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
4454 const char *templ_apr;
4455 apr_status_t status;
4457 SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
4459 /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
4460 data available in POOL and we need a non-const pointer here,
4461 as apr changes the template to return the new filename. */
4462 status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool);
4464 if (status)
4465 return svn_error_wrap_apr(status, _("Can't create temporary file from "
4466 "template '%s'"), templ);
4468 /* Translate the returned path back to utf-8 before returning it */
4469 return svn_error_trace(svn_path_cstring_to_utf8(new_file_name,
4470 templ_apr,
4471 result_pool));
4472 #else
4473 /* The Windows implementation of apr_file_mktemp doesn't handle access
4474 denied errors correctly. Therefore we implement our own temp file
4475 creation function here. */
4477 /* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
4478 ### the function we used before. But we try to guess a more unique
4479 ### name before trying if it exists. */
4481 /* Offset by some time value and a unique request nr to make the number
4482 +- unique for both this process and on the computer */
4483 int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter)
4484 + GetCurrentProcessId();
4485 int i;
4487 /* ### Maybe use an iterpool? */
4488 for (i = 0; i <= 99999; i++)
4490 apr_uint32_t unique_nr;
4491 const char *unique_name;
4492 const char *unique_name_apr;
4493 apr_file_t *try_file;
4494 apr_status_t apr_err;
4496 /* Generate a number that should be unique for this application and
4497 usually for the entire computer to reduce the number of cycles
4498 through this loop. (A bit of calculation is much cheaper then
4499 disk io) */
4500 unique_nr = baseNr + 3 * i;
4502 unique_name = svn_dirent_join(directory,
4503 apr_psprintf(scratch_pool, "svn-%X",
4504 unique_nr),
4505 scratch_pool);
4507 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
4509 apr_err = file_open(&try_file, unique_name_apr, flags,
4510 APR_OS_DEFAULT, FALSE, scratch_pool);
4512 if (APR_STATUS_IS_EEXIST(apr_err))
4513 continue;
4514 else if (apr_err)
4516 /* On Win32, CreateFile fails with an "Access Denied" error
4517 code, rather than "File Already Exists", if the colliding
4518 name belongs to a directory. */
4520 if (APR_STATUS_IS_EACCES(apr_err))
4522 apr_finfo_t finfo;
4523 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
4524 APR_FINFO_TYPE, scratch_pool);
4526 if (!apr_err_2 && finfo.filetype == APR_DIR)
4527 continue;
4529 apr_err_2 = APR_TO_OS_ERROR(apr_err);
4531 if (apr_err_2 == ERROR_ACCESS_DENIED ||
4532 apr_err_2 == ERROR_SHARING_VIOLATION)
4534 /* The file is in use by another process or is hidden;
4535 create a new name, but don't do this 99999 times in
4536 case the folder is not writable */
4537 i += 797;
4538 continue;
4541 /* Else fall through and return the original error. */
4544 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
4545 svn_dirent_local_style(unique_name,
4546 scratch_pool));
4548 else
4550 /* Move file to the right pool */
4551 apr_err = apr_file_setaside(new_file, try_file, result_pool);
4553 if (apr_err)
4554 return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
4555 svn_dirent_local_style(unique_name,
4556 scratch_pool));
4558 *new_file_name = apr_pstrdup(result_pool, unique_name);
4560 return SVN_NO_ERROR;
4564 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
4565 NULL,
4566 _("Unable to make name in '%s'"),
4567 svn_dirent_local_style(directory, scratch_pool));
4568 #endif
4571 /* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
4572 svn_error_t *
4573 svn_io_file_name_get(const char **filename,
4574 apr_file_t *file,
4575 apr_pool_t *pool)
4577 const char *fname_apr;
4578 apr_status_t status;
4580 status = apr_file_name_get(&fname_apr, file);
4581 if (status)
4582 return svn_error_wrap_apr(status, _("Can't get file name"));
4584 if (fname_apr)
4585 SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
4586 else
4587 *filename = NULL;
4589 return SVN_NO_ERROR;
4593 svn_error_t *
4594 svn_io_open_unique_file3(apr_file_t **file,
4595 const char **unique_path,
4596 const char *dirpath,
4597 svn_io_file_del_t delete_when,
4598 apr_pool_t *result_pool,
4599 apr_pool_t *scratch_pool)
4601 apr_file_t *tempfile;
4602 const char *tempname;
4603 struct temp_file_cleanup_s *baton = NULL;
4604 apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
4605 APR_BUFFERED | APR_BINARY);
4606 #if !defined(WIN32) && !defined(__OS2__)
4607 apr_fileperms_t perms;
4608 svn_boolean_t using_system_temp_dir = FALSE;
4609 #endif
4611 SVN_ERR_ASSERT(file || unique_path);
4612 if (file)
4613 *file = NULL;
4614 if (unique_path)
4615 *unique_path = NULL;
4617 if (dirpath == NULL)
4619 #if !defined(WIN32) && !defined(__OS2__)
4620 using_system_temp_dir = TRUE;
4621 #endif
4622 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
4625 switch (delete_when)
4627 case svn_io_file_del_on_pool_cleanup:
4628 baton = apr_palloc(result_pool, sizeof(*baton));
4629 baton->pool = result_pool;
4630 baton->fname_apr = NULL;
4632 /* Because cleanups are run LIFO, we need to make sure to register
4633 our cleanup before the apr_file_close cleanup:
4635 On Windows, you can't remove an open file.
4637 apr_pool_cleanup_register(result_pool, baton,
4638 temp_file_plain_cleanup_handler,
4639 temp_file_child_cleanup_handler);
4641 break;
4642 case svn_io_file_del_on_close:
4643 flags |= APR_DELONCLOSE;
4644 break;
4645 default:
4646 break;
4649 SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
4650 result_pool, scratch_pool));
4652 #if !defined(WIN32) && !defined(__OS2__)
4653 /* apr_file_mktemp() creates files with mode 0600.
4654 * This is appropriate if we're using a system temp dir since we don't
4655 * want to leak sensitive data into temp files other users can read.
4656 * If we're not using a system temp dir we're probably using the
4657 * .svn/tmp area and it's likely that the tempfile will end up being
4658 * copied or renamed into the working copy.
4659 * This would cause working files having mode 0600 while users might
4660 * expect to see 0644 or 0664. So we tweak perms of the tempfile in this
4661 * case, but only if the umask allows it. */
4662 if (!using_system_temp_dir)
4664 SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
4665 SVN_ERR(file_perms_set2(tempfile, perms, scratch_pool));
4667 #endif
4669 if (file)
4670 *file = tempfile;
4671 else
4672 SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
4674 if (unique_path)
4675 *unique_path = tempname; /* Was allocated in result_pool */
4677 if (baton)
4678 SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
4680 return SVN_NO_ERROR;
4683 svn_error_t *
4684 svn_io_file_readline(apr_file_t *file,
4685 svn_stringbuf_t **stringbuf,
4686 const char **eol,
4687 svn_boolean_t *eof,
4688 apr_size_t max_len,
4689 apr_pool_t *result_pool,
4690 apr_pool_t *scratch_pool)
4692 svn_stringbuf_t *str;
4693 const char *eol_str;
4694 apr_size_t numbytes;
4695 char c;
4696 apr_size_t len;
4697 svn_boolean_t found_eof;
4699 str = svn_stringbuf_create_ensure(80, result_pool);
4701 /* Read bytes into STR up to and including, but not storing,
4702 * the next EOL sequence. */
4703 eol_str = NULL;
4704 numbytes = 1;
4705 len = 0;
4706 found_eof = FALSE;
4707 while (!found_eof)
4709 if (len < max_len)
4710 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4711 &found_eof, scratch_pool));
4712 len++;
4713 if (numbytes != 1 || len > max_len)
4715 found_eof = TRUE;
4716 break;
4719 if (c == '\n')
4721 eol_str = "\n";
4723 else if (c == '\r')
4725 eol_str = "\r";
4727 if (!found_eof && len < max_len)
4729 apr_off_t pos;
4731 /* Check for "\r\n" by peeking at the next byte. */
4732 pos = 0;
4733 SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool));
4734 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4735 &found_eof, scratch_pool));
4736 if (numbytes == 1 && c == '\n')
4738 eol_str = "\r\n";
4739 len++;
4741 else
4743 /* Pretend we never peeked. */
4744 SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
4745 found_eof = FALSE;
4746 numbytes = 1;
4750 else
4751 svn_stringbuf_appendbyte(str, c);
4753 if (eol_str)
4754 break;
4757 if (eol)
4758 *eol = eol_str;
4759 if (eof)
4760 *eof = found_eof;
4761 *stringbuf = str;
4763 return SVN_NO_ERROR;