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_ensure(SVN__STREAM_CHUNK_SIZE
,
1683 char *buf
= apr_palloc(pool
, SVN__STREAM_CHUNK_SIZE
);
1685 /* XXX: We should check the incoming data for being of type binary. */
1687 /* apr_file_read will not return data and eof in the same call. So this loop
1688 * is safe from missing read data. */
1689 len
= SVN__STREAM_CHUNK_SIZE
;
1690 err
= svn_io_file_read(file
, buf
, &len
, pool
);
1693 svn_stringbuf_appendbytes(res
, buf
, len
);
1694 len
= SVN__STREAM_CHUNK_SIZE
;
1695 err
= svn_io_file_read(file
, buf
, &len
, pool
);
1698 /* Having read all the data we *expect* EOF */
1699 if (err
&& !APR_STATUS_IS_EOF(err
->apr_err
))
1701 svn_error_clear(err
);
1703 /* Null terminate the stringbuf. */
1704 res
->data
[res
->len
] = 0;
1707 return SVN_NO_ERROR
;
1715 svn_io_remove_file(const char *path
, apr_pool_t
*pool
)
1717 apr_status_t apr_err
;
1718 const char *path_apr
;
1721 /* Set the file writable but only on Windows, because Windows
1722 will not allow us to remove files that are read-only. */
1723 SVN_ERR(svn_io_set_file_read_write(path
, TRUE
, pool
));
1726 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
1728 apr_err
= apr_file_remove(path_apr
, pool
);
1729 WIN32_RETRY_LOOP(apr_err
, apr_file_remove(path_apr
, pool
));
1732 return svn_error_wrap_apr(apr_err
, _("Can't remove file '%s'"),
1733 svn_path_local_style(path
, pool
));
1735 return SVN_NO_ERROR
;
1740 svn_io_remove_dir(const char *path
, apr_pool_t
*pool
)
1742 return svn_io_remove_dir2(path
, FALSE
, NULL
, NULL
, pool
);
1746 Mac OS X has a bug where if you're readding the contents of a
1747 directory via readdir in a loop, and you remove one of the entries in
1748 the directory and the directory has 338 or more files in it you will
1749 skip over some of the entries in the directory. Needless to say,
1750 this causes problems if you are using this kind of loop inside a
1751 function that is recursively deleting a directory, because when you
1752 get around to removing the directory it will still have something in
1755 Similar problem has been observed on FreeBSD.
1757 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 for more
1758 discussion and an initial solution.
1760 To work around the problem, we do a rewinddir after we delete all files
1761 and see if there's anything left. We repeat the steps untill there's
1762 nothing left to delete.
1764 This workaround causes issues on Windows where delete's are asynchronous,
1765 however, so we never rewind if we're on Windows (the delete says it is
1766 complete, we rewind, we see the same file and try to delete it again,
1770 /* Neither windows nor unix allows us to delete a non-empty
1773 This is a function to perform the equivalent of 'rm -rf'. */
1775 svn_io_remove_dir2(const char *path
, svn_boolean_t ignore_enoent
,
1776 svn_cancel_func_t cancel_func
, void *cancel_baton
,
1779 apr_status_t status
;
1780 apr_dir_t
*this_dir
;
1781 apr_finfo_t this_entry
;
1782 apr_pool_t
*subpool
;
1783 apr_int32_t flags
= APR_FINFO_TYPE
| APR_FINFO_NAME
;
1784 const char *path_apr
;
1787 /* Check for pending cancellation request.
1788 If we need to bail out, do so early. */
1791 SVN_ERR((*cancel_func
)(cancel_baton
));
1793 /* APR doesn't like "" directories */
1794 if (path
[0] == '\0')
1797 /* Convert path to native here and call apr_dir_open directly,
1798 instead of just using svn_io_dir_open, because we're going to
1799 need path_apr later anyway when we remove the dir itself. */
1801 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
1803 status
= apr_dir_open(&this_dir
, path_apr
, pool
);
1806 /* if the directory doesn't exist, our mission is accomplished */
1807 if (ignore_enoent
&& APR_STATUS_IS_ENOENT(status
))
1808 return SVN_NO_ERROR
;
1810 return svn_error_wrap_apr(status
,
1811 _("Can't open directory '%s'"),
1812 svn_path_local_style(path
, pool
));
1815 subpool
= svn_pool_create(pool
);
1819 need_rewind
= FALSE
;
1821 for (status
= apr_dir_read(&this_entry
, flags
, this_dir
);
1822 status
== APR_SUCCESS
;
1823 status
= apr_dir_read(&this_entry
, flags
, this_dir
))
1825 svn_pool_clear(subpool
);
1826 if ((this_entry
.filetype
== APR_DIR
)
1827 && ((this_entry
.name
[0] == '.')
1828 && ((this_entry
.name
[1] == '\0')
1829 || ((this_entry
.name
[1] == '.')
1830 && (this_entry
.name
[2] == '\0')))))
1834 else /* something other than "." or "..", so proceed */
1836 const char *fullpath
, *entry_utf8
;
1842 SVN_ERR(svn_path_cstring_to_utf8(&entry_utf8
, this_entry
.name
,
1845 fullpath
= svn_path_join(path
, entry_utf8
, subpool
);
1847 if (this_entry
.filetype
== APR_DIR
)
1849 /* Don't check for cancellation, the callee
1850 will immediately do so */
1851 SVN_ERR(svn_io_remove_dir2(fullpath
, FALSE
,
1852 cancel_func
, cancel_baton
,
1860 SVN_ERR((*cancel_func
)(cancel_baton
));
1862 err
= svn_io_remove_file(fullpath
, subpool
);
1864 return svn_error_createf
1865 (err
->apr_err
, err
, _("Can't remove '%s'"),
1866 svn_path_local_style(fullpath
, subpool
));
1873 status
= apr_dir_rewind(this_dir
);
1875 return svn_error_wrap_apr(status
, _("Can't rewind directory '%s'"),
1876 svn_path_local_style (path
, pool
));
1879 while (need_rewind
);
1881 svn_pool_destroy(subpool
);
1883 if (!APR_STATUS_IS_ENOENT(status
))
1884 return svn_error_wrap_apr(status
, _("Can't read directory '%s'"),
1885 svn_path_local_style(path
, pool
));
1887 status
= apr_dir_close(this_dir
);
1889 return svn_error_wrap_apr(status
, _("Error closing directory '%s'"),
1890 svn_path_local_style(path
, pool
));
1892 status
= apr_dir_remove(path_apr
, pool
);
1893 WIN32_RETRY_LOOP(status
, apr_dir_remove(path_apr
, pool
));
1895 return svn_error_wrap_apr(status
, _("Can't remove '%s'"),
1896 svn_path_local_style(path
, pool
));
1902 svn_io_get_dir_filenames(apr_hash_t
**dirents
,
1906 apr_status_t status
;
1907 apr_dir_t
*this_dir
;
1908 apr_finfo_t this_entry
;
1909 apr_int32_t flags
= APR_FINFO_NAME
;
1911 *dirents
= apr_hash_make(pool
);
1913 SVN_ERR(svn_io_dir_open(&this_dir
, path
, pool
));
1915 for (status
= apr_dir_read(&this_entry
, flags
, this_dir
);
1916 status
== APR_SUCCESS
;
1917 status
= apr_dir_read(&this_entry
, flags
, this_dir
))
1919 if ((this_entry
.name
[0] == '.')
1920 && ((this_entry
.name
[1] == '\0')
1921 || ((this_entry
.name
[1] == '.')
1922 && (this_entry
.name
[2] == '\0'))))
1929 SVN_ERR(svn_path_cstring_to_utf8(&name
, this_entry
.name
, pool
));
1930 apr_hash_set(*dirents
, name
, APR_HASH_KEY_STRING
, name
);
1934 if (! (APR_STATUS_IS_ENOENT(status
)))
1935 return svn_error_wrap_apr(status
, _("Can't read directory '%s'"),
1936 svn_path_local_style(path
, pool
));
1938 status
= apr_dir_close(this_dir
);
1940 return svn_error_wrap_apr(status
, _("Error closing directory '%s'"),
1941 svn_path_local_style(path
, pool
));
1943 return SVN_NO_ERROR
;
1947 svn_io_get_dirents2(apr_hash_t
**dirents
,
1951 apr_status_t status
;
1952 apr_dir_t
*this_dir
;
1953 apr_finfo_t this_entry
;
1954 apr_int32_t flags
= APR_FINFO_TYPE
| APR_FINFO_NAME
;
1956 *dirents
= apr_hash_make(pool
);
1958 SVN_ERR(svn_io_dir_open(&this_dir
, path
, pool
));
1960 for (status
= apr_dir_read(&this_entry
, flags
, this_dir
);
1961 status
== APR_SUCCESS
;
1962 status
= apr_dir_read(&this_entry
, flags
, this_dir
))
1964 if ((this_entry
.name
[0] == '.')
1965 && ((this_entry
.name
[1] == '\0')
1966 || ((this_entry
.name
[1] == '.')
1967 && (this_entry
.name
[2] == '\0'))))
1974 svn_io_dirent_t
*dirent
= apr_palloc(pool
, sizeof(*dirent
));
1976 SVN_ERR(svn_path_cstring_to_utf8(&name
, this_entry
.name
, pool
));
1978 map_apr_finfo_to_node_kind(&(dirent
->kind
),
1982 apr_hash_set(*dirents
, name
, APR_HASH_KEY_STRING
, dirent
);
1986 if (! (APR_STATUS_IS_ENOENT(status
)))
1987 return svn_error_wrap_apr(status
, _("Can't read directory '%s'"),
1988 svn_path_local_style(path
, pool
));
1990 status
= apr_dir_close(this_dir
);
1992 return svn_error_wrap_apr(status
, _("Error closing directory '%s'"),
1993 svn_path_local_style(path
, pool
));
1995 return SVN_NO_ERROR
;
1999 svn_io_get_dirents(apr_hash_t
**dirents
,
2003 /* Note that in C, padding is not allowed at the beginning of structs,
2004 so this is actually portable, since the kind field of svn_io_dirent_t
2005 is first in that struct. */
2006 return svn_io_get_dirents2(dirents
, path
, pool
);
2009 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2010 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2012 /* Handle an error from the child process (before command execution) by
2013 printing DESC and the error string corresponding to STATUS to stderr. */
2015 handle_child_process_error(apr_pool_t
*pool
, apr_status_t status
,
2019 apr_file_t
*errfile
;
2022 /* We can't do anything if we get an error here, so just return. */
2023 if (apr_pool_userdata_get(&p
, ERRFILE_KEY
, pool
))
2028 /* What we get from APR is in native encoding. */
2029 apr_file_printf(errfile
, "%s: %s",
2030 desc
, apr_strerror(status
, errbuf
,
2036 svn_io_start_cmd(apr_proc_t
*cmd_proc
,
2039 const char *const *args
,
2040 svn_boolean_t inherit
,
2042 apr_file_t
*outfile
,
2043 apr_file_t
*errfile
,
2046 apr_status_t apr_err
;
2047 apr_procattr_t
*cmdproc_attr
;
2049 const char **args_native
;
2050 const char *cmd_apr
;
2052 /* Create the process attributes. */
2053 apr_err
= apr_procattr_create(&cmdproc_attr
, pool
);
2055 return svn_error_wrap_apr
2056 (apr_err
, _("Can't create process '%s' attributes"), cmd
);
2058 /* Make sure we invoke cmd directly, not through a shell. */
2059 apr_err
= apr_procattr_cmdtype_set(cmdproc_attr
,
2060 inherit
?APR_PROGRAM_PATH
:APR_PROGRAM
);
2062 return svn_error_wrap_apr(apr_err
, _("Can't set process '%s' cmdtype"),
2065 /* Set the process's working directory. */
2068 const char *path_apr
;
2070 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
2071 apr_err
= apr_procattr_dir_set(cmdproc_attr
, path_apr
);
2073 return svn_error_wrap_apr
2074 (apr_err
, _("Can't set process '%s' directory"), cmd
);
2077 /* Use requested inputs and outputs.
2079 ### Unfortunately each of these apr functions creates a pipe and then
2080 overwrites the pipe file descriptor with the descriptor we pass
2081 in. The pipes can then never be closed. This is an APR bug. */
2084 apr_err
= apr_procattr_child_in_set(cmdproc_attr
, infile
, NULL
);
2086 return svn_error_wrap_apr
2087 (apr_err
, _("Can't set process '%s' child input"), cmd
);
2091 apr_err
= apr_procattr_child_out_set(cmdproc_attr
, outfile
, NULL
);
2093 return svn_error_wrap_apr
2094 (apr_err
, _("Can't set process '%s' child outfile"), cmd
);
2098 apr_err
= apr_procattr_child_err_set(cmdproc_attr
, errfile
, NULL
);
2100 return svn_error_wrap_apr
2101 (apr_err
, _("Can't set process '%s' child errfile"), cmd
);
2104 /* Have the child print any problems executing its program to errfile. */
2105 apr_err
= apr_pool_userdata_set(errfile
, ERRFILE_KEY
, NULL
, pool
);
2107 return svn_error_wrap_apr
2108 (apr_err
, _("Can't set process '%s' child errfile for error handler"),
2110 apr_err
= apr_procattr_child_errfn_set(cmdproc_attr
,
2111 handle_child_process_error
);
2113 return svn_error_wrap_apr
2114 (apr_err
, _("Can't set process '%s' error handler"), cmd
);
2116 /* Convert cmd and args from UTF-8 */
2117 SVN_ERR(svn_path_cstring_from_utf8(&cmd_apr
, cmd
, pool
));
2118 for (num_args
= 0; args
[num_args
]; num_args
++)
2120 args_native
= apr_palloc(pool
, (num_args
+ 1) * sizeof(char *));
2121 args_native
[num_args
] = NULL
;
2124 /* ### Well, it turns out that on APR on Windows expects all
2125 program args to be in UTF-8. Callers of svn_io_run_cmd
2126 should be aware of that. */
2127 SVN_ERR(svn_path_cstring_from_utf8(&args_native
[num_args
],
2133 /* Start the cmd command. */
2134 apr_err
= apr_proc_create(cmd_proc
, cmd_apr
, args_native
, NULL
,
2135 cmdproc_attr
, pool
);
2137 return svn_error_wrap_apr(apr_err
, _("Can't start process '%s'"), cmd
);
2139 return SVN_NO_ERROR
;
2145 svn_io_wait_for_cmd(apr_proc_t
*cmd_proc
,
2148 apr_exit_why_e
*exitwhy
,
2151 apr_status_t apr_err
;
2152 apr_exit_why_e exitwhy_val
;
2155 /* The Win32 apr_proc_wait doesn't set this... */
2156 exitwhy_val
= APR_PROC_EXIT
;
2158 /* Wait for the cmd command to finish. */
2159 apr_err
= apr_proc_wait(cmd_proc
, &exitcode_val
, &exitwhy_val
, APR_WAIT
);
2160 if (!APR_STATUS_IS_CHILD_DONE(apr_err
))
2161 return svn_error_wrap_apr(apr_err
, _("Error waiting for process '%s'"),
2165 *exitwhy
= exitwhy_val
;
2166 else if (! APR_PROC_CHECK_EXIT(exitwhy_val
))
2167 return svn_error_createf
2168 (SVN_ERR_EXTERNAL_PROGRAM
, NULL
,
2169 _("Process '%s' failed (exitwhy %d)"), cmd
, exitwhy_val
);
2172 *exitcode
= exitcode_val
;
2173 else if (exitcode_val
!= 0)
2174 return svn_error_createf
2175 (SVN_ERR_EXTERNAL_PROGRAM
, NULL
,
2176 _("Process '%s' returned error exitcode %d"), cmd
, exitcode_val
);
2178 return SVN_NO_ERROR
;
2183 svn_io_run_cmd(const char *path
,
2185 const char *const *args
,
2187 apr_exit_why_e
*exitwhy
,
2188 svn_boolean_t inherit
,
2190 apr_file_t
*outfile
,
2191 apr_file_t
*errfile
,
2194 apr_proc_t cmd_proc
;
2196 SVN_ERR(svn_io_start_cmd(&cmd_proc
, path
, cmd
, args
, inherit
,
2197 infile
, outfile
, errfile
, pool
));
2199 SVN_ERR(svn_io_wait_for_cmd(&cmd_proc
, cmd
, exitcode
, exitwhy
, pool
));
2201 return SVN_NO_ERROR
;
2206 svn_io_run_diff(const char *dir
,
2207 const char *const *user_args
,
2214 apr_file_t
*outfile
,
2215 apr_file_t
*errfile
,
2216 const char *diff_cmd
,
2222 int nargs
= 4; /* the diff command itself, two paths, plus a trailing NULL */
2223 const char *diff_utf8
;
2224 apr_pool_t
*subpool
= svn_pool_create(pool
);
2226 SVN_ERR(svn_path_cstring_to_utf8(&diff_utf8
, diff_cmd
, pool
));
2228 if (pexitcode
== NULL
)
2229 pexitcode
= &exitcode
;
2231 if (user_args
!= NULL
)
2232 nargs
+= num_user_args
;
2234 nargs
+= 1; /* -u */
2237 nargs
+= 2; /* the -L and the label itself */
2239 nargs
+= 2; /* the -L and the label itself */
2241 args
= apr_palloc(subpool
, nargs
* sizeof(char *));
2244 args
[i
++] = diff_utf8
;
2246 if (user_args
!= NULL
)
2249 for (j
= 0; j
< num_user_args
; ++j
)
2250 args
[i
++] = user_args
[j
];
2253 args
[i
++] = "-u"; /* assume -u if the user didn't give us any args */
2266 args
[i
++] = svn_path_local_style(from
, subpool
);
2267 args
[i
++] = svn_path_local_style(to
, subpool
);
2272 SVN_ERR(svn_io_run_cmd(dir
, diff_utf8
, args
, pexitcode
, NULL
, TRUE
,
2273 NULL
, outfile
, errfile
, subpool
));
2275 /* The man page for (GNU) diff describes the return value as:
2277 "An exit status of 0 means no differences were found, 1 means
2278 some differences were found, and 2 means trouble."
2280 A return value of 2 typically occurs when diff cannot read its input
2281 or write to its output, but in any case we probably ought to return an
2282 error for anything other than 0 or 1 as the output is likely to be
2285 if (*pexitcode
!= 0 && *pexitcode
!= 1)
2286 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM
, NULL
,
2287 _("'%s' returned %d"),
2288 svn_path_local_style(diff_utf8
, pool
),
2291 svn_pool_destroy(subpool
);
2293 return SVN_NO_ERROR
;
2299 svn_io_run_diff3_2(int *exitcode
,
2304 const char *mine_label
,
2305 const char *older_label
,
2306 const char *yours_label
,
2308 const char *diff3_cmd
,
2309 const apr_array_header_t
*user_args
,
2312 const char **args
= apr_palloc(pool
,
2317 const char *diff3_utf8
;
2323 SVN_ERR(svn_path_cstring_to_utf8(&diff3_utf8
, diff3_cmd
, pool
));
2325 /* Labels fall back to sensible defaults if not specified. */
2326 if (mine_label
== NULL
)
2327 mine_label
= ".working";
2328 if (older_label
== NULL
)
2329 older_label
= ".old";
2330 if (yours_label
== NULL
)
2331 yours_label
= ".new";
2333 /* Set up diff3 command line. */
2334 args
[i
++] = diff3_utf8
;
2338 for (j
= 0; j
< user_args
->nelts
; ++j
)
2339 args
[i
++] = APR_ARRAY_IDX(user_args
, j
, const char *);
2341 nargs
+= user_args
->nelts
;
2346 args
[i
++] = "-E"; /* We tried "-A" here, but that caused
2347 overlapping identical changes to
2348 conflict. See issue #682. */
2355 args
[i
++] = mine_label
;
2357 args
[i
++] = older_label
; /* note: this label is ignored if
2358 using 2-part markers, which is the
2361 args
[i
++] = yours_label
;
2362 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
2364 svn_boolean_t has_arg
;
2366 /* ### FIXME: we really shouldn't be reading the config here;
2367 instead, the necessary bits should be passed in by the caller.
2368 But should we add another parameter to this function, when the
2369 whole external diff3 thing might eventually go away? */
2373 SVN_ERR(svn_config_get_config(&config
, pool
));
2374 cfg
= config
? apr_hash_get(config
, SVN_CONFIG_CATEGORY_CONFIG
,
2375 APR_HASH_KEY_STRING
) : NULL
;
2376 SVN_ERR(svn_config_get_bool(cfg
, &has_arg
, SVN_CONFIG_SECTION_HELPERS
,
2377 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG
,
2381 const char *diff_cmd
, *diff_utf8
;
2382 svn_config_get(cfg
, &diff_cmd
, SVN_CONFIG_SECTION_HELPERS
,
2383 SVN_CONFIG_OPTION_DIFF_CMD
, SVN_CLIENT_DIFF
);
2384 SVN_ERR(svn_path_cstring_to_utf8(&diff_utf8
, diff_cmd
, pool
));
2385 args
[i
++] = apr_pstrcat(pool
, "--diff-program=", diff_utf8
, NULL
);
2392 args
[i
++] = svn_path_local_style(mine
, pool
);
2393 args
[i
++] = svn_path_local_style(older
, pool
);
2394 args
[i
++] = svn_path_local_style(yours
, pool
);
2398 /* Run diff3, output the merged text into the scratch file. */
2399 SVN_ERR(svn_io_run_cmd(dir
, diff3_utf8
, args
,
2401 TRUE
, /* keep environment */
2405 /* According to the diff3 docs, a '0' means the merge was clean, and
2406 '1' means conflict markers were found. Anything else is real
2408 if ((*exitcode
!= 0) && (*exitcode
!= 1))
2409 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM
, NULL
,
2410 _("Error running '%s': exitcode was %d, "
2412 "\nin directory '%s', basenames:\n%s\n%s\n%s"),
2413 svn_path_local_style(diff3_utf8
, pool
),
2415 svn_path_local_style(dir
, pool
),
2416 /* Don't call svn_path_local_style() on
2417 the basenames. We don't want them to
2418 be absolute, and we don't need the
2419 separator conversion. */
2420 mine
, older
, yours
);
2422 return SVN_NO_ERROR
;
2426 svn_io_run_diff3(const char *dir
,
2430 const char *mine_label
,
2431 const char *older_label
,
2432 const char *yours_label
,
2435 const char *diff3_cmd
,
2438 return svn_io_run_diff3_2(exitcode
, dir
, mine
, older
, yours
,
2439 mine_label
, older_label
, yours_label
,
2440 merged
, diff3_cmd
, NULL
, pool
);
2445 svn_io_parse_mimetypes_file(apr_hash_t
**type_map
,
2446 const char *mimetypes_file
,
2449 svn_error_t
*err
= SVN_NO_ERROR
;
2450 apr_hash_t
*types
= apr_hash_make(pool
);
2451 svn_boolean_t eof
= FALSE
;
2452 svn_stringbuf_t
*buf
;
2453 apr_pool_t
*subpool
= svn_pool_create(pool
);
2454 apr_file_t
*types_file
;
2455 svn_stream_t
*mimetypes_stream
;
2457 SVN_ERR(svn_io_file_open(&types_file
, mimetypes_file
,
2458 APR_READ
, APR_OS_DEFAULT
, pool
));
2459 mimetypes_stream
= svn_stream_from_aprfile2(types_file
, FALSE
, pool
);
2463 apr_array_header_t
*tokens
;
2467 svn_pool_clear(subpool
);
2470 if ((err
= svn_stream_readline(mimetypes_stream
, &buf
,
2471 APR_EOL_STR
, &eof
, subpool
)))
2474 /* Only pay attention to non-empty, non-comment lines. */
2477 if (buf
->data
[0] == '#')
2480 /* Tokenize (into our return pool). */
2481 tokens
= svn_cstring_split(buf
->data
, " \t", TRUE
, pool
);
2482 if (tokens
->nelts
< 2)
2485 /* The first token in a multi-token line is the media type.
2486 Subsequent tokens are filename extensions associated with
2488 type
= APR_ARRAY_IDX(tokens
, 0, const char *);
2489 for (i
= 1; i
< tokens
->nelts
; i
++)
2491 const char *ext
= APR_ARRAY_IDX(tokens
, i
, const char *);
2492 apr_hash_set(types
, ext
, APR_HASH_KEY_STRING
, type
);
2498 svn_pool_destroy(subpool
);
2500 /* If there was an error above, close the file (ignoring any error
2501 from *that*) and return the originally error. */
2504 svn_error_clear(svn_stream_close(mimetypes_stream
));
2508 /* Close the stream (which closes the underlying file, too). */
2509 SVN_ERR(svn_stream_close(mimetypes_stream
));
2512 return SVN_NO_ERROR
;
2517 svn_io_detect_mimetype2(const char **mimetype
,
2519 apr_hash_t
*mimetype_map
,
2522 static const char * const generic_binary
= "application/octet-stream";
2524 svn_node_kind_t kind
;
2527 unsigned char block
[1024];
2528 apr_size_t amt_read
= sizeof(block
);
2530 /* Default return value is NULL. */
2533 /* See if this file even exists, and make sure it really is a file. */
2534 SVN_ERR(svn_io_check_path(file
, &kind
, pool
));
2535 if (kind
!= svn_node_file
)
2536 return svn_error_createf(SVN_ERR_BAD_FILENAME
, NULL
,
2537 _("Can't detect MIME type of non-file '%s'"),
2538 svn_path_local_style(file
, pool
));
2540 /* If there is a mimetype_map provided, we'll first try to look up
2541 our file's extension in the map. Failing that, we'll run the
2545 const char *type_from_map
, *path_ext
;
2546 svn_path_splitext(NULL
, &path_ext
, file
, pool
);
2547 if ((type_from_map
= apr_hash_get(mimetype_map
, path_ext
,
2548 APR_HASH_KEY_STRING
)))
2550 *mimetype
= type_from_map
;
2551 return SVN_NO_ERROR
;
2555 SVN_ERR(svn_io_file_open(&fh
, file
, APR_READ
, 0, pool
));
2557 /* Read a block of data from FILE. */
2558 err
= svn_io_file_read(fh
, block
, &amt_read
, pool
);
2559 if (err
&& ! APR_STATUS_IS_EOF(err
->apr_err
))
2561 svn_error_clear(err
);
2563 /* Now close the file. No use keeping it open any more. */
2564 SVN_ERR(svn_io_file_close(fh
, pool
));
2567 /* Right now, this function is going to be really stupid. It's
2568 going to examine the first block of data, and make sure that 85%
2569 of the bytes are such that their value is in the ranges 0x07-0x0D
2570 or 0x20-0x7F, and that 100% of those bytes is not 0x00.
2572 If those criteria are not met, we're calling it binary. */
2576 int binary_count
= 0;
2578 /* Run through the data we've read, counting the 'binary-ish'
2579 bytes. HINT: If we see a 0x00 byte, we'll set our count to its
2580 max and stop reading the file. */
2581 for (i
= 0; i
< amt_read
; i
++)
2585 binary_count
= amt_read
;
2588 if ((block
[i
] < 0x07)
2589 || ((block
[i
] > 0x0D) && (block
[i
] < 0x20))
2590 || (block
[i
] > 0x7F))
2596 if (((binary_count
* 1000) / amt_read
) > 850)
2598 *mimetype
= generic_binary
;
2599 return SVN_NO_ERROR
;
2603 return SVN_NO_ERROR
;
2608 svn_io_detect_mimetype(const char **mimetype
,
2612 return svn_io_detect_mimetype2(mimetype
, file
, NULL
, pool
);
2616 svn_io_file_open(apr_file_t
**new_file
, const char *fname
,
2617 apr_int32_t flag
, apr_fileperms_t perm
,
2620 const char *fname_apr
;
2621 apr_status_t status
;
2623 SVN_ERR(svn_path_cstring_from_utf8(&fname_apr
, fname
, pool
));
2624 status
= file_open(new_file
, fname_apr
, flag
| APR_BINARY
, perm
, pool
);
2627 return svn_error_wrap_apr(status
, _("Can't open file '%s'"),
2628 svn_path_local_style(fname
, pool
));
2630 return SVN_NO_ERROR
;
2634 static svn_error_t
*
2635 do_io_file_wrapper_cleanup(apr_file_t
*file
, apr_status_t status
,
2636 const char *msg
, const char *msg_no_name
,
2643 return SVN_NO_ERROR
;
2645 err
= file_name_get(&name
, file
, pool
);
2648 svn_error_clear(err
);
2651 return svn_error_wrap_apr(status
, _(msg
),
2652 svn_path_local_style(name
, pool
));
2654 return svn_error_wrap_apr(status
, _(msg_no_name
));
2659 svn_io_file_close(apr_file_t
*file
, apr_pool_t
*pool
)
2661 return do_io_file_wrapper_cleanup
2662 (file
, apr_file_close(file
),
2663 N_("Can't close file '%s'"),
2664 N_("Can't close stream"),
2670 svn_io_file_getc(char *ch
, apr_file_t
*file
, apr_pool_t
*pool
)
2672 return do_io_file_wrapper_cleanup
2673 (file
, apr_file_getc(ch
, file
),
2674 N_("Can't read file '%s'"),
2675 N_("Can't read stream"),
2681 svn_io_file_info_get(apr_finfo_t
*finfo
, apr_int32_t wanted
,
2682 apr_file_t
*file
, apr_pool_t
*pool
)
2684 return do_io_file_wrapper_cleanup
2685 (file
, apr_file_info_get(finfo
, wanted
, file
),
2686 N_("Can't get attribute information from file '%s'"),
2687 N_("Can't get attribute information from stream"),
2693 svn_io_file_read(apr_file_t
*file
, void *buf
,
2694 apr_size_t
*nbytes
, apr_pool_t
*pool
)
2696 return do_io_file_wrapper_cleanup
2697 (file
, apr_file_read(file
, buf
, nbytes
),
2698 N_("Can't read file '%s'"),
2699 N_("Can't read stream"),
2705 svn_io_file_read_full(apr_file_t
*file
, void *buf
,
2706 apr_size_t nbytes
, apr_size_t
*bytes_read
,
2709 return do_io_file_wrapper_cleanup
2710 (file
, apr_file_read_full(file
, buf
, nbytes
, bytes_read
),
2711 N_("Can't read file '%s'"),
2712 N_("Can't read stream"),
2718 svn_io_file_seek(apr_file_t
*file
, apr_seek_where_t where
,
2719 apr_off_t
*offset
, apr_pool_t
*pool
)
2721 return do_io_file_wrapper_cleanup
2722 (file
, apr_file_seek(file
, where
, offset
),
2723 N_("Can't set position pointer in file '%s'"),
2724 N_("Can't set position pointer in stream"),
2730 svn_io_file_write(apr_file_t
*file
, const void *buf
,
2731 apr_size_t
*nbytes
, apr_pool_t
*pool
)
2733 return do_io_file_wrapper_cleanup
2734 (file
, apr_file_write(file
, buf
, nbytes
),
2735 N_("Can't write to file '%s'"),
2736 N_("Can't write to stream"),
2742 svn_io_file_write_full(apr_file_t
*file
, const void *buf
,
2743 apr_size_t nbytes
, apr_size_t
*bytes_written
,
2746 apr_status_t rv
= apr_file_write_full(file
, buf
, nbytes
, bytes_written
);
2749 #define MAXBUFSIZE 30*1024
2750 if (rv
== APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY
)
2751 && nbytes
> MAXBUFSIZE
)
2757 rv
= apr_file_write_full(file
, buf
,
2758 nbytes
> MAXBUFSIZE
? MAXBUFSIZE
: nbytes
, &bw
);
2759 *bytes_written
+= bw
;
2760 buf
= (char *)buf
+ bw
;
2762 } while (rv
== APR_SUCCESS
&& nbytes
> 0);
2767 return do_io_file_wrapper_cleanup
2769 N_("Can't write to file '%s'"),
2770 N_("Can't write to stream"),
2776 svn_io_read_length_line(apr_file_t
*file
, char *buf
, apr_size_t
*limit
,
2784 for (i
= 0; i
< *limit
; i
++)
2786 SVN_ERR(svn_io_file_getc(&c
, file
, pool
));
2787 /* Note: this error could be APR_EOF, which
2788 is totally fine. The caller should be aware of
2795 return SVN_NO_ERROR
;
2803 err
= file_name_get(&name
, file
, pool
);
2806 svn_error_clear(err
);
2809 return svn_error_createf(SVN_ERR_MALFORMED_FILE
, NULL
,
2810 _("Can't read length line in file '%s'"),
2811 svn_path_local_style(name
, pool
));
2813 return svn_error_create(SVN_ERR_MALFORMED_FILE
, NULL
,
2814 _("Can't read length line in stream"));
2819 svn_io_stat(apr_finfo_t
*finfo
, const char *fname
,
2820 apr_int32_t wanted
, apr_pool_t
*pool
)
2822 apr_status_t status
;
2823 const char *fname_apr
;
2825 /* APR doesn't like "" directories */
2826 if (fname
[0] == '\0')
2829 SVN_ERR(svn_path_cstring_from_utf8(&fname_apr
, fname
, pool
));
2831 status
= apr_stat(finfo
, fname_apr
, wanted
, pool
);
2833 return svn_error_wrap_apr(status
, _("Can't stat '%s'"),
2834 svn_path_local_style(fname
, pool
));
2836 return SVN_NO_ERROR
;
2841 svn_io_file_rename(const char *from_path
, const char *to_path
,
2844 apr_status_t status
= APR_SUCCESS
;
2845 const char *from_path_apr
, *to_path_apr
;
2848 /* Set the destination file writable but only on Windows, because
2849 Windows will not allow us to rename over files that are read-only. */
2850 SVN_ERR(svn_io_set_file_read_write(to_path
, TRUE
, pool
));
2853 SVN_ERR(svn_path_cstring_from_utf8(&from_path_apr
, from_path
, pool
));
2854 SVN_ERR(svn_path_cstring_from_utf8(&to_path_apr
, to_path
, pool
));
2856 status
= apr_file_rename(from_path_apr
, to_path_apr
, pool
);
2857 WIN32_RETRY_LOOP(status
,
2858 apr_file_rename(from_path_apr
, to_path_apr
, pool
));
2861 return svn_error_wrap_apr(status
, _("Can't move '%s' to '%s'"),
2862 svn_path_local_style(from_path
, pool
),
2863 svn_path_local_style(to_path
, pool
));
2865 return SVN_NO_ERROR
;
2870 svn_io_file_move(const char *from_path
, const char *to_path
,
2873 svn_error_t
*err
= svn_io_file_rename(from_path
, to_path
, pool
);
2875 if (err
&& APR_STATUS_IS_EXDEV(err
->apr_err
))
2877 const char *tmp_to_path
;
2879 svn_error_clear(err
);
2881 SVN_ERR(svn_io_open_unique_file2(NULL
, &tmp_to_path
, to_path
,
2882 "tmp", svn_io_file_del_none
, pool
));
2884 err
= svn_io_copy_file(from_path
, tmp_to_path
, TRUE
, pool
);
2888 err
= svn_io_file_rename(tmp_to_path
, to_path
, pool
);
2892 err
= svn_io_remove_file(from_path
, pool
);
2894 return SVN_NO_ERROR
;
2896 svn_error_clear(svn_io_remove_file(to_path
, pool
));
2901 svn_error_clear(svn_io_remove_file(tmp_to_path
, pool
));
2907 /* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
2908 HIDDEN determines if the hidden attribute
2909 should be set on the newly created directory. */
2910 static svn_error_t
*
2911 dir_make(const char *path
, apr_fileperms_t perm
,
2912 svn_boolean_t hidden
, svn_boolean_t sgid
, apr_pool_t
*pool
)
2914 apr_status_t status
;
2915 const char *path_apr
;
2917 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
2919 /* APR doesn't like "" directories */
2920 if (path_apr
[0] == '\0')
2923 #if (APR_OS_DEFAULT & APR_WSTICKY)
2924 /* The APR shipped with httpd 2.0.50 contains a bug where
2925 APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
2926 There is a special case for file creation, but not directory
2927 creation, so directories wind up getting created with the sticky
2928 bit set. (There is no such thing as a setuid directory, and the
2929 setgid bit is apparently ignored at mkdir() time.) If we detect
2930 this problem, work around it by unsetting those bits if we are
2931 passed APR_OS_DEFAULT. */
2932 if (perm
== APR_OS_DEFAULT
)
2933 perm
&= ~(APR_USETID
| APR_GSETID
| APR_WSTICKY
);
2936 status
= apr_dir_make(path_apr
, perm
, pool
);
2937 WIN32_RETRY_LOOP(status
, apr_dir_make(path_apr
, perm
, pool
));
2940 return svn_error_wrap_apr(status
, _("Can't create directory '%s'"),
2941 svn_path_local_style(path
, pool
));
2943 #ifdef APR_FILE_ATTR_HIDDEN
2946 status
= apr_file_attrs_set(path_apr
,
2947 APR_FILE_ATTR_HIDDEN
,
2948 APR_FILE_ATTR_HIDDEN
,
2951 return svn_error_wrap_apr(status
, _("Can't hide directory '%s'"),
2952 svn_path_local_style(path
, pool
));
2960 /* Per our contract, don't do error-checking. Some filesystems
2961 * don't support the sgid bit, and that's okay. */
2962 status
= apr_stat(&finfo
, path_apr
, APR_FINFO_PROT
, pool
);
2965 apr_file_perms_set(path_apr
, finfo
.protection
| APR_GSETID
);
2968 return SVN_NO_ERROR
;
2972 svn_io_dir_make(const char *path
, apr_fileperms_t perm
, apr_pool_t
*pool
)
2974 return dir_make(path
, perm
, FALSE
, FALSE
, pool
);
2978 svn_io_dir_make_hidden(const char *path
, apr_fileperms_t perm
,
2981 return dir_make(path
, perm
, TRUE
, FALSE
, pool
);
2985 svn_io_dir_make_sgid(const char *path
, apr_fileperms_t perm
,
2988 return dir_make(path
, perm
, FALSE
, TRUE
, pool
);
2993 svn_io_dir_open(apr_dir_t
**new_dir
, const char *dirname
, apr_pool_t
*pool
)
2995 apr_status_t status
;
2996 const char *dirname_apr
;
2998 /* APR doesn't like "" directories */
2999 if (dirname
[0] == '\0')
3002 SVN_ERR(svn_path_cstring_from_utf8(&dirname_apr
, dirname
, pool
));
3004 status
= apr_dir_open(new_dir
, dirname_apr
, pool
);
3006 return svn_error_wrap_apr(status
, _("Can't open directory '%s'"),
3007 svn_path_local_style(dirname
, pool
));
3009 return SVN_NO_ERROR
;
3014 svn_io_dir_remove_nonrecursive(const char *dirname
, apr_pool_t
*pool
)
3016 apr_status_t status
;
3017 const char *dirname_apr
;
3019 SVN_ERR(svn_path_cstring_from_utf8(&dirname_apr
, dirname
, pool
));
3021 status
= apr_dir_remove(dirname_apr
, pool
);
3022 WIN32_RETRY_LOOP(status
, apr_dir_remove(dirname_apr
, pool
));
3024 return svn_error_wrap_apr(status
, _("Can't remove directory '%s'"),
3025 svn_path_local_style(dirname
, pool
));
3027 return SVN_NO_ERROR
;
3032 svn_io_dir_read(apr_finfo_t
*finfo
,
3037 apr_status_t status
;
3039 status
= apr_dir_read(finfo
, wanted
, thedir
);
3042 return svn_error_wrap_apr(status
, _("Can't read directory"));
3045 SVN_ERR(svn_path_cstring_to_utf8(&finfo
->fname
, finfo
->fname
, pool
));
3048 SVN_ERR(svn_path_cstring_to_utf8(&finfo
->name
, finfo
->name
, pool
));
3050 return SVN_NO_ERROR
;
3055 svn_io_dir_walk(const char *dirname
,
3057 svn_io_walk_func_t walk_func
,
3061 apr_status_t apr_err
;
3063 apr_pool_t
*subpool
;
3064 const char *dirname_apr
;
3067 wanted
|= APR_FINFO_TYPE
| APR_FINFO_NAME
;
3069 /* The documentation for apr_dir_read used to state that "." and ".."
3070 will be returned as the first two files, but it doesn't
3071 work that way in practice, in particular ext3 on Linux-2.6 doesn't
3072 follow the rules. For details see
3073 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
3075 If APR ever does implement "dot-first" then it would be possible to
3076 remove the svn_io_stat and walk_func calls and use the walk_func
3079 Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
3080 documented to provide it, so we have to do a bit extra. */
3081 SVN_ERR(svn_io_stat(&finfo
, dirname
, wanted
& ~APR_FINFO_NAME
, pool
));
3082 SVN_ERR(svn_path_cstring_from_utf8(&finfo
.name
,
3083 svn_path_basename(dirname
, pool
),
3085 finfo
.valid
|= APR_FINFO_NAME
;
3086 SVN_ERR((*walk_func
)(walk_baton
, dirname
, &finfo
, pool
));
3088 SVN_ERR(svn_path_cstring_from_utf8(&dirname_apr
, dirname
, pool
));
3090 apr_err
= apr_dir_open(&handle
, dirname_apr
, pool
);
3092 return svn_error_wrap_apr(apr_err
, _("Can't open directory '%s'"),
3093 svn_path_local_style(dirname
, pool
));
3095 /* iteration subpool */
3096 subpool
= svn_pool_create(pool
);
3100 const char *name_utf8
;
3101 const char *full_path
;
3103 svn_pool_clear(subpool
);
3105 apr_err
= apr_dir_read(&finfo
, wanted
, handle
);
3106 if (APR_STATUS_IS_ENOENT(apr_err
))
3110 return svn_error_wrap_apr
3111 (apr_err
, _("Can't read directory entry in '%s'"),
3112 svn_path_local_style(dirname
, pool
));
3115 if (finfo
.filetype
== APR_DIR
)
3117 if (finfo
.name
[0] == '.'
3118 && (finfo
.name
[1] == '\0'
3119 || (finfo
.name
[1] == '.' && finfo
.name
[2] == '\0')))
3120 /* skip "." and ".." */
3123 /* some other directory. recurse. it will be passed to the
3124 callback inside the recursion. */
3125 SVN_ERR(svn_path_cstring_to_utf8(&name_utf8
, finfo
.name
,
3127 full_path
= svn_path_join(dirname
, name_utf8
, subpool
);
3128 SVN_ERR(svn_io_dir_walk(full_path
,
3134 else if (finfo
.filetype
== APR_REG
)
3136 /* some other directory. pass it to the callback. */
3137 SVN_ERR(svn_path_cstring_to_utf8(&name_utf8
, finfo
.name
,
3139 full_path
= svn_path_join(dirname
, name_utf8
, subpool
);
3140 SVN_ERR((*walk_func
)(walk_baton
,
3146 some other type of file; skip it.
3151 svn_pool_destroy(subpool
);
3153 apr_err
= apr_dir_close(handle
);
3155 return svn_error_wrap_apr(apr_err
, _("Error closing directory '%s'"),
3156 svn_path_local_style(dirname
, pool
));
3158 return SVN_NO_ERROR
;
3164 * Determine if a directory is empty or not.
3165 * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
3166 * @param path The directory.
3167 * @param pool Used for temporary allocation.
3168 * @remark If path is not a directory, or some other error occurs,
3169 * then return the appropriate apr status code.
3171 * (This function is written in APR style, in anticipation of
3172 * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
3175 dir_is_empty(const char *dir
, apr_pool_t
*pool
)
3177 apr_status_t apr_err
;
3178 apr_dir_t
*dir_handle
;
3180 apr_status_t retval
= APR_SUCCESS
;
3182 /* APR doesn't like "" directories */
3186 apr_err
= apr_dir_open(&dir_handle
, dir
, pool
);
3187 if (apr_err
!= APR_SUCCESS
)
3190 for (apr_err
= apr_dir_read(&finfo
, APR_FINFO_NAME
, dir_handle
);
3191 apr_err
== APR_SUCCESS
;
3192 apr_err
= apr_dir_read(&finfo
, APR_FINFO_NAME
, dir_handle
))
3194 /* Ignore entries for this dir and its parent, robustly.
3195 (APR promises that they'll come first, so technically
3196 this guard could be moved outside the loop. But Ryan Bloom
3197 says he doesn't believe it, and I believe him. */
3198 if (! (finfo
.name
[0] == '.'
3199 && (finfo
.name
[1] == '\0'
3200 || (finfo
.name
[1] == '.' && finfo
.name
[2] == '\0'))))
3202 retval
= APR_ENOTEMPTY
;
3207 /* Make sure we broke out of the loop for the right reason. */
3208 if (apr_err
&& ! APR_STATUS_IS_ENOENT(apr_err
))
3211 apr_err
= apr_dir_close(dir_handle
);
3212 if (apr_err
!= APR_SUCCESS
)
3220 svn_io_dir_empty(svn_boolean_t
*is_empty_p
,
3224 apr_status_t status
;
3225 const char *path_apr
;
3227 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, path
, pool
));
3229 status
= dir_is_empty(path_apr
, pool
);
3233 else if (APR_STATUS_IS_ENOTEMPTY(status
))
3234 *is_empty_p
= FALSE
;
3236 return svn_error_wrap_apr(status
, _("Can't check directory '%s'"),
3237 svn_path_local_style(path
, pool
));
3239 return SVN_NO_ERROR
;
3244 /*** Version/format files ***/
3247 svn_io_write_version_file(const char *path
,
3251 apr_file_t
*format_file
= NULL
;
3252 const char *path_tmp
;
3253 const char *format_contents
= apr_psprintf(pool
, "%d\n", version
);
3255 /* We only promise to handle non-negative integers. */
3257 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS
, NULL
,
3258 _("Version %d is not non-negative"), version
);
3260 /* Create a temporary file to write the data to */
3261 SVN_ERR(svn_io_open_unique_file2(&format_file
, &path_tmp
, path
, ".tmp",
3262 svn_io_file_del_none
, pool
));
3264 /* ...dump out our version number string... */
3265 SVN_ERR(svn_io_file_write_full(format_file
, format_contents
,
3266 strlen(format_contents
), NULL
, pool
));
3268 /* ...and close the file. */
3269 SVN_ERR(svn_io_file_close(format_file
, pool
));
3272 /* make the destination writable, but only on Windows, because
3273 Windows does not let us replace read-only files. */
3274 SVN_ERR(svn_io_set_file_read_write(path
, TRUE
, pool
));
3277 /* rename the temp file as the real destination */
3278 SVN_ERR(svn_io_file_rename(path_tmp
, path
, pool
));
3280 /* And finally remove the perms to make it read only */
3281 SVN_ERR(svn_io_set_file_read_only(path
, FALSE
, pool
));
3283 return SVN_NO_ERROR
;
3288 svn_io_read_version_file(int *version
,
3292 apr_file_t
*format_file
;
3296 /* Read a chunk of data from PATH */
3297 SVN_ERR(svn_io_file_open(&format_file
, path
, APR_READ
,
3298 APR_OS_DEFAULT
, pool
));
3300 SVN_ERR(svn_io_file_read(format_file
, buf
, &len
, pool
));
3302 /* Close the file. */
3303 SVN_ERR(svn_io_file_close(format_file
, pool
));
3305 /* If there was no data in PATH, return an error. */
3307 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF
, NULL
,
3309 svn_path_local_style(path
, pool
));
3311 /* Check that the first line contains only digits. */
3315 for (i
= 0; i
< len
; ++i
)
3319 if (i
> 0 && (c
== '\r' || c
== '\n'))
3321 if (! apr_isdigit(c
))
3322 return svn_error_createf
3323 (SVN_ERR_BAD_VERSION_FILE_FORMAT
, NULL
,
3324 _("First line of '%s' contains non-digit"),
3325 svn_path_local_style(path
, pool
));
3329 /* Convert to integer. */
3330 *version
= atoi(buf
);
3332 return SVN_NO_ERROR
;
3337 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
3338 static svn_error_t
*
3339 contents_identical_p(svn_boolean_t
*identical_p
,
3346 apr_size_t bytes_read1
, bytes_read2
;
3347 char *buf1
= apr_palloc(pool
, SVN__STREAM_CHUNK_SIZE
);
3348 char *buf2
= apr_palloc(pool
, SVN__STREAM_CHUNK_SIZE
);
3349 apr_file_t
*file1_h
= NULL
;
3350 apr_file_t
*file2_h
= NULL
;
3352 SVN_ERR(svn_io_file_open(&file1_h
, file1
, APR_READ
, APR_OS_DEFAULT
,
3354 SVN_ERR(svn_io_file_open(&file2_h
, file2
, APR_READ
, APR_OS_DEFAULT
,
3357 *identical_p
= TRUE
; /* assume TRUE, until disproved below */
3360 err1
= svn_io_file_read_full(file1_h
, buf1
,
3361 SVN__STREAM_CHUNK_SIZE
, &bytes_read1
, pool
);
3362 if (err1
&& !APR_STATUS_IS_EOF(err1
->apr_err
))
3365 err2
= svn_io_file_read_full(file2_h
, buf2
,
3366 SVN__STREAM_CHUNK_SIZE
, &bytes_read2
, pool
);
3367 if (err2
&& !APR_STATUS_IS_EOF(err2
->apr_err
))
3369 svn_error_clear(err1
);
3373 if ((bytes_read1
!= bytes_read2
)
3374 || (memcmp(buf1
, buf2
, bytes_read1
)))
3376 *identical_p
= FALSE
;
3379 } while (! err1
&& ! err2
);
3381 svn_error_clear(err1
);
3382 svn_error_clear(err2
);
3384 SVN_ERR(svn_io_file_close(file1_h
, pool
));
3385 SVN_ERR(svn_io_file_close(file2_h
, pool
));
3387 return SVN_NO_ERROR
;
3393 svn_io_files_contents_same_p(svn_boolean_t
*same
,
3400 SVN_ERR(svn_io_filesizes_different_p(&q
, file1
, file2
, pool
));
3405 return SVN_NO_ERROR
;
3408 SVN_ERR(contents_identical_p(&q
, file1
, file2
, pool
));
3415 return SVN_NO_ERROR
;