2 * io.c: shared file reading, writing, and probing code.
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
28 #ifndef APR_STATUS_IS_EPERM
31 #define APR_STATUS_IS_EPERM(s) ((s) == EPERM)
33 #define APR_STATUS_IS_EPERM(s) (0)
38 #include <apr_pools.h>
39 #include <apr_file_io.h>
40 #include <apr_file_info.h>
41 #include <apr_general.h>
42 #include <apr_strings.h>
43 #include <apr_portable.h>
46 #include "svn_types.h"
48 #include "svn_string.h"
49 #include "svn_error.h"
51 #include "svn_pools.h"
53 #include "svn_config.h"
54 #include "svn_private_config.h"
57 Windows is 'aided' by a number of types of applications that
58 follow other applications around and open up files they have
59 changed for various reasons (the most intrusive are virus
60 scanners). So, if one of these other apps has glommed onto
61 our file we may get an 'access denied' error.
63 This retry loop does not completely solve the problem (who
64 knows how long the other app is going to hold onto it for), but
65 goes a long way towards minimizing it. It is not an infinite
66 loop because there might really be an error.
68 Another reason for retrying delete operations on Windows
69 is that they are asynchronous -- the file or directory is not
70 actually deleted until the last handle to it is closed. The
71 retry loop cannot completely solve this problem either, but can
75 #define WIN32_RETRY_LOOP(err, expr) \
78 apr_status_t os_err = APR_TO_OS_ERROR(err); \
79 int sleep_count = 1000; \
82 retries < 100 && (os_err == ERROR_ACCESS_DENIED \
83 || os_err == ERROR_SHARING_VIOLATION \
84 || os_err == ERROR_DIR_NOT_EMPTY); \
85 ++retries, os_err = APR_TO_OS_ERROR(err)) \
87 apr_sleep(sleep_count); \
88 if (sleep_count < 128000) \
95 #define WIN32_RETRY_LOOP(err, expr) ((void)0)
100 map_apr_finfo_to_node_kind(svn_node_kind_t
*kind
,
101 svn_boolean_t
*is_special
,
106 if (finfo
->filetype
== APR_REG
)
107 *kind
= svn_node_file
;
108 else if (finfo
->filetype
== APR_DIR
)
109 *kind
= svn_node_dir
;
110 else if (finfo
->filetype
== APR_LNK
)
113 *kind
= svn_node_file
;
116 *kind
= svn_node_unknown
;
119 /* Helper for svn_io_check_path() and svn_io_check_resolved_path();
120 essentially the same semantics as those two, with the obvious
121 interpretation for RESOLVE_SYMLINKS. */
123 io_check_path(const char *path
,
124 svn_boolean_t resolve_symlinks
,
125 svn_boolean_t
*is_special_p
,
126 svn_node_kind_t
*kind
,
131 apr_status_t apr_err
;
132 const char *path_apr
;
133 svn_boolean_t is_special
= FALSE
;
138 /* Not using svn_io_stat() here because we want to check the
139 apr_err return explicitly. */
140 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
142 flags
= resolve_symlinks
? APR_FINFO_MIN
: (APR_FINFO_MIN
| APR_FINFO_LINK
);
143 apr_err
= apr_stat(&finfo
, path_apr
, flags
, pool
);
145 if (APR_STATUS_IS_ENOENT(apr_err
))
146 *kind
= svn_node_none
;
147 else if (APR_STATUS_IS_ENOTDIR(apr_err
))
148 *kind
= svn_node_none
;
150 return svn_error_wrap_apr(apr_err
, _("Can't check path '%s'"),
151 svn_path_local_style(path
, pool
));
153 map_apr_finfo_to_node_kind(kind
, &is_special
, &finfo
);
155 *is_special_p
= is_special
;
161 /* Wrapper for apr_file_open() that handles CCSID problems on OS400 V5R4. */
163 file_open(apr_file_t
**f
,
166 apr_fileperms_t perm
,
172 /* All files in OS400 are tagged with a metadata CCSID (Coded Character Set
173 * Identifier) which indicates the character encoding of the file's
174 * contents. Even binary files are assigned a CCSID, typically the system
175 * CCSID of the machine, which is some variant of EBCDIC (there are many
176 * variants of EBCDIC: CCSID 37 - COM EUROPE EBCDIC, CCSID 273 - AUSTRIAN/
177 * GERMAN EBCDIC, CCSID 284 - SPANISH EBCDIC, etc.. In this comment the
178 * assumed system CCSID is 37).
180 * APR on OS400 V5R4 is built with what IBM calls "UTF support" which means
181 * that within the application text file contents are assumed to be in CCSID
184 * On OS400 when using apr_file_open() to read, write, and/or create a file
185 * there is an interplay between the APR_BINARY flag and the file's CCSID:
187 * File | APR_BINARY | Existing | Created | Conversion | Conversion
188 * Exists? | Flag | File's | File's | When | When
189 * | Passed | CCSID | CCSID | Writing | Reading
190 * --------------------------------------------------------------------
191 * Yes | Yes | 1208 | N/A | None | None
192 * Yes | Yes | 37 | N/A | None | None
193 * Yes | No | 1208 | N/A | None | None
194 * Yes | No | 37 | N/A | 1208-->37 | 37-->1208
195 * No | Yes | N/A | 37 | None | None
196 * No | No | N/A | 1208 | None | None
198 * For example: If an existing file with CCSID 37 is opened for reading
199 * without the APR_BINARY flag, the OS will attempt to convert
200 * the file's contents from EBCDIC 37 to UTF-8.
202 * Now for the problem...
204 * - The files Subversion handles have either binary or UTF-8 content.
206 * - Subversion is not structured to differentiate between text files and
207 * binary files. It just always passes the APR_BINARY flag when calling
210 * So when Subversion creates a new file it always has a CCSID of 37 even
211 * though the file *may* contain UTF-8 encoded text. This isn't a problem
212 * for Subversion directly since it always passes APR_BINARY when opening
213 * files, therefore the content is never converted when reading/writing the
216 * The problem is that other OS400 applications/utilities rely on the CCSID
217 * to represent the file's contents. For example, when a text editor opens
218 * a svnserve.conf file tagged with CCSID 37 but actually containing UTF-8
219 * text, the OS will attempt to convert what it thinks is EBCDIC text to
220 * UTF-8. Worse, if the file is empty, the text editor would save the
221 * contents as EBCDIC. Later, when Subversion opens the conf file it's
222 * reading in "UTF-8" data that is actually EBCDIC.
224 * The solution to this problem is to catch the case where Subversion wants
225 * to create a file and make an initial call to apr_file_open() in text mode
226 * (i.e. without the APR_BINARY flag), close the file, and then re-open the
227 * file in binary mode (i.e. with the APR_BINARY flag).
229 apr_status_t apr_err
;
230 if (flag
& APR_CREATE
)
232 /* If we are trying to create a file on OS400 ensure its CCSID is
234 apr_err
= apr_file_open(f
, fname
, flag
& ~APR_BINARY
, perm
, pool
);
241 /* Unset APR_EXCL so the next call to apr_file_open() doesn't
242 * return an error. */
246 status
= apr_file_open(f
, fname
, flag
, perm
, pool
);
247 WIN32_RETRY_LOOP(status
, apr_file_open(f
, fname
, flag
, perm
, pool
));
253 svn_io_check_resolved_path(const char *path
,
254 svn_node_kind_t
*kind
,
257 svn_boolean_t ignored
;
258 return io_check_path(path
, TRUE
, &ignored
, kind
, pool
);
262 svn_io_check_path(const char *path
,
263 svn_node_kind_t
*kind
,
266 svn_boolean_t ignored
;
267 return io_check_path(path
, FALSE
, &ignored
, kind
, pool
);
271 svn_io_check_special_path(const char *path
,
272 svn_node_kind_t
*kind
,
273 svn_boolean_t
*is_special
,
276 return io_check_path(path
, FALSE
, is_special
, kind
, pool
);
279 struct temp_file_cleanup_s
287 temp_file_plain_cleanup_handler(void *baton
)
289 struct temp_file_cleanup_s
*b
= baton
;
290 apr_status_t apr_err
= APR_SUCCESS
;
294 apr_err
= apr_file_remove(b
->name
, b
->pool
);
295 WIN32_RETRY_LOOP(apr_err
, apr_file_remove(b
->name
, b
->pool
));
303 temp_file_child_cleanup_handler(void *baton
)
305 struct temp_file_cleanup_s
*b
= baton
;
307 apr_pool_cleanup_kill(b
->pool
, b
,
308 temp_file_plain_cleanup_handler
);
315 svn_io_open_unique_file2(apr_file_t
**f
,
316 const char **unique_name_p
,
319 svn_io_file_del_t delete_when
,
324 const char *unique_name
;
325 const char *unique_name_apr
;
326 struct temp_file_cleanup_s
*baton
= NULL
;
328 assert(f
|| unique_name_p
);
330 if (delete_when
== svn_io_file_del_on_pool_cleanup
)
332 baton
= apr_palloc(pool
, sizeof(*baton
));
337 /* Because cleanups are run LIFO, we need to make sure to register
338 our cleanup before the apr_file_close cleanup:
340 On Windows, you can't remove an open file.
342 apr_pool_cleanup_register(pool
, baton
, temp_file_plain_cleanup_handler
,
343 temp_file_child_cleanup_handler
);
346 for (i
= 1; i
<= 99999; i
++)
348 apr_status_t apr_err
;
349 apr_int32_t flag
= (APR_READ
| APR_WRITE
| APR_CREATE
| APR_EXCL
350 | APR_BUFFERED
| APR_BINARY
);
352 if (delete_when
== svn_io_file_del_on_close
)
353 flag
|= APR_DELONCLOSE
;
355 /* Special case the first attempt -- if we can avoid having a
356 generated numeric portion at all, that's best. So first we
357 try with just the suffix; then future tries add a number
358 before the suffix. (A do-while loop could avoid the repeated
359 conditional, but it's not worth the clarity loss.)
361 If the first attempt fails, the first number will be "2".
362 This is good, since "1" would misleadingly imply that
363 the second attempt was actually the first... and if someone's
364 got conflicts on their conflicts, we probably don't want to
365 add to their confusion :-). */
367 unique_name
= apr_psprintf(pool
, "%s%s", path
, suffix
);
369 unique_name
= apr_psprintf(pool
, "%s.%u%s", path
, i
, suffix
);
371 /* Hmmm. Ideally, we would append to a native-encoding buf
372 before starting iteration, then convert back to UTF-8 for
373 return. But I suppose that would make the appending code
374 sensitive to i18n in a way it shouldn't be... Oh well. */
375 SVN_ERR(svn_path_cstring_from_utf8(&unique_name_apr
, unique_name
,
378 apr_err
= file_open(&file
, unique_name_apr
, flag
,
379 APR_OS_DEFAULT
, pool
);
381 if (APR_STATUS_IS_EEXIST(apr_err
))
385 /* On Win32, CreateFile failswith an "Access Denied" error
386 code, rather than "File Already Exists", if the colliding
387 name belongs to a directory. */
388 if (APR_STATUS_IS_EACCES(apr_err
))
391 apr_status_t apr_err_2
= apr_stat(&finfo
, unique_name_apr
,
392 APR_FINFO_TYPE
, pool
);
395 && (finfo
.filetype
== APR_DIR
))
398 /* Else ignore apr_err_2; better to fall through and
399 return the original error. */
403 if (unique_name_p
) *unique_name_p
= NULL
;
404 return svn_error_wrap_apr(apr_err
, _("Can't open '%s'"),
405 svn_path_local_style(unique_name
, pool
));
409 if (delete_when
== svn_io_file_del_on_pool_cleanup
)
410 baton
->name
= unique_name_apr
;
415 apr_file_close(file
);
416 if (unique_name_p
) *unique_name_p
= unique_name
;
423 if (unique_name_p
) *unique_name_p
= NULL
;
424 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED
,
426 _("Unable to make name for '%s'"),
427 svn_path_local_style(path
, pool
));
431 svn_io_open_unique_file(apr_file_t
**f
,
432 const char **unique_name_p
,
435 svn_boolean_t delete_on_close
,
438 return svn_io_open_unique_file2(f
, unique_name_p
,
441 ? svn_io_file_del_on_close
442 : svn_io_file_del_none
,
447 svn_io_create_unique_link(const char **unique_name_p
,
455 const char *unique_name
;
456 const char *unique_name_apr
;
457 const char *dest_apr
;
460 const char *dest_apr_ebcdic
;
463 SVN_ERR(svn_path_cstring_from_utf8(&dest_apr
, dest
, pool
));
466 /* On OS400 with UTF support a native cstring is UTF-8, but
467 * symlink() *really* needs EBCDIC paths. */
468 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&dest_apr_ebcdic
, dest_apr
,
469 (const char*)0, pool
));
470 dest_apr
= dest_apr_ebcdic
;
473 for (i
= 1; i
<= 99999; i
++)
475 apr_status_t apr_err
;
477 /* Special case the first attempt -- if we can avoid having a
478 generated numeric portion at all, that's best. So first we
479 try with just the suffix; then future tries add a number
480 before the suffix. (A do-while loop could avoid the repeated
481 conditional, but it's not worth the clarity loss.)
483 If the first attempt fails, the first number will be "2".
484 This is good, since "1" would misleadingly imply that
485 the second attempt was actually the first... and if someone's
486 got conflicts on their conflicts, we probably don't want to
487 add to their confusion :-). */
489 unique_name
= apr_psprintf(pool
, "%s%s", path
, suffix
);
491 unique_name
= apr_psprintf(pool
, "%s.%u%s", path
, i
, suffix
);
493 /* Hmmm. Ideally, we would append to a native-encoding buf
494 before starting iteration, then convert back to UTF-8 for
495 return. But I suppose that would make the appending code
496 sensitive to i18n in a way it shouldn't be... Oh well. */
498 SVN_ERR(svn_path_cstring_from_utf8(&unique_name_apr
, unique_name
,
501 /* On OS400 with UTF support a native cstring is UTF-8,
502 * but symlink() *really* needs an EBCDIC path. */
503 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&unique_name_apr
, unique_name
,
504 (const char*)0, pool
));
508 rv
= symlink(dest_apr
, unique_name_apr
);
509 } while (rv
== -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
511 apr_err
= apr_get_os_error();
513 if (rv
== -1 && APR_STATUS_IS_EEXIST(apr_err
))
515 else if (rv
== -1 && apr_err
)
517 /* On Win32, CreateFile fails with an "Access Denied" error
518 code, rather than "File Already Exists", if the colliding
519 name belongs to a directory. */
520 if (APR_STATUS_IS_EACCES(apr_err
))
523 apr_status_t apr_err_2
= apr_stat(&finfo
, unique_name_apr
,
524 APR_FINFO_TYPE
, pool
);
527 && (finfo
.filetype
== APR_DIR
))
530 /* Else ignore apr_err_2; better to fall through and
531 return the original error. */
534 *unique_name_p
= NULL
;
535 return svn_error_wrap_apr(apr_err
,
536 _("Can't create symbolic link '%s'"),
537 svn_path_local_style(unique_name
, pool
));
541 *unique_name_p
= unique_name
;
546 *unique_name_p
= NULL
;
547 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED
,
549 _("Unable to make name for '%s'"),
550 svn_path_local_style(path
, pool
));
552 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE
, NULL
,
553 _("Symbolic links are not supported on this "
559 svn_io_read_link(svn_string_t
**dest
,
564 svn_string_t dest_apr
;
565 const char *path_apr
;
569 const char *buf_utf8
;
573 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
575 /* On OS400 with UTF support a native cstring is UTF-8, but
576 * readlink() *really* needs an EBCDIC path. */
577 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&path_apr
, path
, (const char*)0,
581 rv
= readlink(path_apr
, buf
, sizeof(buf
) - 1);
582 } while (rv
== -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
585 return svn_error_wrap_apr
586 (apr_get_os_error(), _("Can't read contents of link"));
593 /* ### Cast needed, one of these interfaces is wrong */
594 SVN_ERR(svn_utf_string_to_utf8((const svn_string_t
**)dest
, &dest_apr
,
597 /* The buf filled by readline() is ebcdic encoded
598 * despite V5R4's UTF support. */
599 SVN_ERR(svn_utf_cstring_to_utf8_ex2(&buf_utf8
, dest_apr
.data
,
600 (const char *)0, pool
));
601 *dest
= svn_string_create(buf_utf8
, pool
);
606 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE
, NULL
,
607 _("Symbolic links are not supported on this "
614 svn_io_copy_link(const char *src
,
620 svn_string_t
*link_dest
;
623 /* Notice what the link is pointing at... */
624 SVN_ERR(svn_io_read_link(&link_dest
, src
, pool
));
626 /* Make a tmp-link pointing at the same thing. */
627 SVN_ERR(svn_io_create_unique_link(&dst_tmp
, dst
, link_dest
->data
,
630 /* Move the tmp-link to link. */
631 return svn_io_file_rename(dst_tmp
, dst
, pool
);
634 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE
, NULL
,
635 _("Symbolic links are not supported on this "
642 svn_io_temp_dir(const char **dir
,
645 apr_status_t apr_err
= apr_temp_dir_get(dir
, pool
);
648 return svn_error_wrap_apr(apr_err
, _("Can't find a temporary directory"));
650 *dir
= svn_path_canonicalize(*dir
, pool
);
652 return svn_path_cstring_to_utf8(dir
, *dir
, pool
);
658 /*** Creating, copying and appending files. ***/
660 /* Transfer the contents of FROM_FILE to TO_FILE, using POOL for temporary
663 * NOTE: We don't use apr_copy_file() for this, since it takes filenames
664 * as parameters. Since we want to copy to a temporary file
665 * and rename for atomicity (see below), this would require an extra
666 * close/open pair, which can be expensive, especially on
667 * remote file systems.
670 * Also, On OS400 apr_file_copy() attempts to convert the contents of
671 * the source file from its CCSID to the CCSID of the destination
672 * file. This may corrupt the destination file's contents if the
673 * files' CCSIDs differ from each other and/or the system CCSID.
674 * (See comments for file_open() for more info on CCSIDs.)
677 copy_contents(apr_file_t
*from_file
,
681 /* Copy bytes till the cows come home. */
684 char buf
[SVN__STREAM_CHUNK_SIZE
];
685 apr_size_t bytes_this_time
= sizeof(buf
);
686 apr_status_t read_err
;
687 apr_status_t write_err
;
690 read_err
= apr_file_read(from_file
, buf
, &bytes_this_time
);
691 if (read_err
&& !APR_STATUS_IS_EOF(read_err
))
697 write_err
= apr_file_write_full(to_file
, buf
, bytes_this_time
, NULL
);
703 if (read_err
&& APR_STATUS_IS_EOF(read_err
))
705 /* Return the results of this close: an error, or success. */
714 svn_io_copy_file(const char *src
,
716 svn_boolean_t copy_perms
,
719 apr_file_t
*from_file
, *to_file
;
720 apr_status_t apr_err
;
721 const char *src_apr
, *dst_tmp_apr
;
723 svn_error_t
*err
, *err2
;
725 SVN_ERR(svn_path_cstring_from_utf8(&src_apr
, src
, pool
));
727 SVN_ERR(svn_io_file_open(&from_file
, src
, APR_READ
| APR_BINARY
,
728 APR_OS_DEFAULT
, pool
));
730 /* For atomicity, we copy to a tmp file and then rename the tmp
731 file over the real destination. */
733 SVN_ERR(svn_io_open_unique_file2(&to_file
, &dst_tmp
, dst
, ".tmp",
734 svn_io_file_del_none
, pool
));
735 SVN_ERR(svn_path_cstring_from_utf8(&dst_tmp_apr
, dst_tmp
, pool
));
737 apr_err
= copy_contents(from_file
, to_file
, pool
);
741 err
= svn_error_wrap_apr
742 (apr_err
, _("Can't copy '%s' to '%s'"),
743 svn_path_local_style(src
, pool
),
744 svn_path_local_style(dst_tmp
, pool
));
749 err2
= svn_io_file_close(from_file
, pool
);
753 svn_error_clear(err2
);
754 err2
= svn_io_file_close(to_file
, pool
);
758 svn_error_clear(err2
);
761 apr_err
= apr_file_remove(dst_tmp_apr
, pool
);
762 WIN32_RETRY_LOOP(apr_err
, apr_file_remove(dst_tmp_apr
, pool
));
766 /* If copying perms, set the perms on dst_tmp now, so they will be
767 atomically inherited in the upcoming rename. But note that we
768 had to wait until now to set perms, because if they say
769 read-only, then we'd have failed filling dst_tmp's contents. */
771 /* ### FIXME: apr_file_copy with perms may fail on Win32. We need a
772 platform-specific implementation to get the permissions right. */
779 SVN_ERR(svn_io_file_open(&s
, src
, APR_READ
, APR_OS_DEFAULT
, pool
));
780 SVN_ERR(svn_io_file_info_get(&finfo
, APR_FINFO_PROT
, s
, pool
));
781 SVN_ERR(svn_io_file_close(s
, pool
));
783 apr_err
= apr_file_perms_set(dst_tmp_apr
, finfo
.protection
);
785 /* We shouldn't be able to get APR_INCOMPLETE or APR_ENOTIMPL
786 here under normal circumstances, because the perms themselves
787 came from a call to apr_file_info_get(), and we already know
788 this is the non-Win32 case. But if it does happen, it's not
790 if ((apr_err
!= APR_SUCCESS
)
791 && (apr_err
!= APR_INCOMPLETE
)
792 && (apr_err
!= APR_ENOTIMPL
))
794 return svn_error_wrap_apr
795 (apr_err
, _("Can't set permissions on '%s'"),
796 svn_path_local_style(dst_tmp
, pool
));
801 return svn_io_file_rename(dst_tmp
, dst
, pool
);
806 svn_io_append_file(const char *src
, const char *dst
, apr_pool_t
*pool
)
808 apr_status_t apr_err
;
809 const char *src_apr
, *dst_apr
;
811 SVN_ERR(svn_path_cstring_from_utf8(&src_apr
, src
, pool
));
812 SVN_ERR(svn_path_cstring_from_utf8(&dst_apr
, dst
, pool
));
814 apr_err
= apr_file_append(src_apr
, dst_apr
, APR_OS_DEFAULT
, pool
);
817 return svn_error_wrap_apr(apr_err
, _("Can't append '%s' to '%s'"),
818 svn_path_local_style(src
, pool
),
819 svn_path_local_style(dst
, pool
));
825 svn_error_t
*svn_io_copy_dir_recursively(const char *src
,
826 const char *dst_parent
,
827 const char *dst_basename
,
828 svn_boolean_t copy_perms
,
829 svn_cancel_func_t cancel_func
,
833 svn_node_kind_t kind
;
835 const char *dst_path
;
837 apr_finfo_t this_entry
;
838 apr_int32_t flags
= APR_FINFO_TYPE
| APR_FINFO_NAME
;
840 /* Make a subpool for recursion */
841 apr_pool_t
*subpool
= svn_pool_create(pool
);
843 /* The 'dst_path' is simply dst_parent/dst_basename */
844 dst_path
= svn_path_join(dst_parent
, dst_basename
, pool
);
846 /* Sanity checks: SRC and DST_PARENT are directories, and
847 DST_BASENAME doesn't already exist in DST_PARENT. */
848 SVN_ERR(svn_io_check_path(src
, &kind
, subpool
));
849 if (kind
!= svn_node_dir
)
850 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND
, NULL
,
851 _("Source '%s' is not a directory"),
852 svn_path_local_style(src
, pool
));
854 SVN_ERR(svn_io_check_path(dst_parent
, &kind
, subpool
));
855 if (kind
!= svn_node_dir
)
856 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND
, NULL
,
857 _("Destination '%s' is not a directory"),
858 svn_path_local_style(dst_parent
, pool
));
860 SVN_ERR(svn_io_check_path(dst_path
, &kind
, subpool
));
861 if (kind
!= svn_node_none
)
862 return svn_error_createf(SVN_ERR_ENTRY_EXISTS
, NULL
,
863 _("Destination '%s' already exists"),
864 svn_path_local_style(dst_path
, pool
));
866 /* Create the new directory. */
867 /* ### TODO: copy permissions (needs apr_file_attrs_get()) */
868 SVN_ERR(svn_io_dir_make(dst_path
, APR_OS_DEFAULT
, pool
));
870 /* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */
871 SVN_ERR(svn_io_dir_open(&this_dir
, src
, subpool
));
873 for (status
= apr_dir_read(&this_entry
, flags
, this_dir
);
874 status
== APR_SUCCESS
;
875 status
= apr_dir_read(&this_entry
, flags
, this_dir
))
877 if ((this_entry
.name
[0] == '.')
878 && ((this_entry
.name
[1] == '\0')
879 || ((this_entry
.name
[1] == '.')
880 && (this_entry
.name
[2] == '\0'))))
886 const char *src_target
, *entryname_utf8
;
889 SVN_ERR(cancel_func(cancel_baton
));
891 SVN_ERR(svn_path_cstring_to_utf8(&entryname_utf8
,
892 this_entry
.name
, subpool
));
893 src_target
= svn_path_join(src
, entryname_utf8
, subpool
);
895 if (this_entry
.filetype
== APR_REG
) /* regular file */
897 const char *dst_target
= svn_path_join(dst_path
, entryname_utf8
,
899 SVN_ERR(svn_io_copy_file(src_target
, dst_target
,
900 copy_perms
, subpool
));
902 else if (this_entry
.filetype
== APR_LNK
) /* symlink */
904 const char *dst_target
= svn_path_join(dst_path
, entryname_utf8
,
906 SVN_ERR(svn_io_copy_link(src_target
, dst_target
,
909 else if (this_entry
.filetype
== APR_DIR
) /* recurse */
911 /* Prevent infinite recursion by filtering off our
912 newly created destination path. */
913 if (strcmp(src
, dst_parent
) == 0
914 && strcmp(entryname_utf8
, dst_basename
) == 0)
917 SVN_ERR(svn_io_copy_dir_recursively
926 /* ### support other APR node types someday?? */
931 if (! (APR_STATUS_IS_ENOENT(status
)))
932 return svn_error_wrap_apr(status
, _("Can't read directory '%s'"),
933 svn_path_local_style(src
, pool
));
935 status
= apr_dir_close(this_dir
);
937 return svn_error_wrap_apr(status
, _("Error closing directory '%s'"),
938 svn_path_local_style(src
, pool
));
940 /* Free any memory used by recursion */
941 svn_pool_destroy(subpool
);
948 svn_io_make_dir_recursively(const char *path
, apr_pool_t
*pool
)
950 const char *path_apr
;
951 apr_status_t apr_err
;
953 if (svn_path_is_empty(path
))
954 /* Empty path (current dir) is assumed to always exist,
955 so we do nothing, per docs. */
958 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
960 apr_err
= apr_dir_make_recursive(path_apr
, APR_OS_DEFAULT
, pool
);
961 WIN32_RETRY_LOOP(apr_err
, apr_dir_make_recursive(path_apr
,
962 APR_OS_DEFAULT
, pool
));
965 return svn_error_wrap_apr(apr_err
, _("Can't make directory '%s'"),
966 svn_path_local_style(path
, pool
));
971 svn_error_t
*svn_io_file_create(const char *file
,
972 const char *contents
,
978 SVN_ERR(svn_io_file_open(&f
, file
,
979 (APR_WRITE
| APR_CREATE
| APR_EXCL
),
982 SVN_ERR(svn_io_file_write_full(f
, contents
, strlen(contents
),
984 SVN_ERR(svn_io_file_close(f
, pool
));
989 svn_error_t
*svn_io_dir_file_copy(const char *src_path
,
990 const char *dest_path
,
994 const char *file_dest_path
= svn_path_join(dest_path
, file
, pool
);
995 const char *file_src_path
= svn_path_join(src_path
, file
, pool
);
997 SVN_ERR(svn_io_copy_file(file_src_path
, file_dest_path
, TRUE
, pool
));
1003 /*** Modtime checking. ***/
1006 svn_io_file_affected_time(apr_time_t
*apr_time
,
1012 SVN_ERR(svn_io_stat(&finfo
, path
, APR_FINFO_MIN
| APR_FINFO_LINK
, pool
));
1014 *apr_time
= finfo
.mtime
;
1016 return SVN_NO_ERROR
;
1021 svn_io_set_file_affected_time(apr_time_t apr_time
,
1025 apr_status_t status
;
1026 const char *native_path
;
1028 apr_utimbuf_t aubuf
;
1032 SVN_ERR(svn_path_cstring_from_utf8(&native_path
, path
, pool
));
1035 status
= apr_file_mtime_set(native_path
, apr_time
, pool
);
1037 /* apr_file_mtime_set() isn't implemented on OS400, but IBM does provide
1038 * the OS400 specific function apr_utime() which can be used instead. */
1040 /* Get the file's current access time, we don't want to change that,
1041 * just the mod time. */
1042 status
= apr_stat(&finfo
, native_path
, APR_FINFO_ATIME
, pool
);
1045 aubuf
.atime
= finfo
.atime
;
1046 aubuf
.mtime
= apr_time
;
1047 status
= apr_utime(native_path
, &aubuf
);
1051 return svn_error_wrap_apr
1052 (status
, _("Can't set access time of '%s'"),
1053 svn_path_local_style(path
, pool
));
1055 return SVN_NO_ERROR
;
1060 svn_io_filesizes_different_p(svn_boolean_t
*different_p
,
1067 apr_status_t status
;
1068 const char *file1_apr
, *file2_apr
;
1070 /* Not using svn_io_stat() because don't want to generate
1071 svn_error_t objects for non-error conditions. */
1073 SVN_ERR(svn_path_cstring_from_utf8(&file1_apr
, file1
, pool
));
1074 SVN_ERR(svn_path_cstring_from_utf8(&file2_apr
, file2
, pool
));
1076 /* Stat both files */
1077 status
= apr_stat(&finfo1
, file1_apr
, APR_FINFO_MIN
, pool
);
1080 /* If we got an error stat'ing a file, it could be because the
1081 file was removed... or who knows. Whatever the case, we
1082 don't know if the filesizes are definitely different, so
1083 assume that they're not. */
1084 *different_p
= FALSE
;
1085 return SVN_NO_ERROR
;
1088 status
= apr_stat(&finfo2
, file2_apr
, APR_FINFO_MIN
, pool
);
1091 /* See previous comment. */
1092 *different_p
= FALSE
;
1093 return SVN_NO_ERROR
;
1096 /* Examine file sizes */
1097 if (finfo1
.size
== finfo2
.size
)
1098 *different_p
= FALSE
;
1100 *different_p
= TRUE
;
1102 return SVN_NO_ERROR
;
1107 svn_io_file_checksum(unsigned char digest
[],
1111 struct apr_md5_ctx_t context
;
1112 apr_file_t
*f
= NULL
;
1114 char *buf
= apr_palloc(pool
, SVN__STREAM_CHUNK_SIZE
);
1117 /* ### The apr_md5 functions return apr_status_t, but they only
1118 return success, and really, what could go wrong? So below, we
1119 ignore their return values. */
1121 apr_md5_init(&context
);
1123 SVN_ERR(svn_io_file_open(&f
, file
, APR_READ
, APR_OS_DEFAULT
, pool
));
1125 len
= SVN__STREAM_CHUNK_SIZE
;
1126 err
= svn_io_file_read(f
, buf
, &len
, pool
);
1129 apr_md5_update(&context
, buf
, len
);
1130 len
= SVN__STREAM_CHUNK_SIZE
;
1131 err
= svn_io_file_read(f
, buf
, &len
, pool
);
1134 if (err
&& ! APR_STATUS_IS_EOF(err
->apr_err
))
1136 svn_error_clear(err
);
1138 SVN_ERR(svn_io_file_close(f
, pool
));
1140 apr_md5_final(digest
, &context
);
1142 return SVN_NO_ERROR
;
1147 /*** Permissions and modes. ***/
1150 /* Given the file specified by PATH_APR, attempt to create an
1151 identical version of it owned by the current user. This is done by
1152 moving it to a temporary location, copying the file back to its old
1153 path, then deleting the temporarily moved version. All temporary
1154 allocations are done in POOL. */
1155 static svn_error_t
*
1156 reown_file(const char *path_apr
,
1159 const char *unique_name
;
1161 SVN_ERR(svn_io_open_unique_file2(NULL
, &unique_name
, path_apr
,
1162 ".tmp", svn_io_file_del_none
, pool
));
1163 SVN_ERR(svn_io_file_rename(path_apr
, unique_name
, pool
));
1164 SVN_ERR(svn_io_copy_file(unique_name
, path_apr
, TRUE
, pool
));
1165 SVN_ERR(svn_io_remove_file(unique_name
, pool
));
1167 return SVN_NO_ERROR
;
1170 /* Determine what the read-write PERMS for PATH should be by ORing
1171 together the permissions of PATH and the permissions of a temporary
1172 file that we create. Unfortunately, this is the only way to
1173 determine which combination of write bits (User/Group/World) should
1174 be set to restore a file from read-only to read-write. Make
1175 temporary allocations in POOL. */
1176 static svn_error_t
*
1177 get_default_file_perms(const char *path
, apr_fileperms_t
*perms
,
1180 apr_status_t status
;
1181 apr_finfo_t tmp_finfo
, finfo
;
1183 const char *tmp_path
;
1184 const char *apr_path
;
1186 /* Get the perms for a newly created file to find out what write
1187 * bits should be set. */
1188 SVN_ERR(svn_io_open_unique_file2(&fd
, &tmp_path
, path
,
1189 ".tmp", svn_io_file_del_on_close
, pool
));
1190 status
= apr_stat(&tmp_finfo
, tmp_path
, APR_FINFO_PROT
, pool
);
1192 return svn_error_wrap_apr(status
, _("Can't get default file perms "
1193 "for file at '%s' (file stat error)"),
1197 /* Get the perms for the original file so we'll have any other bits
1198 * that were already set (like the execute bits, for example). */
1199 SVN_ERR(svn_path_cstring_from_utf8(&apr_path
, path
, pool
));
1200 status
= apr_file_open(&fd
, apr_path
, APR_READ
| APR_BINARY
,
1201 APR_OS_DEFAULT
, pool
);
1203 return svn_error_wrap_apr(status
, _("Can't open file at '%s'"), path
);
1205 status
= apr_stat(&finfo
, apr_path
, APR_FINFO_PROT
, pool
);
1207 return svn_error_wrap_apr(status
, _("Can't get file perms for file at "
1208 "'%s' (file stat error)"), path
);
1211 /* Glom the perms together. */
1212 *perms
= tmp_finfo
.protection
| finfo
.protection
;
1213 return SVN_NO_ERROR
;
1216 /* This is a helper function for the svn_io_set_file_read* functions
1217 that attempts to honor the users umask when dealing with
1218 permission changes. It is a no-op when invoked on a symlink. */
1219 static svn_error_t
*
1220 io_set_file_perms(const char *path
,
1221 svn_boolean_t change_readwrite
,
1222 svn_boolean_t enable_write
,
1223 svn_boolean_t change_executable
,
1224 svn_boolean_t executable
,
1225 svn_boolean_t ignore_enoent
,
1228 apr_status_t status
;
1229 const char *path_apr
;
1231 apr_fileperms_t perms_to_set
;
1233 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
1235 /* Try to change only a minimal amount of the perms first
1236 by getting the current perms and adding bits
1237 only on where read perms are granted. If this fails
1238 fall through to just setting file attributes. */
1239 status
= apr_stat(&finfo
, path_apr
, APR_FINFO_PROT
| APR_FINFO_LINK
, pool
);
1242 if (ignore_enoent
&& APR_STATUS_IS_ENOENT(status
))
1243 return SVN_NO_ERROR
;
1244 else if (status
!= APR_ENOTIMPL
)
1245 return svn_error_wrap_apr(status
,
1246 _("Can't change perms of file '%s'"),
1247 svn_path_local_style(path
, pool
));
1248 return SVN_NO_ERROR
;
1251 if (finfo
.filetype
== APR_LNK
)
1252 return SVN_NO_ERROR
;
1254 perms_to_set
= finfo
.protection
;
1255 if (change_readwrite
)
1257 if (enable_write
) /* Make read-write. */
1258 SVN_ERR(get_default_file_perms(path
, &perms_to_set
, pool
));
1261 if (finfo
.protection
& APR_UREAD
)
1262 perms_to_set
&= ~APR_UWRITE
;
1263 if (finfo
.protection
& APR_GREAD
)
1264 perms_to_set
&= ~APR_GWRITE
;
1265 if (finfo
.protection
& APR_WREAD
)
1266 perms_to_set
&= ~APR_WWRITE
;
1270 if (change_executable
)
1274 if (finfo
.protection
& APR_UREAD
)
1275 perms_to_set
|= APR_UEXECUTE
;
1276 if (finfo
.protection
& APR_GREAD
)
1277 perms_to_set
|= APR_GEXECUTE
;
1278 if (finfo
.protection
& APR_WREAD
)
1279 perms_to_set
|= APR_WEXECUTE
;
1283 if (finfo
.protection
& APR_UREAD
)
1284 perms_to_set
&= ~APR_UEXECUTE
;
1285 if (finfo
.protection
& APR_GREAD
)
1286 perms_to_set
&= ~APR_GEXECUTE
;
1287 if (finfo
.protection
& APR_WREAD
)
1288 perms_to_set
&= ~APR_WEXECUTE
;
1292 /* If we aren't changing anything then just return, this saves
1293 some system calls and helps with shared working copies */
1294 if (perms_to_set
== finfo
.protection
)
1295 return SVN_NO_ERROR
;
1297 status
= apr_file_perms_set(path_apr
, perms_to_set
);
1299 return SVN_NO_ERROR
;
1301 if (APR_STATUS_IS_EPERM(status
))
1303 /* We don't have permissions to change the
1304 permissions! Try a move, copy, and delete
1305 workaround to see if we can get the file owned by
1306 us. If these succeed, try the permissions set
1309 Note that we only attempt this in the
1310 stat-available path. This assumes that the
1311 move-copy workaround will only be helpful on
1312 platforms that implement apr_stat. */
1313 SVN_ERR(reown_file(path_apr
, pool
));
1314 status
= apr_file_perms_set(path_apr
, perms_to_set
);
1318 return SVN_NO_ERROR
;
1320 if (ignore_enoent
&& APR_STATUS_IS_ENOENT(status
))
1321 return SVN_NO_ERROR
;
1322 else if (status
== APR_ENOTIMPL
)
1324 /* At least try to set the attributes. */
1325 apr_fileattrs_t attrs
= 0;
1326 apr_fileattrs_t attrs_values
= 0;
1328 if (change_readwrite
)
1330 attrs
= APR_FILE_ATTR_READONLY
;
1332 attrs_values
= APR_FILE_ATTR_READONLY
;
1334 if (change_executable
)
1336 attrs
= APR_FILE_ATTR_EXECUTABLE
;
1338 attrs_values
= APR_FILE_ATTR_EXECUTABLE
;
1340 status
= apr_file_attrs_set(path_apr
, attrs
, attrs_values
, pool
);
1343 return svn_error_wrap_apr(status
,
1344 _("Can't change perms of file '%s'"),
1345 svn_path_local_style(path
, pool
));
1350 svn_io_set_file_read_write_carefully(const char *path
,
1351 svn_boolean_t enable_write
,
1352 svn_boolean_t ignore_enoent
,
1356 return svn_io_set_file_read_write(path
, ignore_enoent
, pool
);
1357 return svn_io_set_file_read_only(path
, ignore_enoent
, pool
);
1361 svn_io_set_file_read_only(const char *path
,
1362 svn_boolean_t ignore_enoent
,
1365 /* On Windows, just set the file attributes -- on unix call
1366 our internal function which attempts to honor the umask. */
1368 return io_set_file_perms(path
, TRUE
, FALSE
, FALSE
, FALSE
,
1369 ignore_enoent
, pool
);
1371 apr_status_t status
;
1372 const char *path_apr
;
1374 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
1376 status
= apr_file_attrs_set(path_apr
,
1377 APR_FILE_ATTR_READONLY
,
1378 APR_FILE_ATTR_READONLY
,
1381 if (status
&& status
!= APR_ENOTIMPL
)
1382 if (!ignore_enoent
|| !APR_STATUS_IS_ENOENT(status
))
1383 return svn_error_wrap_apr(status
,
1384 _("Can't set file '%s' read-only"),
1385 svn_path_local_style(path
, pool
));
1387 return SVN_NO_ERROR
;
1393 svn_io_set_file_read_write(const char *path
,
1394 svn_boolean_t ignore_enoent
,
1397 /* On Windows, just set the file attributes -- on unix call
1398 our internal function which attempts to honor the umask. */
1400 return io_set_file_perms(path
, TRUE
, TRUE
, FALSE
, FALSE
,
1401 ignore_enoent
, pool
);
1403 apr_status_t status
;
1404 const char *path_apr
;
1406 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
1408 status
= apr_file_attrs_set(path_apr
,
1410 APR_FILE_ATTR_READONLY
,
1413 if (status
&& status
!= APR_ENOTIMPL
)
1414 if (!ignore_enoent
|| !APR_STATUS_IS_ENOENT(status
))
1415 return svn_error_wrap_apr(status
,
1416 _("Can't set file '%s' read-write"),
1417 svn_path_local_style(path
, pool
));
1419 return SVN_NO_ERROR
;
1424 svn_io_set_file_executable(const char *path
,
1425 svn_boolean_t executable
,
1426 svn_boolean_t ignore_enoent
,
1429 /* On Windows, just exit -- on unix call our internal function
1430 which attempts to honor the umask. */
1432 return io_set_file_perms(path
, FALSE
, FALSE
, TRUE
, executable
,
1433 ignore_enoent
, pool
);
1435 return SVN_NO_ERROR
;
1441 svn_io_is_file_executable(svn_boolean_t
*executable
,
1445 #if defined(APR_HAS_USER) && !defined(WIN32)
1446 apr_finfo_t file_info
;
1447 apr_status_t apr_err
;
1451 *executable
= FALSE
;
1453 /* Get file and user info. */
1454 SVN_ERR(svn_io_stat(&file_info
, path
,
1455 (APR_FINFO_PROT
| APR_FINFO_OWNER
),
1457 apr_err
= apr_uid_current(&uid
, &gid
, pool
);
1460 return svn_error_wrap_apr(apr_err
, _("Error getting UID of process"));
1462 /* Check executable bit for current user. */
1463 if (apr_uid_compare(uid
, file_info
.user
) == APR_SUCCESS
)
1464 *executable
= (file_info
.protection
& APR_UEXECUTE
);
1466 else if (apr_gid_compare(gid
, file_info
.group
) == APR_SUCCESS
)
1467 *executable
= (file_info
.protection
& APR_GEXECUTE
);
1470 *executable
= (file_info
.protection
& APR_WEXECUTE
);
1472 #else /* defined(WIN32) || !defined(APR_HAS_USER) */
1473 *executable
= FALSE
;
1476 return SVN_NO_ERROR
;
1480 /*** File locking. ***/
1481 /* Clear all outstanding locks on ARG, an open apr_file_t *. */
1483 svn_io__file_clear_and_close(void *arg
)
1485 apr_status_t apr_err
;
1486 apr_file_t
*f
= arg
;
1489 apr_err
= apr_file_unlock(f
);
1493 /* Close the file. */
1494 apr_err
= apr_file_close(f
);
1502 svn_error_t
*svn_io_file_lock(const char *lock_file
,
1503 svn_boolean_t exclusive
,
1506 return svn_io_file_lock2(lock_file
, exclusive
, FALSE
, pool
);
1509 svn_error_t
*svn_io_file_lock2(const char *lock_file
,
1510 svn_boolean_t exclusive
,
1511 svn_boolean_t nonblocking
,
1514 int locktype
= APR_FLOCK_SHARED
;
1515 apr_file_t
*lockfile_handle
;
1517 apr_status_t apr_err
;
1519 if (exclusive
== TRUE
)
1520 locktype
= APR_FLOCK_EXCLUSIVE
;
1523 if (locktype
== APR_FLOCK_EXCLUSIVE
)
1526 if (nonblocking
== TRUE
)
1527 locktype
|= APR_FLOCK_NONBLOCK
;
1529 SVN_ERR(svn_io_file_open(&lockfile_handle
, lock_file
, flags
,
1533 /* Get lock on the filehandle. */
1534 apr_err
= apr_file_lock(lockfile_handle
, locktype
);
1537 switch (locktype
& APR_FLOCK_TYPEMASK
)
1539 case APR_FLOCK_SHARED
:
1540 return svn_error_wrap_apr
1541 (apr_err
, _("Can't get shared lock on file '%s'"),
1542 svn_path_local_style(lock_file
, pool
));
1543 case APR_FLOCK_EXCLUSIVE
:
1544 return svn_error_wrap_apr
1545 (apr_err
, _("Can't get exclusive lock on file '%s'"),
1546 svn_path_local_style(lock_file
, pool
));
1548 /* Cannot happen. */
1553 apr_pool_cleanup_register(pool
, lockfile_handle
,
1554 svn_io__file_clear_and_close
,
1555 apr_pool_cleanup_null
);
1557 return SVN_NO_ERROR
;
1562 /* Data consistency/coherency operations. */
1564 static svn_error_t
*
1565 do_io_file_wrapper_cleanup(apr_file_t
*file
, apr_status_t status
,
1566 const char *msg
, const char *msg_no_name
,
1569 svn_error_t
*svn_io_file_flush_to_disk(apr_file_t
*file
,
1572 apr_os_file_t filehand
;
1574 /* First make sure that any user-space buffered data is flushed. */
1575 SVN_ERR(do_io_file_wrapper_cleanup(file
, apr_file_flush(file
),
1576 N_("Can't flush file '%s'"),
1577 N_("Can't flush stream"),
1580 apr_os_file_get(&filehand
, file
);
1582 /* Call the operating system specific function to actually force the
1587 if (! FlushFileBuffers(filehand
))
1588 return svn_error_wrap_apr
1589 (apr_get_os_error(), _("Can't flush file to disk"));
1595 rv
= fsync(filehand
);
1596 } while (rv
== -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
1598 /* If the file is in a memory filesystem, fsync() may return
1599 EINVAL. Presumably the user knows the risks, and we can just
1600 ignore the error. */
1601 if (rv
== -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
1602 return SVN_NO_ERROR
;
1605 return svn_error_wrap_apr
1606 (apr_get_os_error(), _("Can't flush file to disk"));
1610 return SVN_NO_ERROR
;
1615 /* TODO write test for these two functions, then refactor. */
1618 svn_stringbuf_from_file2(svn_stringbuf_t
**result
,
1619 const char *filename
,
1622 apr_file_t
*f
= NULL
;
1624 if (filename
[0] == '-' && filename
[1] == '\0')
1626 apr_status_t apr_err
;
1627 if ((apr_err
= apr_file_open_stdin(&f
, pool
)))
1628 return svn_error_wrap_apr(apr_err
, _("Can't open stdin"));
1632 SVN_ERR(svn_io_file_open(&f
, filename
, APR_READ
, APR_OS_DEFAULT
, pool
));
1635 SVN_ERR(svn_stringbuf_from_aprfile(result
, f
, pool
));
1636 SVN_ERR(svn_io_file_close(f
, pool
));
1637 return SVN_NO_ERROR
;
1642 svn_stringbuf_from_file(svn_stringbuf_t
**result
,
1643 const char *filename
,
1646 if (filename
[0] == '-' && filename
[1] == '\0')
1647 return svn_error_create
1648 (SVN_ERR_UNSUPPORTED_FEATURE
, NULL
,
1649 _("Reading from stdin is disallowed"));
1650 return svn_stringbuf_from_file2(result
, filename
, pool
);
1654 /* Get the name of FILE, or NULL if FILE is an unnamed stream. */
1655 static svn_error_t
*
1656 file_name_get(const char **fname_utf8
, apr_file_t
*file
, apr_pool_t
*pool
)
1658 apr_status_t apr_err
;
1661 apr_err
= apr_file_name_get(&fname
, file
);
1663 return svn_error_wrap_apr(apr_err
, _("Can't get file name"));
1666 SVN_ERR(svn_path_cstring_to_utf8(fname_utf8
, fname
, pool
));
1670 return SVN_NO_ERROR
;
1675 svn_stringbuf_from_aprfile(svn_stringbuf_t
**result
,
1681 svn_stringbuf_t
*res
= svn_stringbuf_create("", pool
);
1682 char *buf
= apr_palloc(pool
, SVN__STREAM_CHUNK_SIZE
);
1684 /* XXX: We should check the incoming data for being of type binary. */
1686 /* apr_file_read will not return data and eof in the same call. So this loop
1687 * is safe from missing read data. */
1688 len
= SVN__STREAM_CHUNK_SIZE
;
1689 err
= svn_io_file_read(file
, buf
, &len
, pool
);
1692 svn_stringbuf_appendbytes(res
, buf
, len
);
1693 len
= SVN__STREAM_CHUNK_SIZE
;
1694 err
= svn_io_file_read(file
, buf
, &len
, pool
);
1697 /* Having read all the data we *expect* EOF */
1698 if (err
&& !APR_STATUS_IS_EOF(err
->apr_err
))
1700 svn_error_clear(err
);
1702 /* Null terminate the stringbuf. */
1703 res
->data
[res
->len
] = 0;
1706 return SVN_NO_ERROR
;
1714 svn_io_remove_file(const char *path
, apr_pool_t
*pool
)
1716 apr_status_t apr_err
;
1717 const char *path_apr
;
1720 /* Set the file writable but only on Windows, because Windows
1721 will not allow us to remove files that are read-only. */
1722 SVN_ERR(svn_io_set_file_read_write(path
, TRUE
, pool
));
1725 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
1727 apr_err
= apr_file_remove(path_apr
, pool
);
1728 WIN32_RETRY_LOOP(apr_err
, apr_file_remove(path_apr
, pool
));
1731 return svn_error_wrap_apr(apr_err
, _("Can't remove file '%s'"),
1732 svn_path_local_style(path
, pool
));
1734 return SVN_NO_ERROR
;
1739 svn_io_remove_dir(const char *path
, apr_pool_t
*pool
)
1741 return svn_io_remove_dir2(path
, FALSE
, NULL
, NULL
, pool
);
1745 Mac OS X has a bug where if you're readding the contents of a
1746 directory via readdir in a loop, and you remove one of the entries in
1747 the directory and the directory has 338 or more files in it you will
1748 skip over some of the entries in the directory. Needless to say,
1749 this causes problems if you are using this kind of loop inside a
1750 function that is recursively deleting a directory, because when you
1751 get around to removing the directory it will still have something in
1754 Similar problem has been observed on FreeBSD.
1756 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 for more
1757 discussion and an initial solution.
1759 To work around the problem, we do a rewinddir after we delete all files
1760 and see if there's anything left. We repeat the steps untill there's
1761 nothing left to delete.
1763 This workaround causes issues on Windows where delete's are asynchronous,
1764 however, so we never rewind if we're on Windows (the delete says it is
1765 complete, we rewind, we see the same file and try to delete it again,
1769 /* Neither windows nor unix allows us to delete a non-empty
1772 This is a function to perform the equivalent of 'rm -rf'. */
1774 svn_io_remove_dir2(const char *path
, svn_boolean_t ignore_enoent
,
1775 svn_cancel_func_t cancel_func
, void *cancel_baton
,
1778 apr_status_t status
;
1779 apr_dir_t
*this_dir
;
1780 apr_finfo_t this_entry
;
1781 apr_pool_t
*subpool
;
1782 apr_int32_t flags
= APR_FINFO_TYPE
| APR_FINFO_NAME
;
1783 const char *path_apr
;
1786 /* Check for pending cancellation request.
1787 If we need to bail out, do so early. */
1790 SVN_ERR((*cancel_func
)(cancel_baton
));
1792 /* APR doesn't like "" directories */
1793 if (path
[0] == '\0')
1796 /* Convert path to native here and call apr_dir_open directly,
1797 instead of just using svn_io_dir_open, because we're going to
1798 need path_apr later anyway when we remove the dir itself. */
1800 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
1802 status
= apr_dir_open(&this_dir
, path_apr
, pool
);
1805 /* if the directory doesn't exist, our mission is accomplished */
1806 if (ignore_enoent
&& APR_STATUS_IS_ENOENT(status
))
1807 return SVN_NO_ERROR
;
1809 return svn_error_wrap_apr(status
,
1810 _("Can't open directory '%s'"),
1811 svn_path_local_style(path
, pool
));
1814 subpool
= svn_pool_create(pool
);
1818 need_rewind
= FALSE
;
1820 for (status
= apr_dir_read(&this_entry
, flags
, this_dir
);
1821 status
== APR_SUCCESS
;
1822 status
= apr_dir_read(&this_entry
, flags
, this_dir
))
1824 svn_pool_clear(subpool
);
1825 if ((this_entry
.filetype
== APR_DIR
)
1826 && ((this_entry
.name
[0] == '.')
1827 && ((this_entry
.name
[1] == '\0')
1828 || ((this_entry
.name
[1] == '.')
1829 && (this_entry
.name
[2] == '\0')))))
1833 else /* something other than "." or "..", so proceed */
1835 const char *fullpath
, *entry_utf8
;
1841 SVN_ERR(svn_path_cstring_to_utf8(&entry_utf8
, this_entry
.name
,
1844 fullpath
= svn_path_join(path
, entry_utf8
, subpool
);
1846 if (this_entry
.filetype
== APR_DIR
)
1848 /* Don't check for cancellation, the callee
1849 will immediately do so */
1850 SVN_ERR(svn_io_remove_dir2(fullpath
, FALSE
,
1851 cancel_func
, cancel_baton
,
1859 SVN_ERR((*cancel_func
)(cancel_baton
));
1861 err
= svn_io_remove_file(fullpath
, subpool
);
1863 return svn_error_createf
1864 (err
->apr_err
, err
, _("Can't remove '%s'"),
1865 svn_path_local_style(fullpath
, subpool
));
1872 status
= apr_dir_rewind(this_dir
);
1874 return svn_error_wrap_apr(status
, _("Can't rewind directory '%s'"),
1875 svn_path_local_style (path
, pool
));
1878 while (need_rewind
);
1880 svn_pool_destroy(subpool
);
1882 if (!APR_STATUS_IS_ENOENT(status
))
1883 return svn_error_wrap_apr(status
, _("Can't read directory '%s'"),
1884 svn_path_local_style(path
, pool
));
1886 status
= apr_dir_close(this_dir
);
1888 return svn_error_wrap_apr(status
, _("Error closing directory '%s'"),
1889 svn_path_local_style(path
, pool
));
1891 status
= apr_dir_remove(path_apr
, pool
);
1892 WIN32_RETRY_LOOP(status
, apr_dir_remove(path_apr
, pool
));
1894 return svn_error_wrap_apr(status
, _("Can't remove '%s'"),
1895 svn_path_local_style(path
, pool
));
1901 svn_io_get_dir_filenames(apr_hash_t
**dirents
,
1905 apr_status_t status
;
1906 apr_dir_t
*this_dir
;
1907 apr_finfo_t this_entry
;
1908 apr_int32_t flags
= APR_FINFO_NAME
;
1910 *dirents
= apr_hash_make(pool
);
1912 SVN_ERR(svn_io_dir_open(&this_dir
, path
, pool
));
1914 for (status
= apr_dir_read(&this_entry
, flags
, this_dir
);
1915 status
== APR_SUCCESS
;
1916 status
= apr_dir_read(&this_entry
, flags
, this_dir
))
1918 if ((this_entry
.name
[0] == '.')
1919 && ((this_entry
.name
[1] == '\0')
1920 || ((this_entry
.name
[1] == '.')
1921 && (this_entry
.name
[2] == '\0'))))
1928 SVN_ERR(svn_path_cstring_to_utf8(&name
, this_entry
.name
, pool
));
1929 apr_hash_set(*dirents
, name
, APR_HASH_KEY_STRING
, name
);
1933 if (! (APR_STATUS_IS_ENOENT(status
)))
1934 return svn_error_wrap_apr(status
, _("Can't read directory '%s'"),
1935 svn_path_local_style(path
, pool
));
1937 status
= apr_dir_close(this_dir
);
1939 return svn_error_wrap_apr(status
, _("Error closing directory '%s'"),
1940 svn_path_local_style(path
, pool
));
1942 return SVN_NO_ERROR
;
1946 svn_io_get_dirents2(apr_hash_t
**dirents
,
1950 apr_status_t status
;
1951 apr_dir_t
*this_dir
;
1952 apr_finfo_t this_entry
;
1953 apr_int32_t flags
= APR_FINFO_TYPE
| APR_FINFO_NAME
;
1955 *dirents
= apr_hash_make(pool
);
1957 SVN_ERR(svn_io_dir_open(&this_dir
, path
, pool
));
1959 for (status
= apr_dir_read(&this_entry
, flags
, this_dir
);
1960 status
== APR_SUCCESS
;
1961 status
= apr_dir_read(&this_entry
, flags
, this_dir
))
1963 if ((this_entry
.name
[0] == '.')
1964 && ((this_entry
.name
[1] == '\0')
1965 || ((this_entry
.name
[1] == '.')
1966 && (this_entry
.name
[2] == '\0'))))
1973 svn_io_dirent_t
*dirent
= apr_palloc(pool
, sizeof(*dirent
));
1975 SVN_ERR(svn_path_cstring_to_utf8(&name
, this_entry
.name
, pool
));
1977 map_apr_finfo_to_node_kind(&(dirent
->kind
),
1981 apr_hash_set(*dirents
, name
, APR_HASH_KEY_STRING
, dirent
);
1985 if (! (APR_STATUS_IS_ENOENT(status
)))
1986 return svn_error_wrap_apr(status
, _("Can't read directory '%s'"),
1987 svn_path_local_style(path
, pool
));
1989 status
= apr_dir_close(this_dir
);
1991 return svn_error_wrap_apr(status
, _("Error closing directory '%s'"),
1992 svn_path_local_style(path
, pool
));
1994 return SVN_NO_ERROR
;
1998 svn_io_get_dirents(apr_hash_t
**dirents
,
2002 /* Note that in C, padding is not allowed at the beginning of structs,
2003 so this is actually portable, since the kind field of svn_io_dirent_t
2004 is first in that struct. */
2005 return svn_io_get_dirents2(dirents
, path
, pool
);
2008 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2009 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2011 /* Handle an error from the child process (before command execution) by
2012 printing DESC and the error string corresponding to STATUS to stderr. */
2014 handle_child_process_error(apr_pool_t
*pool
, apr_status_t status
,
2018 apr_file_t
*errfile
;
2021 /* We can't do anything if we get an error here, so just return. */
2022 if (apr_pool_userdata_get(&p
, ERRFILE_KEY
, pool
))
2027 /* What we get from APR is in native encoding. */
2028 apr_file_printf(errfile
, "%s: %s",
2029 desc
, apr_strerror(status
, errbuf
,
2035 svn_io_start_cmd(apr_proc_t
*cmd_proc
,
2038 const char *const *args
,
2039 svn_boolean_t inherit
,
2041 apr_file_t
*outfile
,
2042 apr_file_t
*errfile
,
2045 apr_status_t apr_err
;
2046 apr_procattr_t
*cmdproc_attr
;
2048 const char **args_native
;
2049 const char *cmd_apr
;
2051 /* Create the process attributes. */
2052 apr_err
= apr_procattr_create(&cmdproc_attr
, pool
);
2054 return svn_error_wrap_apr
2055 (apr_err
, _("Can't create process '%s' attributes"), cmd
);
2057 /* Make sure we invoke cmd directly, not through a shell. */
2058 apr_err
= apr_procattr_cmdtype_set(cmdproc_attr
,
2059 inherit
?APR_PROGRAM_PATH
:APR_PROGRAM
);
2061 return svn_error_wrap_apr(apr_err
, _("Can't set process '%s' cmdtype"),
2064 /* Set the process's working directory. */
2067 const char *path_apr
;
2069 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
2070 apr_err
= apr_procattr_dir_set(cmdproc_attr
, path_apr
);
2072 return svn_error_wrap_apr
2073 (apr_err
, _("Can't set process '%s' directory"), cmd
);
2076 /* Use requested inputs and outputs.
2078 ### Unfortunately each of these apr functions creates a pipe and then
2079 overwrites the pipe file descriptor with the descriptor we pass
2080 in. The pipes can then never be closed. This is an APR bug. */
2083 apr_err
= apr_procattr_child_in_set(cmdproc_attr
, infile
, NULL
);
2085 return svn_error_wrap_apr
2086 (apr_err
, _("Can't set process '%s' child input"), cmd
);
2090 apr_err
= apr_procattr_child_out_set(cmdproc_attr
, outfile
, NULL
);
2092 return svn_error_wrap_apr
2093 (apr_err
, _("Can't set process '%s' child outfile"), cmd
);
2097 apr_err
= apr_procattr_child_err_set(cmdproc_attr
, errfile
, NULL
);
2099 return svn_error_wrap_apr
2100 (apr_err
, _("Can't set process '%s' child errfile"), cmd
);
2103 /* Have the child print any problems executing its program to errfile. */
2104 apr_err
= apr_pool_userdata_set(errfile
, ERRFILE_KEY
, NULL
, pool
);
2106 return svn_error_wrap_apr
2107 (apr_err
, _("Can't set process '%s' child errfile for error handler"),
2109 apr_err
= apr_procattr_child_errfn_set(cmdproc_attr
,
2110 handle_child_process_error
);
2112 return svn_error_wrap_apr
2113 (apr_err
, _("Can't set process '%s' error handler"), cmd
);
2115 /* Convert cmd and args from UTF-8 */
2116 SVN_ERR(svn_path_cstring_from_utf8(&cmd_apr
, cmd
, pool
));
2117 for (num_args
= 0; args
[num_args
]; num_args
++)
2119 args_native
= apr_palloc(pool
, (num_args
+ 1) * sizeof(char *));
2120 args_native
[num_args
] = NULL
;
2123 /* ### Well, it turns out that on APR on Windows expects all
2124 program args to be in UTF-8. Callers of svn_io_run_cmd
2125 should be aware of that. */
2126 SVN_ERR(svn_path_cstring_from_utf8(&args_native
[num_args
],
2132 /* Start the cmd command. */
2133 apr_err
= apr_proc_create(cmd_proc
, cmd_apr
, args_native
, NULL
,
2134 cmdproc_attr
, pool
);
2136 return svn_error_wrap_apr(apr_err
, _("Can't start process '%s'"), cmd
);
2138 return SVN_NO_ERROR
;
2144 svn_io_wait_for_cmd(apr_proc_t
*cmd_proc
,
2147 apr_exit_why_e
*exitwhy
,
2150 apr_status_t apr_err
;
2151 apr_exit_why_e exitwhy_val
;
2154 /* The Win32 apr_proc_wait doesn't set this... */
2155 exitwhy_val
= APR_PROC_EXIT
;
2157 /* Wait for the cmd command to finish. */
2158 apr_err
= apr_proc_wait(cmd_proc
, &exitcode_val
, &exitwhy_val
, APR_WAIT
);
2159 if (!APR_STATUS_IS_CHILD_DONE(apr_err
))
2160 return svn_error_wrap_apr(apr_err
, _("Error waiting for process '%s'"),
2164 *exitwhy
= exitwhy_val
;
2165 else if (! APR_PROC_CHECK_EXIT(exitwhy_val
))
2166 return svn_error_createf
2167 (SVN_ERR_EXTERNAL_PROGRAM
, NULL
,
2168 _("Process '%s' failed (exitwhy %d)"), cmd
, exitwhy_val
);
2171 *exitcode
= exitcode_val
;
2172 else if (exitcode_val
!= 0)
2173 return svn_error_createf
2174 (SVN_ERR_EXTERNAL_PROGRAM
, NULL
,
2175 _("Process '%s' returned error exitcode %d"), cmd
, exitcode_val
);
2177 return SVN_NO_ERROR
;
2182 svn_io_run_cmd(const char *path
,
2184 const char *const *args
,
2186 apr_exit_why_e
*exitwhy
,
2187 svn_boolean_t inherit
,
2189 apr_file_t
*outfile
,
2190 apr_file_t
*errfile
,
2193 apr_proc_t cmd_proc
;
2195 SVN_ERR(svn_io_start_cmd(&cmd_proc
, path
, cmd
, args
, inherit
,
2196 infile
, outfile
, errfile
, pool
));
2198 SVN_ERR(svn_io_wait_for_cmd(&cmd_proc
, cmd
, exitcode
, exitwhy
, pool
));
2200 return SVN_NO_ERROR
;
2205 svn_io_run_diff(const char *dir
,
2206 const char *const *user_args
,
2213 apr_file_t
*outfile
,
2214 apr_file_t
*errfile
,
2215 const char *diff_cmd
,
2221 int nargs
= 4; /* the diff command itself, two paths, plus a trailing NULL */
2222 const char *diff_utf8
;
2223 apr_pool_t
*subpool
= svn_pool_create(pool
);
2225 SVN_ERR(svn_path_cstring_to_utf8(&diff_utf8
, diff_cmd
, pool
));
2227 if (pexitcode
== NULL
)
2228 pexitcode
= &exitcode
;
2230 if (user_args
!= NULL
)
2231 nargs
+= num_user_args
;
2233 nargs
+= 1; /* -u */
2236 nargs
+= 2; /* the -L and the label itself */
2238 nargs
+= 2; /* the -L and the label itself */
2240 args
= apr_palloc(subpool
, nargs
* sizeof(char *));
2243 args
[i
++] = diff_utf8
;
2245 if (user_args
!= NULL
)
2248 for (j
= 0; j
< num_user_args
; ++j
)
2249 args
[i
++] = user_args
[j
];
2252 args
[i
++] = "-u"; /* assume -u if the user didn't give us any args */
2265 args
[i
++] = svn_path_local_style(from
, subpool
);
2266 args
[i
++] = svn_path_local_style(to
, subpool
);
2271 SVN_ERR(svn_io_run_cmd(dir
, diff_utf8
, args
, pexitcode
, NULL
, TRUE
,
2272 NULL
, outfile
, errfile
, subpool
));
2274 /* The man page for (GNU) diff describes the return value as:
2276 "An exit status of 0 means no differences were found, 1 means
2277 some differences were found, and 2 means trouble."
2279 A return value of 2 typically occurs when diff cannot read its input
2280 or write to its output, but in any case we probably ought to return an
2281 error for anything other than 0 or 1 as the output is likely to be
2284 if (*pexitcode
!= 0 && *pexitcode
!= 1)
2285 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM
, NULL
,
2286 _("'%s' returned %d"),
2287 svn_path_local_style(diff_utf8
, pool
),
2290 svn_pool_destroy(subpool
);
2292 return SVN_NO_ERROR
;
2298 svn_io_run_diff3_2(int *exitcode
,
2303 const char *mine_label
,
2304 const char *older_label
,
2305 const char *yours_label
,
2307 const char *diff3_cmd
,
2308 const apr_array_header_t
*user_args
,
2311 const char **args
= apr_palloc(pool
,
2316 const char *diff3_utf8
;
2322 SVN_ERR(svn_path_cstring_to_utf8(&diff3_utf8
, diff3_cmd
, pool
));
2324 /* Labels fall back to sensible defaults if not specified. */
2325 if (mine_label
== NULL
)
2326 mine_label
= ".working";
2327 if (older_label
== NULL
)
2328 older_label
= ".old";
2329 if (yours_label
== NULL
)
2330 yours_label
= ".new";
2332 /* Set up diff3 command line. */
2333 args
[i
++] = diff3_utf8
;
2337 for (j
= 0; j
< user_args
->nelts
; ++j
)
2338 args
[i
++] = APR_ARRAY_IDX(user_args
, j
, const char *);
2340 nargs
+= user_args
->nelts
;
2345 args
[i
++] = "-E"; /* We tried "-A" here, but that caused
2346 overlapping identical changes to
2347 conflict. See issue #682. */
2354 args
[i
++] = mine_label
;
2356 args
[i
++] = older_label
; /* note: this label is ignored if
2357 using 2-part markers, which is the
2360 args
[i
++] = yours_label
;
2361 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
2363 svn_boolean_t has_arg
;
2365 /* ### FIXME: we really shouldn't be reading the config here;
2366 instead, the necessary bits should be passed in by the caller.
2367 But should we add another parameter to this function, when the
2368 whole external diff3 thing might eventually go away? */
2372 SVN_ERR(svn_config_get_config(&config
, pool
));
2373 cfg
= config
? apr_hash_get(config
, SVN_CONFIG_CATEGORY_CONFIG
,
2374 APR_HASH_KEY_STRING
) : NULL
;
2375 SVN_ERR(svn_config_get_bool(cfg
, &has_arg
, SVN_CONFIG_SECTION_HELPERS
,
2376 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG
,
2380 const char *diff_cmd
, *diff_utf8
;
2381 svn_config_get(cfg
, &diff_cmd
, SVN_CONFIG_SECTION_HELPERS
,
2382 SVN_CONFIG_OPTION_DIFF_CMD
, SVN_CLIENT_DIFF
);
2383 SVN_ERR(svn_path_cstring_to_utf8(&diff_utf8
, diff_cmd
, pool
));
2384 args
[i
++] = apr_pstrcat(pool
, "--diff-program=", diff_utf8
, NULL
);
2391 args
[i
++] = svn_path_local_style(mine
, pool
);
2392 args
[i
++] = svn_path_local_style(older
, pool
);
2393 args
[i
++] = svn_path_local_style(yours
, pool
);
2397 /* Run diff3, output the merged text into the scratch file. */
2398 SVN_ERR(svn_io_run_cmd(dir
, diff3_utf8
, args
,
2400 TRUE
, /* keep environment */
2404 /* According to the diff3 docs, a '0' means the merge was clean, and
2405 '1' means conflict markers were found. Anything else is real
2407 if ((*exitcode
!= 0) && (*exitcode
!= 1))
2408 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM
, NULL
,
2409 _("Error running '%s': exitcode was %d, "
2411 "\nin directory '%s', basenames:\n%s\n%s\n%s"),
2412 svn_path_local_style(diff3_utf8
, pool
),
2414 svn_path_local_style(dir
, pool
),
2415 /* Don't call svn_path_local_style() on
2416 the basenames. We don't want them to
2417 be absolute, and we don't need the
2418 separator conversion. */
2419 mine
, older
, yours
);
2421 return SVN_NO_ERROR
;
2425 svn_io_run_diff3(const char *dir
,
2429 const char *mine_label
,
2430 const char *older_label
,
2431 const char *yours_label
,
2434 const char *diff3_cmd
,
2437 return svn_io_run_diff3_2(exitcode
, dir
, mine
, older
, yours
,
2438 mine_label
, older_label
, yours_label
,
2439 merged
, diff3_cmd
, NULL
, pool
);
2444 svn_io_parse_mimetypes_file(apr_hash_t
**type_map
,
2445 const char *mimetypes_file
,
2448 svn_error_t
*err
= SVN_NO_ERROR
;
2449 apr_hash_t
*types
= apr_hash_make(pool
);
2450 svn_boolean_t eof
= FALSE
;
2451 svn_stringbuf_t
*buf
;
2452 apr_pool_t
*subpool
= svn_pool_create(pool
);
2453 apr_file_t
*types_file
;
2454 svn_stream_t
*mimetypes_stream
;
2456 SVN_ERR(svn_io_file_open(&types_file
, mimetypes_file
,
2457 APR_READ
, APR_OS_DEFAULT
, pool
));
2458 mimetypes_stream
= svn_stream_from_aprfile2(types_file
, FALSE
, pool
);
2462 apr_array_header_t
*tokens
;
2466 svn_pool_clear(subpool
);
2469 if ((err
= svn_stream_readline(mimetypes_stream
, &buf
,
2470 APR_EOL_STR
, &eof
, subpool
)))
2473 /* Only pay attention to non-empty, non-comment lines. */
2476 if (buf
->data
[0] == '#')
2479 /* Tokenize (into our return pool). */
2480 tokens
= svn_cstring_split(buf
->data
, " \t", TRUE
, pool
);
2481 if (tokens
->nelts
< 2)
2484 /* The first token in a multi-token line is the media type.
2485 Subsequent tokens are filename extensions associated with
2487 type
= APR_ARRAY_IDX(tokens
, 0, const char *);
2488 for (i
= 1; i
< tokens
->nelts
; i
++)
2490 const char *ext
= APR_ARRAY_IDX(tokens
, i
, const char *);
2491 apr_hash_set(types
, ext
, APR_HASH_KEY_STRING
, type
);
2497 svn_pool_destroy(subpool
);
2499 /* If there was an error above, close the file (ignoring any error
2500 from *that*) and return the originally error. */
2503 svn_error_clear(svn_stream_close(mimetypes_stream
));
2507 /* Close the stream (which closes the underlying file, too). */
2508 SVN_ERR(svn_stream_close(mimetypes_stream
));
2511 return SVN_NO_ERROR
;
2516 svn_io_detect_mimetype2(const char **mimetype
,
2518 apr_hash_t
*mimetype_map
,
2521 static const char * const generic_binary
= "application/octet-stream";
2523 svn_node_kind_t kind
;
2526 unsigned char block
[1024];
2527 apr_size_t amt_read
= sizeof(block
);
2529 /* Default return value is NULL. */
2532 /* See if this file even exists, and make sure it really is a file. */
2533 SVN_ERR(svn_io_check_path(file
, &kind
, pool
));
2534 if (kind
!= svn_node_file
)
2535 return svn_error_createf(SVN_ERR_BAD_FILENAME
, NULL
,
2536 _("Can't detect MIME type of non-file '%s'"),
2537 svn_path_local_style(file
, pool
));
2539 /* If there is a mimetype_map provided, we'll first try to look up
2540 our file's extension in the map. Failing that, we'll run the
2544 const char *type_from_map
, *path_ext
;
2545 svn_path_splitext(NULL
, &path_ext
, file
, pool
);
2546 if ((type_from_map
= apr_hash_get(mimetype_map
, path_ext
,
2547 APR_HASH_KEY_STRING
)))
2549 *mimetype
= type_from_map
;
2550 return SVN_NO_ERROR
;
2554 SVN_ERR(svn_io_file_open(&fh
, file
, APR_READ
, 0, pool
));
2556 /* Read a block of data from FILE. */
2557 err
= svn_io_file_read(fh
, block
, &amt_read
, pool
);
2558 if (err
&& ! APR_STATUS_IS_EOF(err
->apr_err
))
2560 svn_error_clear(err
);
2562 /* Now close the file. No use keeping it open any more. */
2563 SVN_ERR(svn_io_file_close(fh
, pool
));
2566 /* Right now, this function is going to be really stupid. It's
2567 going to examine the first block of data, and make sure that 85%
2568 of the bytes are such that their value is in the ranges 0x07-0x0D
2569 or 0x20-0x7F, and that 100% of those bytes is not 0x00.
2571 If those criteria are not met, we're calling it binary. */
2575 int binary_count
= 0;
2577 /* Run through the data we've read, counting the 'binary-ish'
2578 bytes. HINT: If we see a 0x00 byte, we'll set our count to its
2579 max and stop reading the file. */
2580 for (i
= 0; i
< amt_read
; i
++)
2584 binary_count
= amt_read
;
2587 if ((block
[i
] < 0x07)
2588 || ((block
[i
] > 0x0D) && (block
[i
] < 0x20))
2589 || (block
[i
] > 0x7F))
2595 if (((binary_count
* 1000) / amt_read
) > 850)
2597 *mimetype
= generic_binary
;
2598 return SVN_NO_ERROR
;
2602 return SVN_NO_ERROR
;
2607 svn_io_detect_mimetype(const char **mimetype
,
2611 return svn_io_detect_mimetype2(mimetype
, file
, NULL
, pool
);
2615 svn_io_file_open(apr_file_t
**new_file
, const char *fname
,
2616 apr_int32_t flag
, apr_fileperms_t perm
,
2619 const char *fname_apr
;
2620 apr_status_t status
;
2622 SVN_ERR(svn_path_cstring_from_utf8(&fname_apr
, fname
, pool
));
2623 status
= file_open(new_file
, fname_apr
, flag
| APR_BINARY
, perm
, pool
);
2626 return svn_error_wrap_apr(status
, _("Can't open file '%s'"),
2627 svn_path_local_style(fname
, pool
));
2629 return SVN_NO_ERROR
;
2633 static svn_error_t
*
2634 do_io_file_wrapper_cleanup(apr_file_t
*file
, apr_status_t status
,
2635 const char *msg
, const char *msg_no_name
,
2642 return SVN_NO_ERROR
;
2644 err
= file_name_get(&name
, file
, pool
);
2647 svn_error_clear(err
);
2650 return svn_error_wrap_apr(status
, _(msg
),
2651 svn_path_local_style(name
, pool
));
2653 return svn_error_wrap_apr(status
, _(msg_no_name
));
2658 svn_io_file_close(apr_file_t
*file
, apr_pool_t
*pool
)
2660 return do_io_file_wrapper_cleanup
2661 (file
, apr_file_close(file
),
2662 N_("Can't close file '%s'"),
2663 N_("Can't close stream"),
2669 svn_io_file_getc(char *ch
, apr_file_t
*file
, apr_pool_t
*pool
)
2671 return do_io_file_wrapper_cleanup
2672 (file
, apr_file_getc(ch
, file
),
2673 N_("Can't read file '%s'"),
2674 N_("Can't read stream"),
2680 svn_io_file_info_get(apr_finfo_t
*finfo
, apr_int32_t wanted
,
2681 apr_file_t
*file
, apr_pool_t
*pool
)
2683 return do_io_file_wrapper_cleanup
2684 (file
, apr_file_info_get(finfo
, wanted
, file
),
2685 N_("Can't get attribute information from file '%s'"),
2686 N_("Can't get attribute information from stream"),
2692 svn_io_file_read(apr_file_t
*file
, void *buf
,
2693 apr_size_t
*nbytes
, apr_pool_t
*pool
)
2695 return do_io_file_wrapper_cleanup
2696 (file
, apr_file_read(file
, buf
, nbytes
),
2697 N_("Can't read file '%s'"),
2698 N_("Can't read stream"),
2704 svn_io_file_read_full(apr_file_t
*file
, void *buf
,
2705 apr_size_t nbytes
, apr_size_t
*bytes_read
,
2708 return do_io_file_wrapper_cleanup
2709 (file
, apr_file_read_full(file
, buf
, nbytes
, bytes_read
),
2710 N_("Can't read file '%s'"),
2711 N_("Can't read stream"),
2717 svn_io_file_seek(apr_file_t
*file
, apr_seek_where_t where
,
2718 apr_off_t
*offset
, apr_pool_t
*pool
)
2720 return do_io_file_wrapper_cleanup
2721 (file
, apr_file_seek(file
, where
, offset
),
2722 N_("Can't set position pointer in file '%s'"),
2723 N_("Can't set position pointer in stream"),
2729 svn_io_file_write(apr_file_t
*file
, const void *buf
,
2730 apr_size_t
*nbytes
, apr_pool_t
*pool
)
2732 return do_io_file_wrapper_cleanup
2733 (file
, apr_file_write(file
, buf
, nbytes
),
2734 N_("Can't write to file '%s'"),
2735 N_("Can't write to stream"),
2741 svn_io_file_write_full(apr_file_t
*file
, const void *buf
,
2742 apr_size_t nbytes
, apr_size_t
*bytes_written
,
2745 apr_status_t rv
= apr_file_write_full(file
, buf
, nbytes
, bytes_written
);
2748 #define MAXBUFSIZE 30*1024
2749 if (rv
== APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY
)
2750 && nbytes
> MAXBUFSIZE
)
2756 rv
= apr_file_write_full(file
, buf
,
2757 nbytes
> MAXBUFSIZE
? MAXBUFSIZE
: nbytes
, &bw
);
2758 *bytes_written
+= bw
;
2759 buf
= (char *)buf
+ bw
;
2761 } while (rv
== APR_SUCCESS
&& nbytes
> 0);
2766 return do_io_file_wrapper_cleanup
2768 N_("Can't write to file '%s'"),
2769 N_("Can't write to stream"),
2775 svn_io_read_length_line(apr_file_t
*file
, char *buf
, apr_size_t
*limit
,
2783 for (i
= 0; i
< *limit
; i
++)
2785 SVN_ERR(svn_io_file_getc(&c
, file
, pool
));
2786 /* Note: this error could be APR_EOF, which
2787 is totally fine. The caller should be aware of
2794 return SVN_NO_ERROR
;
2802 err
= file_name_get(&name
, file
, pool
);
2805 svn_error_clear(err
);
2808 return svn_error_createf(SVN_ERR_MALFORMED_FILE
, NULL
,
2809 _("Can't read length line in file '%s'"),
2810 svn_path_local_style(name
, pool
));
2812 return svn_error_create(SVN_ERR_MALFORMED_FILE
, NULL
,
2813 _("Can't read length line in stream"));
2818 svn_io_stat(apr_finfo_t
*finfo
, const char *fname
,
2819 apr_int32_t wanted
, apr_pool_t
*pool
)
2821 apr_status_t status
;
2822 const char *fname_apr
;
2824 /* APR doesn't like "" directories */
2825 if (fname
[0] == '\0')
2828 SVN_ERR(svn_path_cstring_from_utf8(&fname_apr
, fname
, pool
));
2830 status
= apr_stat(finfo
, fname_apr
, wanted
, pool
);
2832 return svn_error_wrap_apr(status
, _("Can't stat '%s'"),
2833 svn_path_local_style(fname
, pool
));
2835 return SVN_NO_ERROR
;
2840 svn_io_file_rename(const char *from_path
, const char *to_path
,
2843 apr_status_t status
= APR_SUCCESS
;
2844 const char *from_path_apr
, *to_path_apr
;
2847 /* Set the destination file writable but only on Windows, because
2848 Windows will not allow us to rename over files that are read-only. */
2849 SVN_ERR(svn_io_set_file_read_write(to_path
, TRUE
, pool
));
2852 SVN_ERR(svn_path_cstring_from_utf8(&from_path_apr
, from_path
, pool
));
2853 SVN_ERR(svn_path_cstring_from_utf8(&to_path_apr
, to_path
, pool
));
2855 status
= apr_file_rename(from_path_apr
, to_path_apr
, pool
);
2856 WIN32_RETRY_LOOP(status
,
2857 apr_file_rename(from_path_apr
, to_path_apr
, pool
));
2860 return svn_error_wrap_apr(status
, _("Can't move '%s' to '%s'"),
2861 svn_path_local_style(from_path
, pool
),
2862 svn_path_local_style(to_path
, pool
));
2864 return SVN_NO_ERROR
;
2869 svn_io_file_move(const char *from_path
, const char *to_path
,
2872 svn_error_t
*err
= svn_io_file_rename(from_path
, to_path
, pool
);
2874 if (err
&& APR_STATUS_IS_EXDEV(err
->apr_err
))
2876 const char *tmp_to_path
;
2878 svn_error_clear(err
);
2880 SVN_ERR(svn_io_open_unique_file2(NULL
, &tmp_to_path
, to_path
,
2881 "tmp", svn_io_file_del_none
, pool
));
2883 err
= svn_io_copy_file(from_path
, tmp_to_path
, TRUE
, pool
);
2887 err
= svn_io_file_rename(tmp_to_path
, to_path
, pool
);
2891 err
= svn_io_remove_file(from_path
, pool
);
2893 return SVN_NO_ERROR
;
2895 svn_error_clear(svn_io_remove_file(to_path
, pool
));
2900 svn_error_clear(svn_io_remove_file(tmp_to_path
, pool
));
2906 /* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
2907 HIDDEN determines if the hidden attribute
2908 should be set on the newly created directory. */
2909 static svn_error_t
*
2910 dir_make(const char *path
, apr_fileperms_t perm
,
2911 svn_boolean_t hidden
, svn_boolean_t sgid
, apr_pool_t
*pool
)
2913 apr_status_t status
;
2914 const char *path_apr
;
2916 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
2918 /* APR doesn't like "" directories */
2919 if (path_apr
[0] == '\0')
2922 #if (APR_OS_DEFAULT & APR_WSTICKY)
2923 /* The APR shipped with httpd 2.0.50 contains a bug where
2924 APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
2925 There is a special case for file creation, but not directory
2926 creation, so directories wind up getting created with the sticky
2927 bit set. (There is no such thing as a setuid directory, and the
2928 setgid bit is apparently ignored at mkdir() time.) If we detect
2929 this problem, work around it by unsetting those bits if we are
2930 passed APR_OS_DEFAULT. */
2931 if (perm
== APR_OS_DEFAULT
)
2932 perm
&= ~(APR_USETID
| APR_GSETID
| APR_WSTICKY
);
2935 status
= apr_dir_make(path_apr
, perm
, pool
);
2936 WIN32_RETRY_LOOP(status
, apr_dir_make(path_apr
, perm
, pool
));
2939 return svn_error_wrap_apr(status
, _("Can't create directory '%s'"),
2940 svn_path_local_style(path
, pool
));
2942 #ifdef APR_FILE_ATTR_HIDDEN
2945 status
= apr_file_attrs_set(path_apr
,
2946 APR_FILE_ATTR_HIDDEN
,
2947 APR_FILE_ATTR_HIDDEN
,
2950 return svn_error_wrap_apr(status
, _("Can't hide directory '%s'"),
2951 svn_path_local_style(path
, pool
));
2959 /* Per our contract, don't do error-checking. Some filesystems
2960 * don't support the sgid bit, and that's okay. */
2961 status
= apr_stat(&finfo
, path_apr
, APR_FINFO_PROT
, pool
);
2964 apr_file_perms_set(path_apr
, finfo
.protection
| APR_GSETID
);
2967 return SVN_NO_ERROR
;
2971 svn_io_dir_make(const char *path
, apr_fileperms_t perm
, apr_pool_t
*pool
)
2973 return dir_make(path
, perm
, FALSE
, FALSE
, pool
);
2977 svn_io_dir_make_hidden(const char *path
, apr_fileperms_t perm
,
2980 return dir_make(path
, perm
, TRUE
, FALSE
, pool
);
2984 svn_io_dir_make_sgid(const char *path
, apr_fileperms_t perm
,
2987 return dir_make(path
, perm
, FALSE
, TRUE
, pool
);
2992 svn_io_dir_open(apr_dir_t
**new_dir
, const char *dirname
, apr_pool_t
*pool
)
2994 apr_status_t status
;
2995 const char *dirname_apr
;
2997 /* APR doesn't like "" directories */
2998 if (dirname
[0] == '\0')
3001 SVN_ERR(svn_path_cstring_from_utf8(&dirname_apr
, dirname
, pool
));
3003 status
= apr_dir_open(new_dir
, dirname_apr
, pool
);
3005 return svn_error_wrap_apr(status
, _("Can't open directory '%s'"),
3006 svn_path_local_style(dirname
, pool
));
3008 return SVN_NO_ERROR
;
3013 svn_io_dir_remove_nonrecursive(const char *dirname
, apr_pool_t
*pool
)
3015 apr_status_t status
;
3016 const char *dirname_apr
;
3018 SVN_ERR(svn_path_cstring_from_utf8(&dirname_apr
, dirname
, pool
));
3020 status
= apr_dir_remove(dirname_apr
, pool
);
3021 WIN32_RETRY_LOOP(status
, apr_dir_remove(dirname_apr
, pool
));
3023 return svn_error_wrap_apr(status
, _("Can't remove directory '%s'"),
3024 svn_path_local_style(dirname
, pool
));
3026 return SVN_NO_ERROR
;
3031 svn_io_dir_read(apr_finfo_t
*finfo
,
3036 apr_status_t status
;
3038 status
= apr_dir_read(finfo
, wanted
, thedir
);
3041 return svn_error_wrap_apr(status
, _("Can't read directory"));
3044 SVN_ERR(svn_path_cstring_to_utf8(&finfo
->fname
, finfo
->fname
, pool
));
3047 SVN_ERR(svn_path_cstring_to_utf8(&finfo
->name
, finfo
->name
, pool
));
3049 return SVN_NO_ERROR
;
3054 svn_io_dir_walk(const char *dirname
,
3056 svn_io_walk_func_t walk_func
,
3060 apr_status_t apr_err
;
3062 apr_pool_t
*subpool
;
3063 const char *dirname_apr
;
3066 wanted
|= APR_FINFO_TYPE
| APR_FINFO_NAME
;
3068 /* The documentation for apr_dir_read used to state that "." and ".."
3069 will be returned as the first two files, but it doesn't
3070 work that way in practice, in particular ext3 on Linux-2.6 doesn't
3071 follow the rules. For details see
3072 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
3074 If APR ever does implement "dot-first" then it would be possible to
3075 remove the svn_io_stat and walk_func calls and use the walk_func
3078 Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
3079 documented to provide it, so we have to do a bit extra. */
3080 SVN_ERR(svn_io_stat(&finfo
, dirname
, wanted
& ~APR_FINFO_NAME
, pool
));
3081 SVN_ERR(svn_path_cstring_from_utf8(&finfo
.name
,
3082 svn_path_basename(dirname
, pool
),
3084 finfo
.valid
|= APR_FINFO_NAME
;
3085 SVN_ERR((*walk_func
)(walk_baton
, dirname
, &finfo
, pool
));
3087 SVN_ERR(svn_path_cstring_from_utf8(&dirname_apr
, dirname
, pool
));
3089 apr_err
= apr_dir_open(&handle
, dirname_apr
, pool
);
3091 return svn_error_wrap_apr(apr_err
, _("Can't open directory '%s'"),
3092 svn_path_local_style(dirname
, pool
));
3094 /* iteration subpool */
3095 subpool
= svn_pool_create(pool
);
3099 const char *name_utf8
;
3100 const char *full_path
;
3102 svn_pool_clear(subpool
);
3104 apr_err
= apr_dir_read(&finfo
, wanted
, handle
);
3105 if (APR_STATUS_IS_ENOENT(apr_err
))
3109 return svn_error_wrap_apr
3110 (apr_err
, _("Can't read directory entry in '%s'"),
3111 svn_path_local_style(dirname
, pool
));
3114 if (finfo
.filetype
== APR_DIR
)
3116 if (finfo
.name
[0] == '.'
3117 && (finfo
.name
[1] == '\0'
3118 || (finfo
.name
[1] == '.' && finfo
.name
[2] == '\0')))
3119 /* skip "." and ".." */
3122 /* some other directory. recurse. it will be passed to the
3123 callback inside the recursion. */
3124 SVN_ERR(svn_path_cstring_to_utf8(&name_utf8
, finfo
.name
,
3126 full_path
= svn_path_join(dirname
, name_utf8
, subpool
);
3127 SVN_ERR(svn_io_dir_walk(full_path
,
3133 else if (finfo
.filetype
== APR_REG
)
3135 /* some other directory. pass it to the callback. */
3136 SVN_ERR(svn_path_cstring_to_utf8(&name_utf8
, finfo
.name
,
3138 full_path
= svn_path_join(dirname
, name_utf8
, subpool
);
3139 SVN_ERR((*walk_func
)(walk_baton
,
3145 some other type of file; skip it.
3150 svn_pool_destroy(subpool
);
3152 apr_err
= apr_dir_close(handle
);
3154 return svn_error_wrap_apr(apr_err
, _("Error closing directory '%s'"),
3155 svn_path_local_style(dirname
, pool
));
3157 return SVN_NO_ERROR
;
3163 * Determine if a directory is empty or not.
3164 * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
3165 * @param path The directory.
3166 * @param pool Used for temporary allocation.
3167 * @remark If path is not a directory, or some other error occurs,
3168 * then return the appropriate apr status code.
3170 * (This function is written in APR style, in anticipation of
3171 * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
3174 dir_is_empty(const char *dir
, apr_pool_t
*pool
)
3176 apr_status_t apr_err
;
3177 apr_dir_t
*dir_handle
;
3179 apr_status_t retval
= APR_SUCCESS
;
3181 /* APR doesn't like "" directories */
3185 apr_err
= apr_dir_open(&dir_handle
, dir
, pool
);
3186 if (apr_err
!= APR_SUCCESS
)
3189 for (apr_err
= apr_dir_read(&finfo
, APR_FINFO_NAME
, dir_handle
);
3190 apr_err
== APR_SUCCESS
;
3191 apr_err
= apr_dir_read(&finfo
, APR_FINFO_NAME
, dir_handle
))
3193 /* Ignore entries for this dir and its parent, robustly.
3194 (APR promises that they'll come first, so technically
3195 this guard could be moved outside the loop. But Ryan Bloom
3196 says he doesn't believe it, and I believe him. */
3197 if (! (finfo
.name
[0] == '.'
3198 && (finfo
.name
[1] == '\0'
3199 || (finfo
.name
[1] == '.' && finfo
.name
[2] == '\0'))))
3201 retval
= APR_ENOTEMPTY
;
3206 /* Make sure we broke out of the loop for the right reason. */
3207 if (apr_err
&& ! APR_STATUS_IS_ENOENT(apr_err
))
3210 apr_err
= apr_dir_close(dir_handle
);
3211 if (apr_err
!= APR_SUCCESS
)
3219 svn_io_dir_empty(svn_boolean_t
*is_empty_p
,
3223 apr_status_t status
;
3224 const char *path_apr
;
3226 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
3228 status
= dir_is_empty(path_apr
, pool
);
3232 else if (APR_STATUS_IS_ENOTEMPTY(status
))
3233 *is_empty_p
= FALSE
;
3235 return svn_error_wrap_apr(status
, _("Can't check directory '%s'"),
3236 svn_path_local_style(path
, pool
));
3238 return SVN_NO_ERROR
;
3243 /*** Version/format files ***/
3246 svn_io_write_version_file(const char *path
,
3250 apr_file_t
*format_file
= NULL
;
3251 const char *path_tmp
;
3252 const char *format_contents
= apr_psprintf(pool
, "%d\n", version
);
3254 /* We only promise to handle non-negative integers. */
3256 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS
, NULL
,
3257 _("Version %d is not non-negative"), version
);
3259 /* Create a temporary file to write the data to */
3260 SVN_ERR(svn_io_open_unique_file2(&format_file
, &path_tmp
, path
, ".tmp",
3261 svn_io_file_del_none
, pool
));
3263 /* ...dump out our version number string... */
3264 SVN_ERR(svn_io_file_write_full(format_file
, format_contents
,
3265 strlen(format_contents
), NULL
, pool
));
3267 /* ...and close the file. */
3268 SVN_ERR(svn_io_file_close(format_file
, pool
));
3271 /* make the destination writable, but only on Windows, because
3272 Windows does not let us replace read-only files. */
3273 SVN_ERR(svn_io_set_file_read_write(path
, TRUE
, pool
));
3276 /* rename the temp file as the real destination */
3277 SVN_ERR(svn_io_file_rename(path_tmp
, path
, pool
));
3279 /* And finally remove the perms to make it read only */
3280 SVN_ERR(svn_io_set_file_read_only(path
, FALSE
, pool
));
3282 return SVN_NO_ERROR
;
3287 svn_io_read_version_file(int *version
,
3291 apr_file_t
*format_file
;
3295 /* Read a chunk of data from PATH */
3296 SVN_ERR(svn_io_file_open(&format_file
, path
, APR_READ
,
3297 APR_OS_DEFAULT
, pool
));
3299 SVN_ERR(svn_io_file_read(format_file
, buf
, &len
, pool
));
3301 /* Close the file. */
3302 SVN_ERR(svn_io_file_close(format_file
, pool
));
3304 /* If there was no data in PATH, return an error. */
3306 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF
, NULL
,
3308 svn_path_local_style(path
, pool
));
3310 /* Check that the first line contains only digits. */
3314 for (i
= 0; i
< len
; ++i
)
3318 if (i
> 0 && (c
== '\r' || c
== '\n'))
3320 if (! apr_isdigit(c
))
3321 return svn_error_createf
3322 (SVN_ERR_BAD_VERSION_FILE_FORMAT
, NULL
,
3323 _("First line of '%s' contains non-digit"),
3324 svn_path_local_style(path
, pool
));
3328 /* Convert to integer. */
3329 *version
= atoi(buf
);
3331 return SVN_NO_ERROR
;
3336 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
3337 static svn_error_t
*
3338 contents_identical_p(svn_boolean_t
*identical_p
,
3345 apr_size_t bytes_read1
, bytes_read2
;
3346 char *buf1
= apr_palloc(pool
, SVN__STREAM_CHUNK_SIZE
);
3347 char *buf2
= apr_palloc(pool
, SVN__STREAM_CHUNK_SIZE
);
3348 apr_file_t
*file1_h
= NULL
;
3349 apr_file_t
*file2_h
= NULL
;
3351 SVN_ERR(svn_io_file_open(&file1_h
, file1
, APR_READ
, APR_OS_DEFAULT
,
3353 SVN_ERR(svn_io_file_open(&file2_h
, file2
, APR_READ
, APR_OS_DEFAULT
,
3356 *identical_p
= TRUE
; /* assume TRUE, until disproved below */
3359 err1
= svn_io_file_read_full(file1_h
, buf1
,
3360 SVN__STREAM_CHUNK_SIZE
, &bytes_read1
, pool
);
3361 if (err1
&& !APR_STATUS_IS_EOF(err1
->apr_err
))
3364 err2
= svn_io_file_read_full(file2_h
, buf2
,
3365 SVN__STREAM_CHUNK_SIZE
, &bytes_read2
, pool
);
3366 if (err2
&& !APR_STATUS_IS_EOF(err2
->apr_err
))
3368 svn_error_clear(err1
);
3372 if ((bytes_read1
!= bytes_read2
)
3373 || (memcmp(buf1
, buf2
, bytes_read1
)))
3375 *identical_p
= FALSE
;
3378 } while (! err1
&& ! err2
);
3380 svn_error_clear(err1
);
3381 svn_error_clear(err2
);
3383 SVN_ERR(svn_io_file_close(file1_h
, pool
));
3384 SVN_ERR(svn_io_file_close(file2_h
, pool
));
3386 return SVN_NO_ERROR
;
3392 svn_io_files_contents_same_p(svn_boolean_t
*same
,
3399 SVN_ERR(svn_io_filesizes_different_p(&q
, file1
, file2
, pool
));
3404 return SVN_NO_ERROR
;
3407 SVN_ERR(contents_identical_p(&q
, file1
, file2
, pool
));
3414 return SVN_NO_ERROR
;