]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_subr/io.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_subr / io.c
1 /*
2  * io.c:   shared file reading, writing, and probing code.
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 \f
26 #include <stdio.h>
27
28 #ifndef WIN32
29 #include <unistd.h>
30 #endif
31
32 #ifndef APR_STATUS_IS_EPERM
33 #include <errno.h>
34 #ifdef EPERM
35 #define APR_STATUS_IS_EPERM(s)   ((s) == EPERM)
36 #else
37 #define APR_STATUS_IS_EPERM(s)   (0)
38 #endif
39 #endif
40
41 #include <apr_lib.h>
42 #include <apr_pools.h>
43 #include <apr_file_io.h>
44 #include <apr_file_info.h>
45 #include <apr_general.h>
46 #include <apr_strings.h>
47 #include <apr_portable.h>
48 #include <apr_md5.h>
49
50 #if APR_HAVE_FCNTL_H
51 #include <fcntl.h>
52 #endif
53
54 #include "svn_hash.h"
55 #include "svn_types.h"
56 #include "svn_dirent_uri.h"
57 #include "svn_path.h"
58 #include "svn_string.h"
59 #include "svn_error.h"
60 #include "svn_io.h"
61 #include "svn_pools.h"
62 #include "svn_utf.h"
63 #include "svn_config.h"
64 #include "svn_private_config.h"
65 #include "svn_ctype.h"
66
67 #include "private/svn_atomic.h"
68 #include "private/svn_io_private.h"
69 #include "private/svn_utf_private.h"
70 #include "private/svn_dep_compat.h"
71
72 #define SVN_SLEEP_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS"
73 \f
74 /*
75   Windows is 'aided' by a number of types of applications that
76   follow other applications around and open up files they have
77   changed for various reasons (the most intrusive are virus
78   scanners).  So, if one of these other apps has glommed onto
79   our file we may get an 'access denied' error.
80
81   This retry loop does not completely solve the problem (who
82   knows how long the other app is going to hold onto it for), but
83   goes a long way towards minimizing it.  It is not an infinite
84   loop because there might really be an error.
85
86   Another reason for retrying delete operations on Windows
87   is that they are asynchronous -- the file or directory is not
88   actually deleted until the last handle to it is closed.  The
89   retry loop cannot completely solve this problem either, but can
90   help mitigate it.
91 */
92 #define RETRY_MAX_ATTEMPTS 100
93 #define RETRY_INITIAL_SLEEP 1000
94 #define RETRY_MAX_SLEEP 128000
95
96 #define RETRY_LOOP(err, expr, retry_test, sleep_test)                      \
97   do                                                                       \
98     {                                                                      \
99       apr_status_t os_err = APR_TO_OS_ERROR(err);                          \
100       int sleep_count = RETRY_INITIAL_SLEEP;                               \
101       int retries;                                                         \
102       for (retries = 0;                                                    \
103            retries < RETRY_MAX_ATTEMPTS && (retry_test);                   \
104            os_err = APR_TO_OS_ERROR(err))                                  \
105         {                                                                  \
106           if (sleep_test)                                                  \
107             {                                                              \
108               ++retries;                                                   \
109               apr_sleep(sleep_count);                                      \
110               if (sleep_count < RETRY_MAX_SLEEP)                           \
111                 sleep_count *= 2;                                          \
112             }                                                              \
113           (err) = (expr);                                                  \
114         }                                                                  \
115     }                                                                      \
116   while (0)
117
118 #if defined(EDEADLK) && APR_HAS_THREADS
119 #define FILE_LOCK_RETRY_LOOP(err, expr)                                    \
120   RETRY_LOOP(err,                                                          \
121              expr,                                                         \
122              (APR_STATUS_IS_EINTR(err) || os_err == EDEADLK),              \
123              (!APR_STATUS_IS_EINTR(err)))
124 #else
125 #define FILE_LOCK_RETRY_LOOP(err, expr)                                    \
126   RETRY_LOOP(err,                                                          \
127              expr,                                                         \
128              (APR_STATUS_IS_EINTR(err)),                                   \
129              0)
130 #endif
131
132 #ifndef WIN32_RETRY_LOOP
133 #if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP)
134 #define WIN32_RETRY_LOOP(err, expr)                                        \
135   RETRY_LOOP(err, expr, (os_err == ERROR_ACCESS_DENIED                     \
136                          || os_err == ERROR_SHARING_VIOLATION              \
137                          || os_err == ERROR_DIR_NOT_EMPTY),                \
138              1)
139 #else
140 #define WIN32_RETRY_LOOP(err, expr) ((void)0)
141 #endif
142 #endif
143
144 #ifdef WIN32
145
146 #if _WIN32_WINNT < 0x600 /* Does the SDK assume Windows Vista+? */
147 typedef struct _FILE_RENAME_INFO {
148   BOOL   ReplaceIfExists;
149   HANDLE RootDirectory;
150   DWORD  FileNameLength;
151   WCHAR  FileName[1];
152 } FILE_RENAME_INFO, *PFILE_RENAME_INFO;
153
154 typedef struct _FILE_DISPOSITION_INFO {
155   BOOL DeleteFile;
156 } FILE_DISPOSITION_INFO, *PFILE_DISPOSITION_INFO;
157
158 #define FileRenameInfo 3
159 #define FileDispositionInfo 4
160 #endif /* WIN32 < Vista */
161
162 /* One-time initialization of the late bound Windows API functions. */
163 static volatile svn_atomic_t win_dynamic_imports_state = 0;
164
165 /* Pointer to GetFinalPathNameByHandleW function from kernel32.dll. */
166 typedef DWORD (WINAPI *GETFINALPATHNAMEBYHANDLE)(
167                HANDLE hFile,
168                WCHAR *lpszFilePath,
169                DWORD cchFilePath,
170                DWORD dwFlags);
171
172 typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile,
173                                                     int FileInformationClass,
174                                                     LPVOID lpFileInformation,
175                                                     DWORD dwBufferSize);
176
177 static GETFINALPATHNAMEBYHANDLE get_final_path_name_by_handle_proc = NULL;
178 static SetFileInformationByHandle_t set_file_information_by_handle_proc = NULL;
179
180 /* Forward declaration. */
181 static svn_error_t * io_win_read_link(svn_string_t **dest,
182                                       const char *path,
183                                       apr_pool_t *pool);
184
185 #endif
186
187 /* Forward declaration */
188 static apr_status_t
189 dir_is_empty(const char *dir, apr_pool_t *pool);
190 static APR_INLINE svn_error_t *
191 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
192                            const char *msg, const char *msg_no_name,
193                            apr_pool_t *pool);
194
195 /* Local wrapper of svn_path_cstring_to_utf8() that does no copying on
196  * operating systems where APR always uses utf-8 as native path format */
197 static svn_error_t *
198 cstring_to_utf8(const char **path_utf8,
199                 const char *path_apr,
200                 apr_pool_t *pool)
201 {
202 #if defined(WIN32) || defined(DARWIN)
203   *path_utf8 = path_apr;
204   return SVN_NO_ERROR;
205 #else
206   return svn_path_cstring_to_utf8(path_utf8, path_apr, pool);
207 #endif
208 }
209
210 /* Local wrapper of svn_path_cstring_from_utf8() that does no copying on
211  * operating systems where APR always uses utf-8 as native path format */
212 static svn_error_t *
213 cstring_from_utf8(const char **path_apr,
214                   const char *path_utf8,
215                   apr_pool_t *pool)
216 {
217 #if defined(WIN32) || defined(DARWIN)
218   *path_apr = path_utf8;
219   return SVN_NO_ERROR;
220 #else
221   return svn_path_cstring_from_utf8(path_apr, path_utf8, pool);
222 #endif
223 }
224
225 /* Helper function that allows to convert an APR-level PATH to something
226  * that we can pass the svn_error_wrap_apr. Since we use it in context
227  * of error reporting, having *some* path info may be more useful than
228  * having none.  Therefore, we use a best effort approach here.
229  *
230  * This is different from svn_io_file_name_get in that it uses a different
231  * signature style and will never fail.
232  */
233 static const char *
234 try_utf8_from_internal_style(const char *path, apr_pool_t *pool)
235 {
236   svn_error_t *error;
237   const char *path_utf8;
238
239   /* Special case. */
240   if (path == NULL)
241     return "(NULL)";
242
243   /* (try to) convert PATH to UTF-8. If that fails, continue with the plain
244    * PATH because it is the best we have. It may actually be UTF-8 already.
245    */
246   error = cstring_to_utf8(&path_utf8, path, pool);
247   if (error)
248     {
249       /* fallback to best representation we have */
250
251       svn_error_clear(error);
252       path_utf8 = path;
253     }
254
255   /* Toggle (back-)slashes etc. as necessary.
256    */
257   return svn_dirent_local_style(path_utf8, pool);
258 }
259
260
261 /* Set *NAME_P to the UTF-8 representation of directory entry NAME.
262  * NAME is in the internal encoding used by APR; PARENT is in
263  * UTF-8 and in internal (not local) style.
264  *
265  * Use PARENT only for generating an error string if the conversion
266  * fails because NAME could not be represented in UTF-8.  In that
267  * case, return a two-level error in which the outer error's message
268  * mentions PARENT, but the inner error's message does not mention
269  * NAME (except possibly in hex) since NAME may not be printable.
270  * Such a compound error at least allows the user to go looking in the
271  * right directory for the problem.
272  *
273  * If there is any other error, just return that error directly.
274  *
275  * If there is any error, the effect on *NAME_P is undefined.
276  *
277  * *NAME_P and NAME may refer to the same storage.
278  */
279 static svn_error_t *
280 entry_name_to_utf8(const char **name_p,
281                    const char *name,
282                    const char *parent,
283                    apr_pool_t *pool)
284 {
285 #if defined(WIN32) || defined(DARWIN)
286   *name_p = apr_pstrdup(pool, name);
287   return SVN_NO_ERROR;
288 #else
289   svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool);
290   if (err && err->apr_err == APR_EINVAL)
291     {
292       return svn_error_createf(err->apr_err, err,
293                                _("Error converting entry "
294                                  "in directory '%s' to UTF-8"),
295                                svn_dirent_local_style(parent, pool));
296     }
297   return err;
298 #endif
299 }
300
301
302
303 static void
304 map_apr_finfo_to_node_kind(svn_node_kind_t *kind,
305                            svn_boolean_t *is_special,
306                            apr_finfo_t *finfo)
307 {
308   *is_special = FALSE;
309
310   if (finfo->filetype == APR_REG)
311     *kind = svn_node_file;
312   else if (finfo->filetype == APR_DIR)
313     *kind = svn_node_dir;
314   else if (finfo->filetype == APR_LNK)
315     {
316       *is_special = TRUE;
317       *kind = svn_node_file;
318     }
319   else
320     *kind = svn_node_unknown;
321 }
322
323 /* Helper for svn_io_check_path() and svn_io_check_resolved_path();
324    essentially the same semantics as those two, with the obvious
325    interpretation for RESOLVE_SYMLINKS. */
326 static svn_error_t *
327 io_check_path(const char *path,
328               svn_boolean_t resolve_symlinks,
329               svn_boolean_t *is_special_p,
330               svn_node_kind_t *kind,
331               apr_pool_t *pool)
332 {
333   apr_int32_t flags;
334   apr_finfo_t finfo;
335   apr_status_t apr_err;
336   const char *path_apr;
337   svn_boolean_t is_special = FALSE;
338
339   if (path[0] == '\0')
340     path = ".";
341
342   /* Not using svn_io_stat() here because we want to check the
343      apr_err return explicitly. */
344   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
345
346   flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK);
347   apr_err = apr_stat(&finfo, path_apr, flags, pool);
348
349   if (APR_STATUS_IS_ENOENT(apr_err))
350     *kind = svn_node_none;
351   else if (SVN__APR_STATUS_IS_ENOTDIR(apr_err))
352     *kind = svn_node_none;
353   else if (apr_err)
354     return svn_error_wrap_apr(apr_err, _("Can't check path '%s'"),
355                               svn_dirent_local_style(path, pool));
356   else
357     map_apr_finfo_to_node_kind(kind, &is_special, &finfo);
358
359   *is_special_p = is_special;
360
361   return SVN_NO_ERROR;
362 }
363
364
365 /* Wrapper for apr_file_open(), taking an APR-encoded filename. */
366 static apr_status_t
367 file_open(apr_file_t **f,
368           const char *fname_apr,
369           apr_int32_t flag,
370           apr_fileperms_t perm,
371           svn_boolean_t retry_on_failure,
372           apr_pool_t *pool)
373 {
374   apr_status_t status = apr_file_open(f, fname_apr, flag, perm, pool);
375
376   if (retry_on_failure)
377     {
378 #ifdef WIN32
379       if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED))
380         {
381           if ((flag & (APR_CREATE | APR_EXCL)) == (APR_CREATE | APR_EXCL))
382             return status; /* Can't create if there is something */
383
384           if (flag & (APR_WRITE | APR_CREATE))
385             {
386               apr_finfo_t finfo;
387
388               if (!apr_stat(&finfo, fname_apr, SVN__APR_FINFO_READONLY, pool))
389                 {
390                   if (finfo.protection & APR_FREADONLY)
391                     return status; /* Retrying won't fix this */
392                 }
393             }
394         }
395 #endif
396
397       WIN32_RETRY_LOOP(status, apr_file_open(f, fname_apr, flag, perm, pool));
398     }
399   return status;
400 }
401
402
403 svn_error_t *
404 svn_io_check_resolved_path(const char *path,
405                            svn_node_kind_t *kind,
406                            apr_pool_t *pool)
407 {
408   svn_boolean_t ignored;
409   return io_check_path(path, TRUE, &ignored, kind, pool);
410 }
411
412 svn_error_t *
413 svn_io_check_path(const char *path,
414                   svn_node_kind_t *kind,
415                   apr_pool_t *pool)
416 {
417   svn_boolean_t ignored;
418   return io_check_path(path, FALSE, &ignored, kind, pool);
419 }
420
421 svn_error_t *
422 svn_io_check_special_path(const char *path,
423                           svn_node_kind_t *kind,
424                           svn_boolean_t *is_special,
425                           apr_pool_t *pool)
426 {
427   return io_check_path(path, FALSE, is_special, kind, pool);
428 }
429
430 struct temp_file_cleanup_s
431 {
432   apr_pool_t *pool;
433   /* The (APR-encoded) full path of the file to be removed, or NULL if
434    * nothing to do. */
435   const char *fname_apr;
436 };
437
438
439 static apr_status_t
440 temp_file_plain_cleanup_handler(void *baton)
441 {
442   struct  temp_file_cleanup_s *b = baton;
443   apr_status_t apr_err = APR_SUCCESS;
444
445   if (b->fname_apr)
446     {
447       apr_err = apr_file_remove(b->fname_apr, b->pool);
448       WIN32_RETRY_LOOP(apr_err, apr_file_remove(b->fname_apr, b->pool));
449     }
450
451   return apr_err;
452 }
453
454
455 static apr_status_t
456 temp_file_child_cleanup_handler(void *baton)
457 {
458   struct  temp_file_cleanup_s *b = baton;
459
460   apr_pool_cleanup_kill(b->pool, b,
461                         temp_file_plain_cleanup_handler);
462
463   return APR_SUCCESS;
464 }
465
466
467 svn_error_t *
468 svn_io_open_uniquely_named(apr_file_t **file,
469                            const char **unique_path,
470                            const char *dirpath,
471                            const char *filename,
472                            const char *suffix,
473                            svn_io_file_del_t delete_when,
474                            apr_pool_t *result_pool,
475                            apr_pool_t *scratch_pool)
476 {
477   const char *path;
478   unsigned int i;
479   struct temp_file_cleanup_s *baton = NULL;
480
481   /* At the beginning, we don't know whether unique_path will need
482      UTF8 conversion */
483   svn_boolean_t needs_utf8_conversion = TRUE;
484
485   SVN_ERR_ASSERT(file || unique_path);
486
487   if (dirpath == NULL)
488     SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
489   if (filename == NULL)
490     filename = "tempfile";
491   if (suffix == NULL)
492     suffix = ".tmp";
493
494   path = svn_dirent_join(dirpath, filename, scratch_pool);
495
496   if (delete_when == svn_io_file_del_on_pool_cleanup)
497     {
498       baton = apr_palloc(result_pool, sizeof(*baton));
499
500       baton->pool = result_pool;
501       baton->fname_apr = NULL;
502
503       /* Because cleanups are run LIFO, we need to make sure to register
504          our cleanup before the apr_file_close cleanup:
505
506          On Windows, you can't remove an open file.
507       */
508       apr_pool_cleanup_register(result_pool, baton,
509                                 temp_file_plain_cleanup_handler,
510                                 temp_file_child_cleanup_handler);
511     }
512
513   for (i = 1; i <= 99999; i++)
514     {
515       const char *unique_name;
516       const char *unique_name_apr;
517       apr_file_t *try_file;
518       apr_status_t apr_err;
519       apr_int32_t flag = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL
520                           | APR_BUFFERED | APR_BINARY);
521
522       if (delete_when == svn_io_file_del_on_close)
523         flag |= APR_DELONCLOSE;
524
525       /* Special case the first attempt -- if we can avoid having a
526          generated numeric portion at all, that's best.  So first we
527          try with just the suffix; then future tries add a number
528          before the suffix.  (A do-while loop could avoid the repeated
529          conditional, but it's not worth the clarity loss.)
530
531          If the first attempt fails, the first number will be "2".
532          This is good, since "1" would misleadingly imply that
533          the second attempt was actually the first... and if someone's
534          got conflicts on their conflicts, we probably don't want to
535          add to their confusion :-). */
536       if (i == 1)
537         unique_name = apr_psprintf(scratch_pool, "%s%s", path, suffix);
538       else
539         unique_name = apr_psprintf(scratch_pool, "%s.%u%s", path, i, suffix);
540
541       /* Hmmm.  Ideally, we would append to a native-encoding buf
542          before starting iteration, then convert back to UTF-8 for
543          return. But I suppose that would make the appending code
544          sensitive to i18n in a way it shouldn't be... Oh well. */
545       if (needs_utf8_conversion)
546         {
547           SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name,
548                                     scratch_pool));
549           if (i == 1)
550             {
551               /* The variable parts of unique_name will not require UTF8
552                  conversion. Therefore, if UTF8 conversion had no effect
553                  on it in the first iteration, it won't require conversion
554                  in any future iteration. */
555               needs_utf8_conversion = strcmp(unique_name_apr, unique_name);
556             }
557         }
558       else
559         unique_name_apr = unique_name;
560
561       apr_err = file_open(&try_file, unique_name_apr, flag,
562                           APR_OS_DEFAULT, FALSE, result_pool);
563
564       if (APR_STATUS_IS_EEXIST(apr_err))
565         continue;
566       else if (apr_err)
567         {
568           /* On Win32, CreateFile fails with an "Access Denied" error
569              code, rather than "File Already Exists", if the colliding
570              name belongs to a directory. */
571           if (APR_STATUS_IS_EACCES(apr_err))
572             {
573               apr_finfo_t finfo;
574               apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
575                                                 APR_FINFO_TYPE, scratch_pool);
576
577               if (!apr_err_2 && finfo.filetype == APR_DIR)
578                 continue;
579
580 #ifdef WIN32
581               apr_err_2 = APR_TO_OS_ERROR(apr_err);
582
583               if (apr_err_2 == ERROR_ACCESS_DENIED ||
584                   apr_err_2 == ERROR_SHARING_VIOLATION)
585                 {
586                   /* The file is in use by another process or is hidden;
587                      create a new name, but don't do this 99999 times in
588                      case the folder is not writable */
589                   i += 797;
590                   continue;
591                 }
592 #endif
593
594               /* Else fall through and return the original error. */
595             }
596
597           if (file)
598             *file = NULL;
599           if (unique_path)
600             *unique_path = NULL;
601           return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
602                                     svn_dirent_local_style(unique_name,
603                                                          scratch_pool));
604         }
605       else
606         {
607           if (delete_when == svn_io_file_del_on_pool_cleanup)
608             baton->fname_apr = apr_pstrdup(result_pool, unique_name_apr);
609
610           if (file)
611             *file = try_file;
612           else
613             apr_file_close(try_file);
614           if (unique_path)
615             *unique_path = apr_pstrdup(result_pool, unique_name);
616
617           return SVN_NO_ERROR;
618         }
619     }
620
621   if (file)
622     *file = NULL;
623   if (unique_path)
624     *unique_path = NULL;
625   return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
626                            NULL,
627                            _("Unable to make name for '%s'"),
628                            svn_dirent_local_style(path, scratch_pool));
629 }
630
631 svn_error_t *
632 svn_io_create_unique_link(const char **unique_name_p,
633                           const char *path,
634                           const char *dest,
635                           const char *suffix,
636                           apr_pool_t *pool)
637 {
638 #ifdef HAVE_SYMLINK
639   unsigned int i;
640   const char *unique_name;
641   const char *unique_name_apr;
642   const char *dest_apr;
643   int rv;
644
645   SVN_ERR(cstring_from_utf8(&dest_apr, dest, pool));
646   for (i = 1; i <= 99999; i++)
647     {
648       apr_status_t apr_err;
649
650       /* Special case the first attempt -- if we can avoid having a
651          generated numeric portion at all, that's best.  So first we
652          try with just the suffix; then future tries add a number
653          before the suffix.  (A do-while loop could avoid the repeated
654          conditional, but it's not worth the clarity loss.)
655
656          If the first attempt fails, the first number will be "2".
657          This is good, since "1" would misleadingly imply that
658          the second attempt was actually the first... and if someone's
659          got conflicts on their conflicts, we probably don't want to
660          add to their confusion :-). */
661       if (i == 1)
662         unique_name = apr_psprintf(pool, "%s%s", path, suffix);
663       else
664         unique_name = apr_psprintf(pool, "%s.%u%s", path, i, suffix);
665
666       /* Hmmm.  Ideally, we would append to a native-encoding buf
667          before starting iteration, then convert back to UTF-8 for
668          return. But I suppose that would make the appending code
669          sensitive to i18n in a way it shouldn't be... Oh well. */
670       SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, pool));
671       do {
672         rv = symlink(dest_apr, unique_name_apr);
673       } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
674
675       apr_err = apr_get_os_error();
676
677       if (rv == -1 && APR_STATUS_IS_EEXIST(apr_err))
678         continue;
679       else if (rv == -1 && apr_err)
680         {
681           /* On Win32, CreateFile fails with an "Access Denied" error
682              code, rather than "File Already Exists", if the colliding
683              name belongs to a directory. */
684           if (APR_STATUS_IS_EACCES(apr_err))
685             {
686               apr_finfo_t finfo;
687               apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
688                                                 APR_FINFO_TYPE, pool);
689
690               if (!apr_err_2
691                   && (finfo.filetype == APR_DIR))
692                 continue;
693
694               /* Else ignore apr_err_2; better to fall through and
695                  return the original error. */
696             }
697
698           *unique_name_p = NULL;
699           return svn_error_wrap_apr(apr_err,
700                                     _("Can't create symbolic link '%s'"),
701                                     svn_dirent_local_style(unique_name, pool));
702         }
703       else
704         {
705           *unique_name_p = unique_name;
706           return SVN_NO_ERROR;
707         }
708     }
709
710   *unique_name_p = NULL;
711   return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
712                            NULL,
713                            _("Unable to make name for '%s'"),
714                            svn_dirent_local_style(path, pool));
715 #else
716   return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
717                           _("Symbolic links are not supported on this "
718                             "platform"));
719 #endif
720 }
721
722 svn_error_t *
723 svn_io_read_link(svn_string_t **dest,
724                  const char *path,
725                  apr_pool_t *pool)
726 {
727 #if defined(HAVE_READLINK)
728   svn_string_t dest_apr;
729   const char *path_apr;
730   char buf[1025];
731   ssize_t rv;
732
733   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
734   do {
735     rv = readlink(path_apr, buf, sizeof(buf) - 1);
736   } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
737
738   if (rv == -1)
739     return svn_error_wrap_apr(apr_get_os_error(),
740                               _("Can't read contents of link"));
741
742   buf[rv] = '\0';
743   dest_apr.data = buf;
744   dest_apr.len = rv;
745
746   /* ### Cast needed, one of these interfaces is wrong */
747   return svn_utf_string_to_utf8((const svn_string_t **)dest, &dest_apr, pool);
748 #elif defined(WIN32)
749   return io_win_read_link(dest, path, pool);
750 #else
751   return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
752                           _("Symbolic links are not supported on this "
753                             "platform"));
754 #endif
755 }
756
757
758 svn_error_t *
759 svn_io_copy_link(const char *src,
760                  const char *dst,
761                  apr_pool_t *pool)
762
763 {
764 #ifdef HAVE_READLINK
765   svn_string_t *link_dest;
766   const char *dst_tmp;
767
768   /* Notice what the link is pointing at... */
769   SVN_ERR(svn_io_read_link(&link_dest, src, pool));
770
771   /* Make a tmp-link pointing at the same thing. */
772   SVN_ERR(svn_io_create_unique_link(&dst_tmp, dst, link_dest->data,
773                                     ".tmp", pool));
774
775   /* Move the tmp-link to link. */
776   return svn_io_file_rename(dst_tmp, dst, pool);
777
778 #else
779   return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
780                           _("Symbolic links are not supported on this "
781                             "platform"));
782 #endif
783 }
784
785 /* Temporary directory name cache for svn_io_temp_dir() */
786 static volatile svn_atomic_t temp_dir_init_state = 0;
787 static const char *temp_dir;
788
789 /* Helper function to initialize temp dir. Passed to svn_atomic__init_once */
790 static svn_error_t *
791 init_temp_dir(void *baton, apr_pool_t *scratch_pool)
792 {
793   /* Global pool for the temp path */
794   apr_pool_t *global_pool = svn_pool_create(NULL);
795   const char *dir;
796
797   apr_status_t apr_err = apr_temp_dir_get(&dir, scratch_pool);
798
799   if (apr_err)
800     return svn_error_wrap_apr(apr_err, _("Can't find a temporary directory"));
801
802   SVN_ERR(cstring_to_utf8(&dir, dir, scratch_pool));
803
804   dir = svn_dirent_internal_style(dir, scratch_pool);
805
806   SVN_ERR(svn_dirent_get_absolute(&temp_dir, dir, global_pool));
807
808   return SVN_NO_ERROR;
809 }
810
811
812 svn_error_t *
813 svn_io_temp_dir(const char **dir,
814                 apr_pool_t *pool)
815 {
816   SVN_ERR(svn_atomic__init_once(&temp_dir_init_state,
817                                 init_temp_dir, NULL, pool));
818
819   *dir = apr_pstrdup(pool, temp_dir);
820
821   return SVN_NO_ERROR;
822 }
823
824
825
826 \f
827 /*** Creating, copying and appending files. ***/
828
829 /* Transfer the contents of FROM_FILE to TO_FILE, using POOL for temporary
830  * allocations.
831  *
832  * NOTE: We don't use apr_copy_file() for this, since it takes filenames
833  * as parameters.  Since we want to copy to a temporary file
834  * and rename for atomicity (see below), this would require an extra
835  * close/open pair, which can be expensive, especially on
836  * remote file systems.
837  */
838 static apr_status_t
839 copy_contents(apr_file_t *from_file,
840               apr_file_t *to_file,
841               apr_pool_t *pool)
842 {
843   /* Copy bytes till the cows come home. */
844   while (1)
845     {
846       char buf[SVN__STREAM_CHUNK_SIZE];
847       apr_size_t bytes_this_time = sizeof(buf);
848       apr_status_t read_err;
849       apr_status_t write_err;
850
851       /* Read 'em. */
852       read_err = apr_file_read(from_file, buf, &bytes_this_time);
853       if (read_err && !APR_STATUS_IS_EOF(read_err))
854         {
855           return read_err;
856         }
857
858       /* Write 'em. */
859       write_err = apr_file_write_full(to_file, buf, bytes_this_time, NULL);
860       if (write_err)
861         {
862           return write_err;
863         }
864
865       if (read_err && APR_STATUS_IS_EOF(read_err))
866         {
867           /* Return the results of this close: an error, or success. */
868           return APR_SUCCESS;
869         }
870     }
871   /* NOTREACHED */
872 }
873
874
875 svn_error_t *
876 svn_io_copy_file(const char *src,
877                  const char *dst,
878                  svn_boolean_t copy_perms,
879                  apr_pool_t *pool)
880 {
881   apr_file_t *from_file, *to_file;
882   apr_status_t apr_err;
883   const char *dst_tmp;
884   svn_error_t *err;
885
886   /* ### NOTE: sometimes src == dst. In this case, because we copy to a
887      ###   temporary file, and then rename over the top of the destination,
888      ###   the net result is resetting the permissions on src/dst.
889      ###
890      ### Note: specifically, this can happen during a switch when the desired
891      ###   permissions for a file change from one branch to another. See
892      ###   switch_tests 17.
893      ###
894      ### ... yes, we should avoid copying to the same file, and we should
895      ###     make the "reset perms" explicit. The switch *happens* to work
896      ###     because of this copy-to-temp-then-rename implementation. If it
897      ###     weren't for that, the switch would break.
898   */
899 #ifdef CHECK_FOR_SAME_FILE
900   if (strcmp(src, dst) == 0)
901     return SVN_NO_ERROR;
902 #endif
903
904   SVN_ERR(svn_io_file_open(&from_file, src, APR_READ,
905                            APR_OS_DEFAULT, pool));
906
907   /* For atomicity, we copy to a tmp file and then rename the tmp
908      file over the real destination. */
909
910   SVN_ERR(svn_io_open_unique_file3(&to_file, &dst_tmp,
911                                    svn_dirent_dirname(dst, pool),
912                                    svn_io_file_del_none, pool, pool));
913
914   apr_err = copy_contents(from_file, to_file, pool);
915
916   if (apr_err)
917     {
918       err = svn_error_wrap_apr(apr_err, _("Can't copy '%s' to '%s'"),
919                                svn_dirent_local_style(src, pool),
920                                svn_dirent_local_style(dst_tmp, pool));
921     }
922    else
923      err = NULL;
924
925   err = svn_error_compose_create(err,
926                                  svn_io_file_close(from_file, pool));
927
928   err = svn_error_compose_create(err,
929                                  svn_io_file_close(to_file, pool));
930
931   if (err)
932     {
933       return svn_error_compose_create(
934                                  err,
935                                  svn_io_remove_file2(dst_tmp, TRUE, pool));
936     }
937
938   /* If copying perms, set the perms on dst_tmp now, so they will be
939      atomically inherited in the upcoming rename.  But note that we
940      had to wait until now to set perms, because if they say
941      read-only, then we'd have failed filling dst_tmp's contents. */
942   if (copy_perms)
943     SVN_ERR(svn_io_copy_perms(src, dst_tmp, pool));
944
945   return svn_error_trace(svn_io_file_rename(dst_tmp, dst, pool));
946 }
947
948 #if !defined(WIN32) && !defined(__OS2__)
949 /* Wrapper for apr_file_perms_set(), taking a UTF8-encoded filename. */
950 static svn_error_t *
951 file_perms_set(const char *fname, apr_fileperms_t perms,
952                apr_pool_t *pool)
953 {
954   const char *fname_apr;
955   apr_status_t status;
956
957   SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
958
959   status = apr_file_perms_set(fname_apr, perms);
960   if (status)
961     return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
962                               fname);
963   else
964     return SVN_NO_ERROR;
965 }
966
967 /* Set permissions PERMS on the FILE. This is a cheaper variant of the
968  * file_perms_set wrapper() function because no locale-dependent string
969  * conversion is required. POOL will be used for allocations.
970  */
971 static svn_error_t *
972 file_perms_set2(apr_file_t* file, apr_fileperms_t perms, apr_pool_t *pool)
973 {
974   const char *fname_apr;
975   apr_status_t status;
976
977   status = apr_file_name_get(&fname_apr, file);
978   if (status)
979     return svn_error_wrap_apr(status, _("Can't get file name"));
980
981   status = apr_file_perms_set(fname_apr, perms);
982   if (status)
983     return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
984                               try_utf8_from_internal_style(fname_apr, pool));
985   else
986     return SVN_NO_ERROR;
987 }
988
989 #endif /* !WIN32 && !__OS2__ */
990
991 svn_error_t *
992 svn_io_copy_perms(const char *src,
993                   const char *dst,
994                   apr_pool_t *pool)
995 {
996   /* ### On Windows or OS/2, apr_file_perms_set always returns APR_ENOTIMPL,
997          and the path passed to apr_file_perms_set must be encoded
998          in the platform-specific path encoding; not necessary UTF-8.
999          We need a platform-specific implementation to get the
1000          permissions right. */
1001
1002 #if !defined(WIN32) && !defined(__OS2__)
1003   {
1004     apr_finfo_t finfo;
1005     svn_node_kind_t kind;
1006     svn_boolean_t is_special;
1007     svn_error_t *err;
1008
1009     /* If DST is a symlink, don't bother copying permissions. */
1010     SVN_ERR(svn_io_check_special_path(dst, &kind, &is_special, pool));
1011     if (is_special)
1012       return SVN_NO_ERROR;
1013
1014     SVN_ERR(svn_io_stat(&finfo, src, APR_FINFO_PROT, pool));
1015     err = file_perms_set(dst, finfo.protection, pool);
1016     if (err)
1017       {
1018         /* We shouldn't be able to get APR_INCOMPLETE or APR_ENOTIMPL
1019            here under normal circumstances, because the perms themselves
1020            came from a call to apr_file_info_get(), and we already know
1021            this is the non-Win32 case.  But if it does happen, it's not
1022            an error. */
1023         if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
1024             APR_STATUS_IS_ENOTIMPL(err->apr_err))
1025           svn_error_clear(err);
1026         else
1027           {
1028             return svn_error_quick_wrapf(
1029                      err, _("Can't set permissions on '%s'"),
1030                      svn_dirent_local_style(dst, pool));
1031           }
1032       }
1033   }
1034 #endif /* !WIN32 && !__OS2__ */
1035
1036   return SVN_NO_ERROR;
1037 }
1038
1039
1040 svn_error_t *
1041 svn_io_append_file(const char *src, const char *dst, apr_pool_t *pool)
1042 {
1043   apr_status_t apr_err;
1044   const char *src_apr, *dst_apr;
1045
1046   SVN_ERR(cstring_from_utf8(&src_apr, src, pool));
1047   SVN_ERR(cstring_from_utf8(&dst_apr, dst, pool));
1048
1049   apr_err = apr_file_append(src_apr, dst_apr, APR_OS_DEFAULT, pool);
1050
1051   if (apr_err)
1052     return svn_error_wrap_apr(apr_err, _("Can't append '%s' to '%s'"),
1053                               svn_dirent_local_style(src, pool),
1054                               svn_dirent_local_style(dst, pool));
1055
1056   return SVN_NO_ERROR;
1057 }
1058
1059
1060 svn_error_t *svn_io_copy_dir_recursively(const char *src,
1061                                          const char *dst_parent,
1062                                          const char *dst_basename,
1063                                          svn_boolean_t copy_perms,
1064                                          svn_cancel_func_t cancel_func,
1065                                          void *cancel_baton,
1066                                          apr_pool_t *pool)
1067 {
1068   svn_node_kind_t kind;
1069   apr_status_t status;
1070   const char *dst_path;
1071   apr_dir_t *this_dir;
1072   apr_finfo_t this_entry;
1073   apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
1074
1075   /* Make a subpool for recursion */
1076   apr_pool_t *subpool = svn_pool_create(pool);
1077
1078   /* The 'dst_path' is simply dst_parent/dst_basename */
1079   dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
1080
1081   /* Sanity checks:  SRC and DST_PARENT are directories, and
1082      DST_BASENAME doesn't already exist in DST_PARENT. */
1083   SVN_ERR(svn_io_check_path(src, &kind, subpool));
1084   if (kind != svn_node_dir)
1085     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1086                              _("Source '%s' is not a directory"),
1087                              svn_dirent_local_style(src, pool));
1088
1089   SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool));
1090   if (kind != svn_node_dir)
1091     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1092                              _("Destination '%s' is not a directory"),
1093                              svn_dirent_local_style(dst_parent, pool));
1094
1095   SVN_ERR(svn_io_check_path(dst_path, &kind, subpool));
1096   if (kind != svn_node_none)
1097     return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
1098                              _("Destination '%s' already exists"),
1099                              svn_dirent_local_style(dst_path, pool));
1100
1101   /* Create the new directory. */
1102   /* ### TODO: copy permissions (needs apr_file_attrs_get()) */
1103   SVN_ERR(svn_io_dir_make(dst_path, APR_OS_DEFAULT, pool));
1104
1105   /* Loop over the dirents in SRC.  ('.' and '..' are auto-excluded) */
1106   SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
1107
1108   for (status = apr_dir_read(&this_entry, flags, this_dir);
1109        status == APR_SUCCESS;
1110        status = apr_dir_read(&this_entry, flags, this_dir))
1111     {
1112       if ((this_entry.name[0] == '.')
1113           && ((this_entry.name[1] == '\0')
1114               || ((this_entry.name[1] == '.')
1115                   && (this_entry.name[2] == '\0'))))
1116         {
1117           continue;
1118         }
1119       else
1120         {
1121           const char *src_target, *entryname_utf8;
1122
1123           if (cancel_func)
1124             SVN_ERR(cancel_func(cancel_baton));
1125
1126           SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
1127                                      src, subpool));
1128           src_target = svn_dirent_join(src, entryname_utf8, subpool);
1129
1130           if (this_entry.filetype == APR_REG) /* regular file */
1131             {
1132               const char *dst_target = svn_dirent_join(dst_path,
1133                                                        entryname_utf8,
1134                                                        subpool);
1135               SVN_ERR(svn_io_copy_file(src_target, dst_target,
1136                                        copy_perms, subpool));
1137             }
1138           else if (this_entry.filetype == APR_LNK) /* symlink */
1139             {
1140               const char *dst_target = svn_dirent_join(dst_path,
1141                                                        entryname_utf8,
1142                                                        subpool);
1143               SVN_ERR(svn_io_copy_link(src_target, dst_target,
1144                                        subpool));
1145             }
1146           else if (this_entry.filetype == APR_DIR) /* recurse */
1147             {
1148               /* Prevent infinite recursion by filtering off our
1149                  newly created destination path. */
1150               if (strcmp(src, dst_parent) == 0
1151                   && strcmp(entryname_utf8, dst_basename) == 0)
1152                 continue;
1153
1154               SVN_ERR(svn_io_copy_dir_recursively
1155                       (src_target,
1156                        dst_path,
1157                        entryname_utf8,
1158                        copy_perms,
1159                        cancel_func,
1160                        cancel_baton,
1161                        subpool));
1162             }
1163           /* ### support other APR node types someday?? */
1164
1165         }
1166     }
1167
1168   if (! (APR_STATUS_IS_ENOENT(status)))
1169     return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
1170                               svn_dirent_local_style(src, pool));
1171
1172   status = apr_dir_close(this_dir);
1173   if (status)
1174     return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
1175                               svn_dirent_local_style(src, pool));
1176
1177   /* Free any memory used by recursion */
1178   svn_pool_destroy(subpool);
1179
1180   return SVN_NO_ERROR;
1181 }
1182
1183
1184 svn_error_t *
1185 svn_io_make_dir_recursively(const char *path, apr_pool_t *pool)
1186 {
1187   const char *path_apr;
1188   apr_status_t apr_err;
1189
1190   if (svn_path_is_empty(path))
1191     /* Empty path (current dir) is assumed to always exist,
1192        so we do nothing, per docs. */
1193     return SVN_NO_ERROR;
1194
1195   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1196
1197   apr_err = apr_dir_make_recursive(path_apr, APR_OS_DEFAULT, pool);
1198 #ifdef WIN32
1199   /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a
1200      permanent error */
1201   if (apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
1202     WIN32_RETRY_LOOP(apr_err, apr_dir_make_recursive(path_apr,
1203                                                      APR_OS_DEFAULT, pool));
1204 #endif
1205
1206   if (apr_err)
1207     return svn_error_wrap_apr(apr_err, _("Can't make directory '%s'"),
1208                               svn_dirent_local_style(path, pool));
1209
1210   return SVN_NO_ERROR;
1211 }
1212
1213 svn_error_t *
1214 svn_io_file_create_bytes(const char *file,
1215                          const void *contents,
1216                          apr_size_t length,
1217                          apr_pool_t *scratch_pool)
1218 {
1219   apr_file_t *f;
1220   apr_size_t written;
1221   svn_error_t *err = SVN_NO_ERROR;
1222
1223   SVN_ERR(svn_io_file_open(&f, file,
1224                            (APR_WRITE | APR_CREATE | APR_EXCL),
1225                            APR_OS_DEFAULT,
1226                            scratch_pool));
1227   if (length)
1228     err = svn_io_file_write_full(f, contents, length, &written,
1229                                  scratch_pool);
1230
1231   err = svn_error_compose_create(
1232                     err,
1233                     svn_io_file_close(f, scratch_pool));
1234
1235   if (err)
1236     {
1237       /* Our caller doesn't know if we left a file or not if we return
1238          an error. Better to cleanup after ourselves if we created the
1239          file. */
1240       return svn_error_trace(
1241                 svn_error_compose_create(
1242                     err,
1243                     svn_io_remove_file2(file, TRUE, scratch_pool)));
1244     }
1245
1246   return SVN_NO_ERROR;
1247 }
1248
1249 svn_error_t *
1250 svn_io_file_create(const char *file,
1251                    const char *contents,
1252                    apr_pool_t *pool)
1253 {
1254   return svn_error_trace(svn_io_file_create_bytes(file, contents,
1255                                                   contents ? strlen(contents)
1256                                                            : 0,
1257                                                   pool));
1258 }
1259
1260 svn_error_t *
1261 svn_io_file_create_empty(const char *file,
1262                          apr_pool_t *scratch_pool)
1263 {
1264   return svn_error_trace(svn_io_file_create_bytes(file, NULL, 0,
1265                                                   scratch_pool));
1266 }
1267
1268 svn_error_t *
1269 svn_io_dir_file_copy(const char *src_path,
1270                      const char *dest_path,
1271                      const char *file,
1272                      apr_pool_t *pool)
1273 {
1274   const char *file_dest_path = svn_dirent_join(dest_path, file, pool);
1275   const char *file_src_path = svn_dirent_join(src_path, file, pool);
1276
1277   return svn_error_trace(
1278             svn_io_copy_file(file_src_path, file_dest_path, TRUE, pool));
1279 }
1280
1281 \f
1282 /*** Modtime checking. ***/
1283
1284 svn_error_t *
1285 svn_io_file_affected_time(apr_time_t *apr_time,
1286                           const char *path,
1287                           apr_pool_t *pool)
1288 {
1289   apr_finfo_t finfo;
1290
1291   SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_MIN | APR_FINFO_LINK, pool));
1292
1293   *apr_time = finfo.mtime;
1294
1295   return SVN_NO_ERROR;
1296 }
1297
1298
1299 svn_error_t *
1300 svn_io_set_file_affected_time(apr_time_t apr_time,
1301                               const char *path,
1302                               apr_pool_t *pool)
1303 {
1304   apr_status_t status;
1305   const char *native_path;
1306
1307   SVN_ERR(cstring_from_utf8(&native_path, path, pool));
1308   status = apr_file_mtime_set(native_path, apr_time, pool);
1309
1310   if (status)
1311     return svn_error_wrap_apr(status, _("Can't set access time of '%s'"),
1312                               svn_dirent_local_style(path, pool));
1313
1314   return SVN_NO_ERROR;
1315 }
1316
1317
1318 void
1319 svn_io_sleep_for_timestamps(const char *path, apr_pool_t *pool)
1320 {
1321   apr_time_t now, then;
1322   svn_error_t *err;
1323   char *sleep_env_var;
1324
1325   sleep_env_var = getenv(SVN_SLEEP_ENV_VAR);
1326
1327   if (sleep_env_var && apr_strnatcasecmp(sleep_env_var, "yes") == 0)
1328     return; /* Allow skipping for testing */
1329
1330   now = apr_time_now();
1331
1332   /* Calculate 0.02 seconds after the next second wallclock tick. */
1333   then = apr_time_make(apr_time_sec(now) + 1, APR_USEC_PER_SEC / 50);
1334
1335   /* Worst case is waiting one second, so we can use that time to determine
1336      if we can sleep shorter than that */
1337   if (path)
1338     {
1339       apr_finfo_t finfo;
1340
1341       err = svn_io_stat(&finfo, path, APR_FINFO_MTIME | APR_FINFO_LINK, pool);
1342
1343       if (err)
1344         {
1345           svn_error_clear(err); /* Fall back on original behavior */
1346         }
1347       else if (finfo.mtime % APR_USEC_PER_SEC)
1348         {
1349           /* Very simplistic but safe approach:
1350               If the filesystem has < sec mtime we can be reasonably sure
1351               that the filesystem has some sub-second resolution.  On Windows
1352               it is likely to be sub-millisecond; on Linux systems it depends
1353               on the filesystem, ext4 is typically 1ms, 4ms or 10ms resolution.
1354
1355              ## Perhaps find a better algorithm here. This will fail once
1356                 in every 1000 cases on a millisecond precision filesystem
1357                 if the mtime happens to be an exact second.
1358
1359                 But better to fail once in every thousand cases than every
1360                 time, like we did before.
1361
1362              Note for further research on algorithm:
1363                FAT32 has < 1 sec precision on ctime, but 2 sec on mtime.
1364
1365                Linux/ext4 with CONFIG_HZ=250 has high resolution
1366                apr_time_now and although the filesystem timestamps
1367                have similar high precision they are only updated with
1368                a coarser 4ms resolution. */
1369
1370           /* 10 milliseconds after now. */
1371 #ifndef SVN_HI_RES_SLEEP_MS
1372 #define SVN_HI_RES_SLEEP_MS 10
1373 #endif
1374           then = now + apr_time_from_msec(SVN_HI_RES_SLEEP_MS);
1375         }
1376
1377       /* Remove time taken to do stat() from sleep. */
1378       now = apr_time_now();
1379     }
1380
1381   if (now >= then)
1382     return; /* Passing negative values may suspend indefinitely (Windows) */
1383
1384   /* (t < 1000 will be round to 0 in apr) */
1385   if (then - now < 1000)
1386     apr_sleep(1000);
1387   else
1388     apr_sleep(then - now);
1389 }
1390
1391
1392 svn_error_t *
1393 svn_io_filesizes_different_p(svn_boolean_t *different_p,
1394                              const char *file1,
1395                              const char *file2,
1396                              apr_pool_t *pool)
1397 {
1398   apr_finfo_t finfo1;
1399   apr_finfo_t finfo2;
1400   apr_status_t status;
1401   const char *file1_apr, *file2_apr;
1402
1403   /* Not using svn_io_stat() because don't want to generate
1404      svn_error_t objects for non-error conditions. */
1405
1406   SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool));
1407   SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool));
1408
1409   /* Stat both files */
1410   status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool);
1411   if (status)
1412     {
1413       /* If we got an error stat'ing a file, it could be because the
1414          file was removed... or who knows.  Whatever the case, we
1415          don't know if the filesizes are definitely different, so
1416          assume that they're not. */
1417       *different_p = FALSE;
1418       return SVN_NO_ERROR;
1419     }
1420
1421   status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool);
1422   if (status)
1423     {
1424       /* See previous comment. */
1425       *different_p = FALSE;
1426       return SVN_NO_ERROR;
1427     }
1428
1429   /* Examine file sizes */
1430   if (finfo1.size == finfo2.size)
1431     *different_p = FALSE;
1432   else
1433     *different_p = TRUE;
1434
1435   return SVN_NO_ERROR;
1436 }
1437
1438
1439 svn_error_t *
1440 svn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
1441                                    svn_boolean_t *different_p23,
1442                                    svn_boolean_t *different_p13,
1443                                    const char *file1,
1444                                    const char *file2,
1445                                    const char *file3,
1446                                    apr_pool_t *scratch_pool)
1447 {
1448   apr_finfo_t finfo1, finfo2, finfo3;
1449   apr_status_t status1, status2, status3;
1450   const char *file1_apr, *file2_apr, *file3_apr;
1451
1452   /* Not using svn_io_stat() because don't want to generate
1453      svn_error_t objects for non-error conditions. */
1454
1455   SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool));
1456   SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool));
1457   SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool));
1458
1459   /* Stat all three files */
1460   status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool);
1461   status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool);
1462   status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool);
1463
1464   /* If we got an error stat'ing a file, it could be because the
1465      file was removed... or who knows.  Whatever the case, we
1466      don't know if the filesizes are definitely different, so
1467      assume that they're not. */
1468   *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size;
1469   *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size;
1470   *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size;
1471
1472   return SVN_NO_ERROR;
1473 }
1474
1475
1476 svn_error_t *
1477 svn_io_file_checksum2(svn_checksum_t **checksum,
1478                       const char *file,
1479                       svn_checksum_kind_t kind,
1480                       apr_pool_t *pool)
1481 {
1482   svn_stream_t *file_stream;
1483   svn_stream_t *checksum_stream;
1484   apr_file_t* f;
1485
1486   SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool));
1487   file_stream = svn_stream_from_aprfile2(f, FALSE, pool);
1488   checksum_stream = svn_stream_checksummed2(file_stream, checksum, NULL, kind,
1489                                             TRUE, pool);
1490
1491   /* Because the checksummed stream will force the reading (and
1492      checksumming) of all the file's bytes, we can just close the stream
1493      and let its magic work. */
1494   return svn_stream_close(checksum_stream);
1495 }
1496
1497
1498 svn_error_t *
1499 svn_io_file_checksum(unsigned char digest[],
1500                      const char *file,
1501                      apr_pool_t *pool)
1502 {
1503   svn_checksum_t *checksum;
1504
1505   SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool));
1506   memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE);
1507
1508   return SVN_NO_ERROR;
1509 }
1510
1511
1512 \f
1513 /*** Permissions and modes. ***/
1514
1515 #if !defined(WIN32) && !defined(__OS2__)
1516 /* Given the file specified by PATH, attempt to create an
1517    identical version of it owned by the current user.  This is done by
1518    moving it to a temporary location, copying the file back to its old
1519    path, then deleting the temporarily moved version.  All temporary
1520    allocations are done in POOL. */
1521 static svn_error_t *
1522 reown_file(const char *path,
1523            apr_pool_t *pool)
1524 {
1525   const char *unique_name;
1526
1527   SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name,
1528                                    svn_dirent_dirname(path, pool),
1529                                    svn_io_file_del_none, pool, pool));
1530   SVN_ERR(svn_io_file_rename(path, unique_name, pool));
1531   SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool));
1532   return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool));
1533 }
1534
1535 /* Determine what the PERMS for a new file should be by looking at the
1536    permissions of a temporary file that we create.
1537    Unfortunately, umask() as defined in POSIX provides no thread-safe way
1538    to get at the current value of the umask, so what we're doing here is
1539    the only way we have to determine which combination of write bits
1540    (User/Group/World) should be set by default.
1541    Make temporary allocations in SCRATCH_POOL.  */
1542 static svn_error_t *
1543 get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool)
1544 {
1545   /* the default permissions as read from the temp folder */
1546   static apr_fileperms_t default_perms = 0;
1547
1548   /* Technically, this "racy": Multiple threads may use enter here and
1549      try to figure out the default permission concurrently. That's fine
1550      since they will end up with the same results. Even more technical,
1551      apr_fileperms_t is an atomic type on 32+ bit machines.
1552    */
1553   if (default_perms == 0)
1554     {
1555       apr_finfo_t finfo;
1556       apr_file_t *fd;
1557       const char *fname_base, *fname;
1558       apr_uint32_t randomish;
1559       svn_error_t *err;
1560
1561       /* Get the perms for a newly created file to find out what bits
1562         should be set.
1563
1564         Explicitly delete the file because we want this file to be as
1565         short-lived as possible since its presence means other
1566         processes may have to try multiple names.
1567
1568         Using svn_io_open_uniquely_named() here because other tempfile
1569         creation functions tweak the permission bits of files they create.
1570       */
1571       randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool
1572                    + (apr_uint32_t)apr_time_now());
1573       fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish);
1574
1575       SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, NULL, fname_base,
1576                                          NULL, svn_io_file_del_none,
1577                                          scratch_pool, scratch_pool));
1578       err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool);
1579       err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool));
1580       err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE,
1581                                                               scratch_pool));
1582       SVN_ERR(err);
1583       *perms = finfo.protection;
1584       default_perms = finfo.protection;
1585     }
1586   else
1587     *perms = default_perms;
1588
1589   return SVN_NO_ERROR;
1590 }
1591
1592 /* OR together permission bits of the file FD and the default permissions
1593    of a file as determined by get_default_file_perms(). Do temporary
1594    allocations in SCRATCH_POOL. */
1595 static svn_error_t *
1596 merge_default_file_perms(apr_file_t *fd, apr_fileperms_t *perms,
1597                          apr_pool_t *scratch_pool)
1598 {
1599   apr_finfo_t finfo;
1600   apr_fileperms_t default_perms;
1601
1602   SVN_ERR(get_default_file_perms(&default_perms, scratch_pool));
1603   SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool));
1604
1605   /* Glom the perms together. */
1606   *perms = default_perms | finfo.protection;
1607   return SVN_NO_ERROR;
1608 }
1609
1610 /* This is a helper function for the svn_io_set_file_read* functions
1611    that attempts to honor the users umask when dealing with
1612    permission changes.  It is a no-op when invoked on a symlink. */
1613 static svn_error_t *
1614 io_set_file_perms(const char *path,
1615                   svn_boolean_t change_readwrite,
1616                   svn_boolean_t enable_write,
1617                   svn_boolean_t change_executable,
1618                   svn_boolean_t executable,
1619                   svn_boolean_t ignore_enoent,
1620                   apr_pool_t *pool)
1621 {
1622   apr_status_t status;
1623   const char *path_apr;
1624   apr_finfo_t finfo;
1625   apr_fileperms_t perms_to_set;
1626
1627   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1628
1629   /* Try to change only a minimal amount of the perms first
1630      by getting the current perms and adding bits
1631      only on where read perms are granted.  If this fails
1632      fall through to just setting file attributes. */
1633   status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool);
1634   if (status)
1635     {
1636       if (ignore_enoent && (APR_STATUS_IS_ENOENT(status)
1637                             || SVN__APR_STATUS_IS_ENOTDIR(status)))
1638         return SVN_NO_ERROR;
1639       else if (status != APR_ENOTIMPL)
1640         return svn_error_wrap_apr(status,
1641                                   _("Can't change perms of file '%s'"),
1642                                   svn_dirent_local_style(path, pool));
1643       return SVN_NO_ERROR;
1644     }
1645
1646   if (finfo.filetype == APR_LNK)
1647     return SVN_NO_ERROR;
1648
1649   perms_to_set = finfo.protection;
1650   if (change_readwrite)
1651     {
1652       if (enable_write) /* Make read-write. */
1653         {
1654           /* Tweak the owner bits only. The group/other bits aren't safe to
1655            * touch because we may end up setting them in undesired ways. */
1656           perms_to_set |= (APR_UREAD|APR_UWRITE);
1657         }
1658       else
1659         {
1660           if (finfo.protection & APR_UREAD)
1661             perms_to_set &= ~APR_UWRITE;
1662           if (finfo.protection & APR_GREAD)
1663             perms_to_set &= ~APR_GWRITE;
1664           if (finfo.protection & APR_WREAD)
1665             perms_to_set &= ~APR_WWRITE;
1666         }
1667     }
1668
1669   if (change_executable)
1670     {
1671       if (executable)
1672         {
1673           if (finfo.protection & APR_UREAD)
1674             perms_to_set |= APR_UEXECUTE;
1675           if (finfo.protection & APR_GREAD)
1676             perms_to_set |= APR_GEXECUTE;
1677           if (finfo.protection & APR_WREAD)
1678             perms_to_set |= APR_WEXECUTE;
1679         }
1680       else
1681         {
1682           if (finfo.protection & APR_UREAD)
1683             perms_to_set &= ~APR_UEXECUTE;
1684           if (finfo.protection & APR_GREAD)
1685             perms_to_set &= ~APR_GEXECUTE;
1686           if (finfo.protection & APR_WREAD)
1687             perms_to_set &= ~APR_WEXECUTE;
1688         }
1689     }
1690
1691   /* If we aren't changing anything then just return, this saves
1692      some system calls and helps with shared working copies */
1693   if (perms_to_set == finfo.protection)
1694     return SVN_NO_ERROR;
1695
1696   status = apr_file_perms_set(path_apr, perms_to_set);
1697   if (!status)
1698     return SVN_NO_ERROR;
1699
1700   if (APR_STATUS_IS_EPERM(status))
1701     {
1702       /* We don't have permissions to change the
1703          permissions!  Try a move, copy, and delete
1704          workaround to see if we can get the file owned by
1705          us.  If these succeed, try the permissions set
1706          again.
1707
1708          Note that we only attempt this in the
1709          stat-available path.  This assumes that the
1710          move-copy workaround will only be helpful on
1711          platforms that implement apr_stat. */
1712       SVN_ERR(reown_file(path, pool));
1713       status = apr_file_perms_set(path_apr, perms_to_set);
1714     }
1715
1716   if (!status)
1717     return SVN_NO_ERROR;
1718
1719   if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1720     return SVN_NO_ERROR;
1721   else if (status == APR_ENOTIMPL)
1722     {
1723       /* At least try to set the attributes. */
1724       apr_fileattrs_t attrs = 0;
1725       apr_fileattrs_t attrs_values = 0;
1726
1727       if (change_readwrite)
1728         {
1729           attrs = APR_FILE_ATTR_READONLY;
1730           if (!enable_write)
1731             attrs_values = APR_FILE_ATTR_READONLY;
1732         }
1733       if (change_executable)
1734         {
1735           attrs = APR_FILE_ATTR_EXECUTABLE;
1736           if (executable)
1737             attrs_values = APR_FILE_ATTR_EXECUTABLE;
1738         }
1739       status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
1740     }
1741
1742   return svn_error_wrap_apr(status,
1743                             _("Can't change perms of file '%s'"),
1744                             svn_dirent_local_style(path, pool));
1745 }
1746 #endif /* !WIN32 && !__OS2__ */
1747
1748 #ifdef WIN32
1749 /* This is semantically the same as the APR utf8_to_unicode_path
1750    function, but reimplemented here because APR does not export it. */
1751 svn_error_t*
1752 svn_io__utf8_to_unicode_longpath(const WCHAR **result,
1753                                  const char *source,
1754                                  apr_pool_t *result_pool)
1755 {
1756     /* This is correct, we don't twist the filename if it will
1757      * definitely be shorter than 248 characters.  It merits some
1758      * performance testing to see if this has any effect, but there
1759      * seem to be applications that get confused by the resulting
1760      * Unicode \\?\ style file names, especially if they use argv[0]
1761      * or call the Win32 API functions such as GetModuleName, etc.
1762      * Not every application is prepared to handle such names.
1763      *
1764      * Note also this is shorter than MAX_PATH, as directory paths
1765      * are actually limited to 248 characters.
1766      *
1767      * Note that a utf-8 name can never result in more wide chars
1768      * than the original number of utf-8 narrow chars.
1769      */
1770     const WCHAR *prefix = NULL;
1771     const int srclen = strlen(source);
1772     WCHAR *buffer;
1773
1774     if (srclen > 248)
1775     {
1776         if (svn_ctype_isalpha(source[0]) && source[1] == ':'
1777             && (source[2] == '/' || source[2] == '\\'))
1778         {
1779             /* This is an ordinary absolute path. */
1780             prefix = L"\\\\?\\";
1781         }
1782         else if ((source[0] == '/' || source[0] == '\\')
1783                  && (source[1] == '/' || source[1] == '\\')
1784                  && source[2] != '?')
1785         {
1786             /* This is a UNC path */
1787             source += 2;        /* Skip the leading slashes */
1788             prefix = L"\\\\?\\UNC\\";
1789         }
1790     }
1791
1792     SVN_ERR(svn_utf__win32_utf8_to_utf16(&(const WCHAR*)buffer, source,
1793                                          prefix, result_pool));
1794
1795     /* Convert slashes to backslashes because the \\?\ path format
1796        does not allow backslashes as path separators. */
1797     *result = buffer;
1798     for (; *buffer; ++buffer)
1799     {
1800         if (*buffer == '/')
1801             *buffer = '\\';
1802     }
1803     return SVN_NO_ERROR;
1804 }
1805
1806 /* This is semantically the same as the APR unicode_to_utf8_path
1807    function, but reimplemented here because APR does not export it. */
1808 static svn_error_t *
1809 io_unicode_to_utf8_path(const char **result,
1810                         const WCHAR *source,
1811                         apr_pool_t *result_pool)
1812 {
1813     const char *utf8_buffer;
1814     char *buffer;
1815
1816     SVN_ERR(svn_utf__win32_utf16_to_utf8(&utf8_buffer, source,
1817                                          NULL, result_pool));
1818     if (!*utf8_buffer)
1819       {
1820         *result = utf8_buffer;
1821         return SVN_NO_ERROR;
1822       }
1823
1824     /* We know that the non-empty buffer returned from the UTF-16 to
1825        UTF-8 conversion function is in fact writable. */
1826     buffer = (char*)utf8_buffer;
1827
1828     /* Skip the leading 4 characters if the path begins \\?\, or substitute
1829      * // for the \\?\UNC\ path prefix, allocating the maximum string
1830      * length based on the remaining string, plus the trailing null.
1831      * then transform \\'s back into /'s since the \\?\ form never
1832      * allows '/' path separators, and APR always uses '/'s.
1833      */
1834     if (0 == strncmp(buffer, "\\\\?\\", 4))
1835     {
1836         buffer += 4;
1837         if (0 == strncmp(buffer, "UNC\\", 4))
1838         {
1839             buffer += 2;
1840             *buffer = '/';
1841         }
1842     }
1843
1844     *result = buffer;
1845     for (; *buffer; ++buffer)
1846     {
1847         if (*buffer == '\\')
1848             *buffer = '/';
1849     }
1850     return SVN_NO_ERROR;
1851 }
1852
1853 static svn_error_t *
1854 io_win_file_attrs_set(const char *fname,
1855                       DWORD attributes,
1856                       DWORD attr_mask,
1857                       apr_pool_t *pool)
1858 {
1859     /* this is an implementation of apr_file_attrs_set() but one
1860        that uses the proper Windows attributes instead of the apr
1861        attributes. This way, we can apply any Windows file and
1862        folder attributes even if apr doesn't implement them */
1863     DWORD flags;
1864     const WCHAR *wfname;
1865
1866     SVN_ERR(svn_io__utf8_to_unicode_longpath(&wfname, fname, pool));
1867
1868     flags = GetFileAttributesW(wfname);
1869     if (flags == 0xFFFFFFFF)
1870         return svn_error_wrap_apr(apr_get_os_error(),
1871                                   _("Can't get attributes of file '%s'"),
1872                                   svn_dirent_local_style(fname, pool));
1873
1874     flags &= ~attr_mask;
1875     flags |= (attributes & attr_mask);
1876
1877     if (!SetFileAttributesW(wfname, flags))
1878         return svn_error_wrap_apr(apr_get_os_error(),
1879                                   _("Can't set attributes of file '%s'"),
1880                                   svn_dirent_local_style(fname, pool));
1881
1882     return SVN_NO_ERROR;;
1883 }
1884
1885 static svn_error_t *win_init_dynamic_imports(void *baton, apr_pool_t *pool)
1886 {
1887   HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
1888
1889   if (kernel32)
1890     {
1891       get_final_path_name_by_handle_proc = (GETFINALPATHNAMEBYHANDLE)
1892         GetProcAddress(kernel32, "GetFinalPathNameByHandleW");
1893
1894       set_file_information_by_handle_proc = (SetFileInformationByHandle_t)
1895         GetProcAddress(kernel32, "SetFileInformationByHandle");
1896     }
1897
1898   return SVN_NO_ERROR;
1899 }
1900
1901 static svn_error_t * io_win_read_link(svn_string_t **dest,
1902                                       const char *path,
1903                                       apr_pool_t *pool)
1904 {
1905     SVN_ERR(svn_atomic__init_once(&win_dynamic_imports_state,
1906                                   win_init_dynamic_imports, NULL, pool));
1907
1908     if (get_final_path_name_by_handle_proc)
1909       {
1910         DWORD rv;
1911         apr_status_t status;
1912         apr_file_t *file;
1913         apr_os_file_t filehand;
1914         WCHAR wdest[APR_PATH_MAX];
1915         const char *data;
1916
1917         /* reserve one char for terminating zero. */
1918         DWORD wdest_len = sizeof(wdest)/sizeof(wdest[0]) - 1;
1919
1920         status = apr_file_open(&file, path, APR_OPENINFO, APR_OS_DEFAULT, pool);
1921
1922         if (status)
1923           return svn_error_wrap_apr(status,
1924                                     _("Can't read contents of link"));
1925
1926         apr_os_file_get(&filehand, file);
1927
1928         rv = get_final_path_name_by_handle_proc(
1929                filehand, wdest, wdest_len,
1930                FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
1931
1932         /* Save error code. */
1933         status = apr_get_os_error();
1934
1935         /* Close file/directory handle in any case. */
1936         apr_file_close(file);
1937
1938         /* GetFinaPathNameByHandleW returns number of characters copied to
1939          * output buffer. Returns zero on error. Returns required buffer size
1940          * if supplied buffer is not enough. */
1941         if (rv > wdest_len || rv == 0)
1942           {
1943             return svn_error_wrap_apr(status,
1944                                       _("Can't read contents of link"));
1945           }
1946
1947         /* GetFinaPathNameByHandleW doesn't add terminating NUL. */
1948         wdest[rv] = 0;
1949         SVN_ERR(io_unicode_to_utf8_path(&data, wdest, pool));
1950
1951         /* The result is already in the correct pool, so avoid copying
1952            it to create the string. */
1953         *dest = svn_string_create_empty(pool);
1954         if (*data)
1955           {
1956             (*dest)->data = data;
1957             (*dest)->len = strlen(data);
1958           }
1959
1960         return SVN_NO_ERROR;
1961       }
1962     else
1963       {
1964         return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1965                                 _("Symbolic links are not supported on this "
1966                                 "platform"));
1967       }
1968 }
1969
1970 /* Wrapper around Windows API function SetFileInformationByHandle() that
1971  * returns APR status instead of boolean flag. */
1972 static apr_status_t
1973 win32_set_file_information_by_handle(HANDLE hFile,
1974                                      int FileInformationClass,
1975                                      LPVOID lpFileInformation,
1976                                      DWORD dwBufferSize)
1977 {
1978   svn_error_clear(svn_atomic__init_once(&win_dynamic_imports_state,
1979                                         win_init_dynamic_imports,
1980                                         NULL, NULL));
1981
1982   if (!set_file_information_by_handle_proc)
1983     {
1984       return SVN_ERR_UNSUPPORTED_FEATURE;
1985     }
1986
1987   if (!set_file_information_by_handle_proc(hFile, FileInformationClass,
1988                                            lpFileInformation,
1989                                            dwBufferSize))
1990     {
1991       return apr_get_os_error();
1992     }
1993
1994   return APR_SUCCESS;
1995 }
1996
1997 svn_error_t *
1998 svn_io__win_delete_file_on_close(apr_file_t *file,
1999                                  const char *path,
2000                                  apr_pool_t *pool)
2001 {
2002   FILE_DISPOSITION_INFO disposition_info;
2003   HANDLE hFile;
2004   apr_status_t status;
2005
2006   apr_os_file_get(&hFile, file);
2007
2008   disposition_info.DeleteFile = TRUE;
2009
2010   status = win32_set_file_information_by_handle(hFile, FileDispositionInfo,
2011                                                 &disposition_info,
2012                                                 sizeof(disposition_info));
2013
2014   if (status)
2015     {
2016       return svn_error_wrap_apr(status, _("Can't remove file '%s'"),
2017                                 svn_dirent_local_style(path, pool));
2018     }
2019
2020   return SVN_NO_ERROR;
2021 }
2022
2023 svn_error_t *
2024 svn_io__win_rename_open_file(apr_file_t *file,
2025                              const char *from_path,
2026                              const char *to_path,
2027                              apr_pool_t *pool)
2028 {
2029   WCHAR *w_final_abspath;
2030   size_t path_len;
2031   size_t rename_size;
2032   FILE_RENAME_INFO *rename_info;
2033   HANDLE hFile;
2034   apr_status_t status;
2035
2036   apr_os_file_get(&hFile, file);
2037
2038   SVN_ERR(svn_io__utf8_to_unicode_longpath(
2039             &w_final_abspath, svn_dirent_local_style(to_path,pool),
2040             pool));
2041
2042   path_len = wcslen(w_final_abspath);
2043   rename_size = sizeof(*rename_info) + sizeof(WCHAR) * path_len;
2044
2045   /* The rename info struct doesn't need hacks for long paths,
2046      so no ugly escaping calls here */
2047   rename_info = apr_pcalloc(pool, rename_size);
2048   rename_info->ReplaceIfExists = TRUE;
2049   rename_info->FileNameLength = path_len;
2050   memcpy(rename_info->FileName, w_final_abspath, path_len * sizeof(WCHAR));
2051
2052   status = win32_set_file_information_by_handle(hFile, FileRenameInfo,
2053                                                 rename_info,
2054                                                 rename_size);
2055
2056   if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
2057     {
2058       /* Set the destination file writable because Windows will not allow
2059          us to rename when final_abspath is read-only. */
2060       SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
2061
2062       status = win32_set_file_information_by_handle(hFile,
2063                                                     FileRenameInfo,
2064                                                     rename_info,
2065                                                     rename_size);
2066    }
2067
2068   /* Windows returns Vista+ client accessing network share stored on Windows
2069      Server 2003 returns ERROR_ACCESS_DENIED. The same happens when Vista+
2070      client access Windows Server 2008 with disabled SMBv2 protocol.
2071
2072      So return SVN_ERR_UNSUPPORTED_FEATURE in this case like we do when
2073      SetFileInformationByHandle() is not available and let caller to
2074      handle it.
2075
2076      See "Access denied error on checkout-commit after updating to 1.9.X"
2077      discussion on dev@s.a.o:
2078      http://svn.haxx.se/dev/archive-2015-09/0054.shtml */
2079   if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED))
2080     {
2081       status = SVN_ERR_UNSUPPORTED_FEATURE;
2082     }
2083
2084   if (status)
2085     {
2086       return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
2087                                 svn_dirent_local_style(from_path, pool),
2088                                 svn_dirent_local_style(to_path, pool));
2089     }
2090
2091   return SVN_NO_ERROR;
2092 }
2093
2094 #endif /* WIN32 */
2095
2096 svn_error_t *
2097 svn_io_set_file_read_write_carefully(const char *path,
2098                                      svn_boolean_t enable_write,
2099                                      svn_boolean_t ignore_enoent,
2100                                      apr_pool_t *pool)
2101 {
2102   if (enable_write)
2103     return svn_io_set_file_read_write(path, ignore_enoent, pool);
2104   return svn_io_set_file_read_only(path, ignore_enoent, pool);
2105 }
2106
2107 svn_error_t *
2108 svn_io_set_file_read_only(const char *path,
2109                           svn_boolean_t ignore_enoent,
2110                           apr_pool_t *pool)
2111 {
2112   /* On Windows and OS/2, just set the file attributes -- on unix call
2113      our internal function which attempts to honor the umask. */
2114 #if !defined(WIN32) && !defined(__OS2__)
2115   return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE,
2116                            ignore_enoent, pool);
2117 #else
2118   apr_status_t status;
2119   const char *path_apr;
2120
2121   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2122
2123   status = apr_file_attrs_set(path_apr,
2124                               APR_FILE_ATTR_READONLY,
2125                               APR_FILE_ATTR_READONLY,
2126                               pool);
2127
2128   if (status && status != APR_ENOTIMPL)
2129     if (!(ignore_enoent && (APR_STATUS_IS_ENOENT(status)
2130                             || SVN__APR_STATUS_IS_ENOTDIR(status))))
2131       return svn_error_wrap_apr(status,
2132                                 _("Can't set file '%s' read-only"),
2133                                 svn_dirent_local_style(path, pool));
2134
2135   return SVN_NO_ERROR;
2136 #endif
2137 }
2138
2139
2140 svn_error_t *
2141 svn_io_set_file_read_write(const char *path,
2142                            svn_boolean_t ignore_enoent,
2143                            apr_pool_t *pool)
2144 {
2145   /* On Windows and OS/2, just set the file attributes -- on unix call
2146      our internal function which attempts to honor the umask. */
2147 #if !defined(WIN32) && !defined(__OS2__)
2148   return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE,
2149                            ignore_enoent, pool);
2150 #else
2151   apr_status_t status;
2152   const char *path_apr;
2153
2154   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2155
2156   status = apr_file_attrs_set(path_apr,
2157                               0,
2158                               APR_FILE_ATTR_READONLY,
2159                               pool);
2160
2161   if (status && status != APR_ENOTIMPL)
2162     if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
2163       return svn_error_wrap_apr(status,
2164                                 _("Can't set file '%s' read-write"),
2165                                 svn_dirent_local_style(path, pool));
2166
2167   return SVN_NO_ERROR;
2168 #endif
2169 }
2170
2171 svn_error_t *
2172 svn_io_set_file_executable(const char *path,
2173                            svn_boolean_t executable,
2174                            svn_boolean_t ignore_enoent,
2175                            apr_pool_t *pool)
2176 {
2177   /* On Windows and OS/2, just exit -- on unix call our internal function
2178   which attempts to honor the umask. */
2179 #if (!defined(WIN32) && !defined(__OS2__))
2180   return io_set_file_perms(path, FALSE, FALSE, TRUE, executable,
2181                            ignore_enoent, pool);
2182 #else
2183   return SVN_NO_ERROR;
2184 #endif
2185 }
2186
2187
2188 svn_error_t *
2189 svn_io__is_finfo_read_only(svn_boolean_t *read_only,
2190                            apr_finfo_t *file_info,
2191                            apr_pool_t *pool)
2192 {
2193 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2194   apr_status_t apr_err;
2195   apr_uid_t uid;
2196   apr_gid_t gid;
2197
2198   *read_only = FALSE;
2199
2200   apr_err = apr_uid_current(&uid, &gid, pool);
2201
2202   if (apr_err)
2203     return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
2204
2205   /* Check write bit for current user. */
2206   if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
2207     *read_only = !(file_info->protection & APR_UWRITE);
2208
2209   else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
2210     *read_only = !(file_info->protection & APR_GWRITE);
2211
2212   else
2213     *read_only = !(file_info->protection & APR_WWRITE);
2214
2215 #else  /* WIN32 || __OS2__ || !APR_HAS_USER */
2216   *read_only = (file_info->protection & APR_FREADONLY);
2217 #endif
2218
2219   return SVN_NO_ERROR;
2220 }
2221
2222 svn_error_t *
2223 svn_io__is_finfo_executable(svn_boolean_t *executable,
2224                             apr_finfo_t *file_info,
2225                             apr_pool_t *pool)
2226 {
2227 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2228   apr_status_t apr_err;
2229   apr_uid_t uid;
2230   apr_gid_t gid;
2231
2232   *executable = FALSE;
2233
2234   apr_err = apr_uid_current(&uid, &gid, pool);
2235
2236   if (apr_err)
2237     return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
2238
2239   /* Check executable bit for current user. */
2240   if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
2241     *executable = (file_info->protection & APR_UEXECUTE);
2242
2243   else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
2244     *executable = (file_info->protection & APR_GEXECUTE);
2245
2246   else
2247     *executable = (file_info->protection & APR_WEXECUTE);
2248
2249 #else  /* WIN32 || __OS2__ || !APR_HAS_USER */
2250   *executable = FALSE;
2251 #endif
2252
2253   return SVN_NO_ERROR;
2254 }
2255
2256 svn_error_t *
2257 svn_io_is_file_executable(svn_boolean_t *executable,
2258                           const char *path,
2259                           apr_pool_t *pool)
2260 {
2261 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2262   apr_finfo_t file_info;
2263
2264   SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
2265                       pool));
2266   SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
2267
2268 #else  /* WIN32 || __OS2__ || !APR_HAS_USER */
2269   *executable = FALSE;
2270 #endif
2271
2272   return SVN_NO_ERROR;
2273 }
2274
2275 \f
2276 /*** File locking. ***/
2277 #if !defined(WIN32) && !defined(__OS2__)
2278 /* Clear all outstanding locks on ARG, an open apr_file_t *. */
2279 static apr_status_t
2280 file_clear_locks(void *arg)
2281 {
2282   apr_status_t apr_err;
2283   apr_file_t *f = arg;
2284
2285   /* Remove locks. */
2286   apr_err = apr_file_unlock(f);
2287   if (apr_err)
2288     return apr_err;
2289
2290   return 0;
2291 }
2292 #endif
2293
2294 svn_error_t *
2295 svn_io_lock_open_file(apr_file_t *lockfile_handle,
2296                       svn_boolean_t exclusive,
2297                       svn_boolean_t nonblocking,
2298                       apr_pool_t *pool)
2299 {
2300   int locktype = APR_FLOCK_SHARED;
2301   apr_status_t apr_err;
2302   const char *fname;
2303
2304   if (exclusive)
2305     locktype = APR_FLOCK_EXCLUSIVE;
2306   if (nonblocking)
2307     locktype |= APR_FLOCK_NONBLOCK;
2308
2309   /* We need this only in case of an error but this is cheap to get -
2310    * so we do it here for clarity. */
2311   apr_err = apr_file_name_get(&fname, lockfile_handle);
2312   if (apr_err)
2313     return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2314
2315   /* Get lock on the filehandle. */
2316   apr_err = apr_file_lock(lockfile_handle, locktype);
2317
2318   /* In deployments with two or more multithreaded servers running on
2319      the same system serving two or more fsfs repositories it is
2320      possible for a deadlock to occur when getting a write lock on
2321      db/txn-current-lock:
2322
2323      Process 1                         Process 2
2324      ---------                         ---------
2325      thread 1: get lock in repos A
2326                                        thread 1: get lock in repos B
2327                                        thread 2: block getting lock in repos A
2328      thread 2: try to get lock in B *** deadlock ***
2329
2330      Retry for a while for the deadlock to clear. */
2331   FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
2332
2333   if (apr_err)
2334     {
2335       switch (locktype & APR_FLOCK_TYPEMASK)
2336         {
2337         case APR_FLOCK_SHARED:
2338           return svn_error_wrap_apr(apr_err,
2339                                     _("Can't get shared lock on file '%s'"),
2340                                     try_utf8_from_internal_style(fname, pool));
2341         case APR_FLOCK_EXCLUSIVE:
2342           return svn_error_wrap_apr(apr_err,
2343                                     _("Can't get exclusive lock on file '%s'"),
2344                                     try_utf8_from_internal_style(fname, pool));
2345         default:
2346           SVN_ERR_MALFUNCTION();
2347         }
2348     }
2349
2350 /* On Windows and OS/2 file locks are automatically released when
2351    the file handle closes */
2352 #if !defined(WIN32) && !defined(__OS2__)
2353   apr_pool_cleanup_register(pool, lockfile_handle,
2354                             file_clear_locks,
2355                             apr_pool_cleanup_null);
2356 #endif
2357
2358   return SVN_NO_ERROR;
2359 }
2360
2361 svn_error_t *
2362 svn_io_unlock_open_file(apr_file_t *lockfile_handle,
2363                         apr_pool_t *pool)
2364 {
2365   const char *fname;
2366   apr_status_t apr_err;
2367
2368   /* We need this only in case of an error but this is cheap to get -
2369    * so we do it here for clarity. */
2370   apr_err = apr_file_name_get(&fname, lockfile_handle);
2371   if (apr_err)
2372     return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2373
2374   /* The actual unlock attempt. */
2375   apr_err = apr_file_unlock(lockfile_handle);
2376   if (apr_err)
2377     return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
2378                               try_utf8_from_internal_style(fname, pool));
2379
2380 /* On Windows and OS/2 file locks are automatically released when
2381    the file handle closes */
2382 #if !defined(WIN32) && !defined(__OS2__)
2383   apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks);
2384 #endif
2385
2386   return SVN_NO_ERROR;
2387 }
2388
2389 svn_error_t *
2390 svn_io_file_lock2(const char *lock_file,
2391                   svn_boolean_t exclusive,
2392                   svn_boolean_t nonblocking,
2393                   apr_pool_t *pool)
2394 {
2395   int locktype = APR_FLOCK_SHARED;
2396   apr_file_t *lockfile_handle;
2397   apr_int32_t flags;
2398
2399   if (exclusive)
2400     locktype = APR_FLOCK_EXCLUSIVE;
2401
2402   flags = APR_READ;
2403   if (locktype == APR_FLOCK_EXCLUSIVE)
2404     flags |= APR_WRITE;
2405
2406   /* locktype is never read after this block, so we don't need to bother
2407      setting it.  If that were to ever change, uncomment the following
2408      block.
2409   if (nonblocking)
2410     locktype |= APR_FLOCK_NONBLOCK;
2411   */
2412
2413   SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
2414                            APR_OS_DEFAULT,
2415                            pool));
2416
2417   /* Get lock on the filehandle. */
2418   return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
2419 }
2420
2421 svn_error_t *
2422 svn_io__file_lock_autocreate(const char *lock_file,
2423                              apr_pool_t *pool)
2424 {
2425   svn_error_t *err
2426     = svn_io_file_lock2(lock_file, TRUE, FALSE, pool);
2427   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
2428     {
2429       /* No lock file?  No big deal; these are just empty files anyway.
2430          Create it and try again. */
2431       svn_error_clear(err);
2432
2433       /* This file creation is racy.
2434          We don't care as long as file gets created at all. */
2435       err = svn_io_file_create_empty(lock_file, pool);
2436       if (err && APR_STATUS_IS_EEXIST(err->apr_err))
2437         {
2438           svn_error_clear(err);
2439           err = NULL;
2440         }
2441
2442       /* Finally, lock the file - if it exists */
2443       if (!err)
2444         err = svn_io_file_lock2(lock_file, TRUE, FALSE, pool);
2445     }
2446
2447   return svn_error_trace(err);
2448 }
2449
2450
2451 \f
2452 /* Data consistency/coherency operations. */
2453
2454 svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
2455                                        apr_pool_t *pool)
2456 {
2457   apr_os_file_t filehand;
2458
2459   /* ### In apr 1.4+ we could delegate most of this function to
2460          apr_file_sync(). The only major difference is that this doesn't
2461          contain the retry loop for EINTR on linux. */
2462
2463   /* First make sure that any user-space buffered data is flushed. */
2464   SVN_ERR(svn_io_file_flush(file, pool));
2465
2466   apr_os_file_get(&filehand, file);
2467
2468   /* Call the operating system specific function to actually force the
2469      data to disk. */
2470   {
2471 #ifdef WIN32
2472
2473     if (! FlushFileBuffers(filehand))
2474         return svn_error_wrap_apr(apr_get_os_error(),
2475                                   _("Can't flush file to disk"));
2476
2477 #else
2478       int rv;
2479
2480       do {
2481 #ifdef F_FULLFSYNC
2482         rv = fcntl(filehand, F_FULLFSYNC, 0);
2483 #else
2484         rv = fsync(filehand);
2485 #endif
2486       } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
2487
2488       /* If the file is in a memory filesystem, fsync() may return
2489          EINVAL.  Presumably the user knows the risks, and we can just
2490          ignore the error. */
2491       if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
2492         return SVN_NO_ERROR;
2493
2494       if (rv == -1)
2495         return svn_error_wrap_apr(apr_get_os_error(),
2496                                   _("Can't flush file to disk"));
2497
2498 #endif
2499   }
2500   return SVN_NO_ERROR;
2501 }
2502
2503
2504 \f
2505 /* TODO write test for these two functions, then refactor. */
2506
2507 /* Set RESULT to an svn_stringbuf_t containing the contents of FILE.
2508    FILENAME is the FILE's on-disk APR-safe name, or NULL if that name
2509    isn't known.  If CHECK_SIZE is TRUE, the function will attempt to
2510    first stat() the file to determine it's size before sucking its
2511    contents into the stringbuf.  (Doing so can prevent unnecessary
2512    memory usage, an unwanted side effect of the stringbuf growth and
2513    reallocation mechanism.)  */
2514 static svn_error_t *
2515 stringbuf_from_aprfile(svn_stringbuf_t **result,
2516                        const char *filename,
2517                        apr_file_t *file,
2518                        svn_boolean_t check_size,
2519                        apr_pool_t *pool)
2520 {
2521   apr_size_t len;
2522   svn_error_t *err;
2523   svn_stringbuf_t *res = NULL;
2524   apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE;
2525   char *buf;
2526
2527   /* If our caller wants us to check the size of the file for
2528      efficient memory handling, we'll try to do so. */
2529   if (check_size)
2530     {
2531       apr_finfo_t finfo = { 0 };
2532
2533       /* In some cases we get size 0 and no error for non files,
2534           so we also check for the name. (= cached in apr_file_t) */
2535       if (! apr_file_info_get(&finfo, APR_FINFO_SIZE, file) && finfo.fname)
2536         {
2537           /* we've got the file length. Now, read it in one go. */
2538           svn_boolean_t eof;
2539           res_initial_len = (apr_size_t)finfo.size;
2540           res = svn_stringbuf_create_ensure(res_initial_len, pool);
2541           SVN_ERR(svn_io_file_read_full2(file, res->data,
2542                                          res_initial_len, &res->len,
2543                                          &eof, pool));
2544           res->data[res->len] = 0;
2545
2546           *result = res;
2547           return SVN_NO_ERROR;
2548         }
2549     }
2550
2551   /* XXX: We should check the incoming data for being of type binary. */
2552   buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
2553   res = svn_stringbuf_create_ensure(res_initial_len, pool);
2554
2555   /* apr_file_read will not return data and eof in the same call. So this loop
2556    * is safe from missing read data.  */
2557   len = SVN__STREAM_CHUNK_SIZE;
2558   err = svn_io_file_read(file, buf, &len, pool);
2559   while (! err)
2560     {
2561       svn_stringbuf_appendbytes(res, buf, len);
2562       len = SVN__STREAM_CHUNK_SIZE;
2563       err = svn_io_file_read(file, buf, &len, pool);
2564     }
2565
2566   /* Having read all the data we *expect* EOF */
2567   if (err && !APR_STATUS_IS_EOF(err->apr_err))
2568     return svn_error_trace(err);
2569   svn_error_clear(err);
2570
2571   *result = res;
2572   return SVN_NO_ERROR;
2573 }
2574
2575 svn_error_t *
2576 svn_stringbuf_from_file2(svn_stringbuf_t **result,
2577                          const char *filename,
2578                          apr_pool_t *pool)
2579 {
2580   apr_file_t *f;
2581
2582   if (filename[0] == '-' && filename[1] == '\0')
2583     {
2584       apr_status_t apr_err;
2585       if ((apr_err = apr_file_open_stdin(&f, pool)))
2586         return svn_error_wrap_apr(apr_err, _("Can't open stdin"));
2587       SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool));
2588     }
2589   else
2590     {
2591       SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool));
2592       SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool));
2593     }
2594   return svn_io_file_close(f, pool);
2595 }
2596
2597
2598 svn_error_t *
2599 svn_stringbuf_from_file(svn_stringbuf_t **result,
2600                         const char *filename,
2601                         apr_pool_t *pool)
2602 {
2603   if (filename[0] == '-' && filename[1] == '\0')
2604     return svn_error_create
2605         (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2606          _("Reading from stdin is disallowed"));
2607   return svn_stringbuf_from_file2(result, filename, pool);
2608 }
2609
2610 svn_error_t *
2611 svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
2612                            apr_file_t *file,
2613                            apr_pool_t *pool)
2614 {
2615   return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
2616 }
2617
2618
2619 \f
2620 /* Deletion. */
2621
2622 svn_error_t *
2623 svn_io_remove_file2(const char *path,
2624                     svn_boolean_t ignore_enoent,
2625                     apr_pool_t *scratch_pool)
2626 {
2627   apr_status_t apr_err;
2628   const char *path_apr;
2629
2630   SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
2631
2632   apr_err = apr_file_remove(path_apr, scratch_pool);
2633
2634 #ifdef WIN32
2635   /* If the target is read only NTFS reports EACCESS and FAT/FAT32
2636      reports EEXIST */
2637   if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
2638     {
2639       /* Set the destination file writable because Windows will not
2640          allow us to delete when path is read-only */
2641       SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool));
2642       apr_err = apr_file_remove(path_apr, scratch_pool);
2643
2644       if (!apr_err)
2645         return SVN_NO_ERROR;
2646     }
2647
2648   /* Check to make sure we aren't trying to delete a directory */
2649   if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED)
2650       || apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
2651     {
2652       apr_finfo_t finfo;
2653
2654       if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
2655           && finfo.filetype == APR_REG)
2656         {
2657           WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr, scratch_pool));
2658         }
2659     }
2660
2661   /* Just return the delete error */
2662 #endif
2663
2664   if (!apr_err)
2665     {
2666       return SVN_NO_ERROR;
2667     }
2668   else if (ignore_enoent && (APR_STATUS_IS_ENOENT(apr_err)
2669                              || SVN__APR_STATUS_IS_ENOTDIR(apr_err)))
2670     {
2671       return SVN_NO_ERROR;
2672     }
2673   else
2674     {
2675       return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
2676                                 svn_dirent_local_style(path, scratch_pool));
2677     }
2678 }
2679
2680
2681 svn_error_t *
2682 svn_io_remove_dir(const char *path, apr_pool_t *pool)
2683 {
2684   return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
2685 }
2686
2687 /*
2688  Mac OS X has a bug where if you're reading the contents of a
2689  directory via readdir in a loop, and you remove one of the entries in
2690  the directory and the directory has 338 or more files in it you will
2691  skip over some of the entries in the directory.  Needless to say,
2692  this causes problems if you are using this kind of loop inside a
2693  function that is recursively deleting a directory, because when you
2694  get around to removing the directory it will still have something in
2695  it. A similar problem has been observed in other BSDs. This bug has
2696  since been fixed. See http://www.vnode.ch/fixing_seekdir for details.
2697
2698  The workaround is to delete the files only _after_ the initial
2699  directory scan.  A previous workaround involving rewinddir is
2700  problematic on Win32 and some NFS clients, notably NetBSD.
2701
2702  See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
2703  http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
2704 */
2705
2706 /* Neither windows nor unix allows us to delete a non-empty
2707    directory.
2708
2709    This is a function to perform the equivalent of 'rm -rf'. */
2710 svn_error_t *
2711 svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
2712                    svn_cancel_func_t cancel_func, void *cancel_baton,
2713                    apr_pool_t *pool)
2714 {
2715   svn_error_t *err;
2716   apr_pool_t *subpool;
2717   apr_hash_t *dirents;
2718   apr_hash_index_t *hi;
2719
2720   /* Check for pending cancellation request.
2721      If we need to bail out, do so early. */
2722
2723   if (cancel_func)
2724     SVN_ERR((*cancel_func)(cancel_baton));
2725
2726   subpool = svn_pool_create(pool);
2727
2728   err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
2729   if (err)
2730     {
2731       /* if the directory doesn't exist, our mission is accomplished */
2732       if (ignore_enoent && (APR_STATUS_IS_ENOENT(err->apr_err)
2733                             || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2734         {
2735           svn_error_clear(err);
2736           return SVN_NO_ERROR;
2737         }
2738       return svn_error_trace(err);
2739     }
2740
2741   for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
2742     {
2743       const char *name = apr_hash_this_key(hi);
2744       const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
2745       const char *fullpath;
2746
2747       fullpath = svn_dirent_join(path, name, subpool);
2748       if (dirent->kind == svn_node_dir)
2749         {
2750           /* Don't check for cancellation, the callee will immediately do so */
2751           SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func,
2752                                      cancel_baton, subpool));
2753         }
2754       else
2755         {
2756           if (cancel_func)
2757             SVN_ERR((*cancel_func)(cancel_baton));
2758
2759           err = svn_io_remove_file2(fullpath, FALSE, subpool);
2760           if (err)
2761             return svn_error_createf
2762               (err->apr_err, err, _("Can't remove '%s'"),
2763                svn_dirent_local_style(fullpath, subpool));
2764         }
2765     }
2766
2767   svn_pool_destroy(subpool);
2768
2769   return svn_io_dir_remove_nonrecursive(path, pool);
2770 }
2771
2772 svn_error_t *
2773 svn_io_get_dir_filenames(apr_hash_t **dirents,
2774                          const char *path,
2775                          apr_pool_t *pool)
2776 {
2777   return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
2778                                              pool, pool));
2779 }
2780
2781 svn_io_dirent2_t *
2782 svn_io_dirent2_create(apr_pool_t *result_pool)
2783 {
2784   svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
2785
2786   /*dirent->kind = svn_node_none;
2787   dirent->special = FALSE;*/
2788   dirent->filesize = SVN_INVALID_FILESIZE;
2789   /*dirent->mtime = 0;*/
2790
2791   return dirent;
2792 }
2793
2794 svn_io_dirent2_t *
2795 svn_io_dirent2_dup(const svn_io_dirent2_t *item,
2796                    apr_pool_t *result_pool)
2797 {
2798   return apr_pmemdup(result_pool,
2799                      item,
2800                      sizeof(*item));
2801 }
2802
2803 svn_error_t *
2804 svn_io_get_dirents3(apr_hash_t **dirents,
2805                     const char *path,
2806                     svn_boolean_t only_check_type,
2807                     apr_pool_t *result_pool,
2808                     apr_pool_t *scratch_pool)
2809 {
2810   apr_status_t status;
2811   apr_dir_t *this_dir;
2812   apr_finfo_t this_entry;
2813   apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
2814
2815   if (!only_check_type)
2816     flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
2817
2818   *dirents = apr_hash_make(result_pool);
2819
2820   SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
2821
2822   for (status = apr_dir_read(&this_entry, flags, this_dir);
2823        status == APR_SUCCESS;
2824        status = apr_dir_read(&this_entry, flags, this_dir))
2825     {
2826       if ((this_entry.name[0] == '.')
2827           && ((this_entry.name[1] == '\0')
2828               || ((this_entry.name[1] == '.')
2829                   && (this_entry.name[2] == '\0'))))
2830         {
2831           continue;
2832         }
2833       else
2834         {
2835           const char *name;
2836           svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
2837
2838           SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
2839
2840           map_apr_finfo_to_node_kind(&(dirent->kind),
2841                                      &(dirent->special),
2842                                      &this_entry);
2843
2844           if (!only_check_type)
2845             {
2846               dirent->filesize = this_entry.size;
2847               dirent->mtime = this_entry.mtime;
2848             }
2849
2850           svn_hash_sets(*dirents, name, dirent);
2851         }
2852     }
2853
2854   if (! (APR_STATUS_IS_ENOENT(status)))
2855     return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
2856                               svn_dirent_local_style(path, scratch_pool));
2857
2858   status = apr_dir_close(this_dir);
2859   if (status)
2860     return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
2861                               svn_dirent_local_style(path, scratch_pool));
2862
2863   return SVN_NO_ERROR;
2864 }
2865
2866 svn_error_t *
2867 svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
2868                     const char *path,
2869                     svn_boolean_t verify_truename,
2870                     svn_boolean_t ignore_enoent,
2871                     apr_pool_t *result_pool,
2872                     apr_pool_t *scratch_pool)
2873 {
2874   apr_finfo_t finfo;
2875   svn_io_dirent2_t *dirent;
2876   svn_error_t *err;
2877   apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
2878                        | APR_FINFO_SIZE | APR_FINFO_MTIME;
2879
2880 #if defined(WIN32) || defined(__OS2__)
2881   if (verify_truename)
2882     wanted |= APR_FINFO_NAME;
2883 #endif
2884
2885   err = svn_io_stat(&finfo, path, wanted, scratch_pool);
2886
2887   if (err && ignore_enoent &&
2888       (APR_STATUS_IS_ENOENT(err->apr_err)
2889        || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2890     {
2891       svn_error_clear(err);
2892       dirent = svn_io_dirent2_create(result_pool);
2893       SVN_ERR_ASSERT(dirent->kind == svn_node_none);
2894
2895       *dirent_p = dirent;
2896       return SVN_NO_ERROR;
2897     }
2898   SVN_ERR(err);
2899
2900 #if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
2901   if (verify_truename)
2902     {
2903       const char *requested_name = svn_dirent_basename(path, NULL);
2904
2905       if (requested_name[0] == '\0')
2906         {
2907           /* No parent directory. No need to stat/verify */
2908         }
2909 #if defined(WIN32) || defined(__OS2__)
2910       else if (finfo.name)
2911         {
2912           const char *name_on_disk;
2913           SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
2914                                      scratch_pool));
2915
2916           if (strcmp(name_on_disk, requested_name) /* != 0 */)
2917             {
2918               if (ignore_enoent)
2919                 {
2920                   *dirent_p = svn_io_dirent2_create(result_pool);
2921                   return SVN_NO_ERROR;
2922                 }
2923               else
2924                 return svn_error_createf(APR_ENOENT, NULL,
2925                           _("Path '%s' not found, case obstructed by '%s'"),
2926                           svn_dirent_local_style(path, scratch_pool),
2927                           name_on_disk);
2928             }
2929         }
2930 #elif defined(DARWIN)
2931       /* Currently apr doesn't set finfo.name on DARWIN, returning
2932                    APR_INCOMPLETE.
2933          ### Can we optimize this in another way? */
2934       else
2935         {
2936           apr_hash_t *dirents;
2937
2938           err = svn_io_get_dirents3(&dirents,
2939                                     svn_dirent_dirname(path, scratch_pool),
2940                                     TRUE /* only_check_type */,
2941                                     scratch_pool, scratch_pool);
2942
2943           if (err && ignore_enoent
2944               && (APR_STATUS_IS_ENOENT(err->apr_err)
2945                   || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2946             {
2947               svn_error_clear(err);
2948
2949               *dirent_p = svn_io_dirent2_create(result_pool);
2950               return SVN_NO_ERROR;
2951             }
2952           else
2953             SVN_ERR(err);
2954
2955           if (! svn_hash_gets(dirents, requested_name))
2956             {
2957               if (ignore_enoent)
2958                 {
2959                   *dirent_p = svn_io_dirent2_create(result_pool);
2960                   return SVN_NO_ERROR;
2961                 }
2962               else
2963                 return svn_error_createf(APR_ENOENT, NULL,
2964                           _("Path '%s' not found"),
2965                           svn_dirent_local_style(path, scratch_pool));
2966             }
2967         }
2968 #endif
2969     }
2970 #endif
2971
2972   dirent = svn_io_dirent2_create(result_pool);
2973   map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
2974
2975   dirent->filesize = finfo.size;
2976   dirent->mtime = finfo.mtime;
2977
2978   *dirent_p = dirent;
2979
2980   return SVN_NO_ERROR;
2981 }
2982
2983 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2984 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2985
2986 /* Handle an error from the child process (before command execution) by
2987    printing DESC and the error string corresponding to STATUS to stderr. */
2988 static void
2989 handle_child_process_error(apr_pool_t *pool, apr_status_t status,
2990                            const char *desc)
2991 {
2992   char errbuf[256];
2993   apr_file_t *errfile;
2994   void *p;
2995
2996   /* We can't do anything if we get an error here, so just return. */
2997   if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool))
2998     return;
2999   errfile = p;
3000
3001   if (errfile)
3002     /* What we get from APR is in native encoding. */
3003     apr_file_printf(errfile, "%s: %s",
3004                     desc, apr_strerror(status, errbuf,
3005                                        sizeof(errbuf)));
3006 }
3007
3008
3009 svn_error_t *
3010 svn_io_start_cmd3(apr_proc_t *cmd_proc,
3011                   const char *path,
3012                   const char *cmd,
3013                   const char *const *args,
3014                   const char *const *env,
3015                   svn_boolean_t inherit,
3016                   svn_boolean_t infile_pipe,
3017                   apr_file_t *infile,
3018                   svn_boolean_t outfile_pipe,
3019                   apr_file_t *outfile,
3020                   svn_boolean_t errfile_pipe,
3021                   apr_file_t *errfile,
3022                   apr_pool_t *pool)
3023 {
3024   apr_status_t apr_err;
3025   apr_procattr_t *cmdproc_attr;
3026   int num_args;
3027   const char **args_native;
3028   const char *cmd_apr;
3029
3030   SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
3031   SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
3032   SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
3033
3034   /* Create the process attributes. */
3035   apr_err = apr_procattr_create(&cmdproc_attr, pool);
3036   if (apr_err)
3037     return svn_error_wrap_apr(apr_err,
3038                               _("Can't create process '%s' attributes"),
3039                               cmd);
3040
3041   /* Make sure we invoke cmd directly, not through a shell. */
3042   apr_err = apr_procattr_cmdtype_set(cmdproc_attr,
3043                                      inherit ? APR_PROGRAM_PATH : APR_PROGRAM);
3044   if (apr_err)
3045     return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
3046                               cmd);
3047
3048   /* Set the process's working directory. */
3049   if (path)
3050     {
3051       const char *path_apr;
3052
3053       /* APR doesn't like our canonical path format for current directory */
3054       if (path[0] == '\0')
3055         path = ".";
3056
3057       SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
3058       apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
3059       if (apr_err)
3060         return svn_error_wrap_apr(apr_err,
3061                                   _("Can't set process '%s' directory"),
3062                                   cmd);
3063     }
3064
3065   /* Use requested inputs and outputs.
3066
3067      ### Unfortunately each of these apr functions creates a pipe and then
3068      overwrites the pipe file descriptor with the descriptor we pass
3069      in. The pipes can then never be closed. This is an APR bug. */
3070   if (infile)
3071     {
3072       apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
3073       if (apr_err)
3074         return svn_error_wrap_apr(apr_err,
3075                                   _("Can't set process '%s' child input"),
3076                                   cmd);
3077     }
3078   if (outfile)
3079     {
3080       apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
3081       if (apr_err)
3082         return svn_error_wrap_apr(apr_err,
3083                                   _("Can't set process '%s' child outfile"),
3084                                   cmd);
3085     }
3086   if (errfile)
3087     {
3088       apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
3089       if (apr_err)
3090         return svn_error_wrap_apr(apr_err,
3091                                   _("Can't set process '%s' child errfile"),
3092                                   cmd);
3093     }
3094
3095   /* Forward request for pipes to APR. */
3096   if (infile_pipe || outfile_pipe || errfile_pipe)
3097     {
3098       apr_err = apr_procattr_io_set(cmdproc_attr,
3099                                     infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
3100                                     outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
3101                                     errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE);
3102
3103       if (apr_err)
3104         return svn_error_wrap_apr(apr_err,
3105                                   _("Can't set process '%s' stdio pipes"),
3106                                   cmd);
3107     }
3108
3109   /* Have the child print any problems executing its program to errfile. */
3110   apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
3111   if (apr_err)
3112     return svn_error_wrap_apr(apr_err,
3113                               _("Can't set process '%s' child errfile for "
3114                                 "error handler"),
3115                               cmd);
3116   apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
3117                                          handle_child_process_error);
3118   if (apr_err)
3119     return svn_error_wrap_apr(apr_err,
3120                               _("Can't set process '%s' error handler"),
3121                               cmd);
3122
3123   /* Convert cmd and args from UTF-8 */
3124   SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool));
3125   for (num_args = 0; args[num_args]; num_args++)
3126     ;
3127   args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
3128   args_native[num_args] = NULL;
3129   while (num_args--)
3130     {
3131       /* ### Well, it turns out that on APR on Windows expects all
3132              program args to be in UTF-8. Callers of svn_io_run_cmd
3133              should be aware of that. */
3134       SVN_ERR(cstring_from_utf8(&args_native[num_args],
3135                                 args[num_args], pool));
3136     }
3137
3138
3139   /* Start the cmd command. */
3140   apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
3141                             inherit ? NULL : env, cmdproc_attr, pool);
3142   if (apr_err)
3143     return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
3144
3145   return SVN_NO_ERROR;
3146 }
3147
3148 #undef ERRFILE_KEY
3149
3150 svn_error_t *
3151 svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
3152                     const char *cmd,
3153                     int *exitcode,
3154                     apr_exit_why_e *exitwhy,
3155                     apr_pool_t *pool)
3156 {
3157   apr_status_t apr_err;
3158   apr_exit_why_e exitwhy_val;
3159   int exitcode_val;
3160
3161   /* The Win32 apr_proc_wait doesn't set this... */
3162   exitwhy_val = APR_PROC_EXIT;
3163
3164   /* Wait for the cmd command to finish. */
3165   apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT);
3166   if (!APR_STATUS_IS_CHILD_DONE(apr_err))
3167     return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"),
3168                               cmd);
3169
3170   if (exitwhy)
3171     *exitwhy = exitwhy_val;
3172   else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)
3173            && APR_PROC_CHECK_CORE_DUMP(exitwhy_val))
3174     return svn_error_createf
3175       (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3176        _("Process '%s' failed (signal %d, core dumped)"),
3177        cmd, exitcode_val);
3178   else if (APR_PROC_CHECK_SIGNALED(exitwhy_val))
3179     return svn_error_createf
3180       (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3181        _("Process '%s' failed (signal %d)"),
3182        cmd, exitcode_val);
3183   else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
3184     /* Don't really know what happened here. */
3185     return svn_error_createf
3186       (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3187        _("Process '%s' failed (exitwhy %d, exitcode %d)"),
3188        cmd, exitwhy_val, exitcode_val);
3189
3190   if (exitcode)
3191     *exitcode = exitcode_val;
3192   else if (exitcode_val != 0)
3193     return svn_error_createf
3194       (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3195        _("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
3196
3197   return SVN_NO_ERROR;
3198 }
3199
3200
3201 svn_error_t *
3202 svn_io_run_cmd(const char *path,
3203                const char *cmd,
3204                const char *const *args,
3205                int *exitcode,
3206                apr_exit_why_e *exitwhy,
3207                svn_boolean_t inherit,
3208                apr_file_t *infile,
3209                apr_file_t *outfile,
3210                apr_file_t *errfile,
3211                apr_pool_t *pool)
3212 {
3213   apr_proc_t cmd_proc;
3214
3215   SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
3216                             FALSE, infile, FALSE, outfile, FALSE, errfile,
3217                             pool));
3218
3219   return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
3220 }
3221
3222
3223 svn_error_t *
3224 svn_io_run_diff2(const char *dir,
3225                  const char *const *user_args,
3226                  int num_user_args,
3227                  const char *label1,
3228                  const char *label2,
3229                  const char *from,
3230                  const char *to,
3231                  int *pexitcode,
3232                  apr_file_t *outfile,
3233                  apr_file_t *errfile,
3234                  const char *diff_cmd,
3235                  apr_pool_t *pool)
3236 {
3237   const char **args;
3238   int i;
3239   int exitcode;
3240   int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
3241   apr_pool_t *subpool = svn_pool_create(pool);
3242
3243   if (pexitcode == NULL)
3244     pexitcode = &exitcode;
3245
3246   if (user_args != NULL)
3247     nargs += num_user_args;
3248   else
3249     nargs += 1; /* -u */
3250
3251   if (label1 != NULL)
3252     nargs += 2; /* the -L and the label itself */
3253   if (label2 != NULL)
3254     nargs += 2; /* the -L and the label itself */
3255
3256   args = apr_palloc(subpool, nargs * sizeof(char *));
3257
3258   i = 0;
3259   args[i++] = diff_cmd;
3260
3261   if (user_args != NULL)
3262     {
3263       int j;
3264       for (j = 0; j < num_user_args; ++j)
3265         args[i++] = user_args[j];
3266     }
3267   else
3268     args[i++] = "-u"; /* assume -u if the user didn't give us any args */
3269
3270   if (label1 != NULL)
3271     {
3272       args[i++] = "-L";
3273       args[i++] = label1;
3274     }
3275   if (label2 != NULL)
3276     {
3277       args[i++] = "-L";
3278       args[i++] = label2;
3279     }
3280
3281   args[i++] = svn_dirent_local_style(from, subpool);
3282   args[i++] = svn_dirent_local_style(to, subpool);
3283   args[i++] = NULL;
3284
3285   SVN_ERR_ASSERT(i == nargs);
3286
3287   SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
3288                          NULL, outfile, errfile, subpool));
3289
3290   /* The man page for (GNU) diff describes the return value as:
3291
3292        "An exit status of 0 means no differences were found, 1 means
3293         some differences were found, and 2 means trouble."
3294
3295      A return value of 2 typically occurs when diff cannot read its input
3296      or write to its output, but in any case we probably ought to return an
3297      error for anything other than 0 or 1 as the output is likely to be
3298      corrupt.
3299    */
3300   if (*pexitcode != 0 && *pexitcode != 1)
3301     return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3302                              _("'%s' returned %d"),
3303                              svn_dirent_local_style(diff_cmd, pool),
3304                              *pexitcode);
3305
3306   svn_pool_destroy(subpool);
3307
3308   return SVN_NO_ERROR;
3309 }
3310
3311
3312 svn_error_t *
3313 svn_io_run_diff3_3(int *exitcode,
3314                    const char *dir,
3315                    const char *mine,
3316                    const char *older,
3317                    const char *yours,
3318                    const char *mine_label,
3319                    const char *older_label,
3320                    const char *yours_label,
3321                    apr_file_t *merged,
3322                    const char *diff3_cmd,
3323                    const apr_array_header_t *user_args,
3324                    apr_pool_t *pool)
3325 {
3326   const char **args = apr_palloc(pool,
3327                                  sizeof(char*) * (13
3328                                                   + (user_args
3329                                                      ? user_args->nelts
3330                                                      : 1)));
3331 #ifndef NDEBUG
3332   int nargs = 12;
3333 #endif
3334   int i = 0;
3335
3336   /* Labels fall back to sensible defaults if not specified. */
3337   if (mine_label == NULL)
3338     mine_label = ".working";
3339   if (older_label == NULL)
3340     older_label = ".old";
3341   if (yours_label == NULL)
3342     yours_label = ".new";
3343
3344   /* Set up diff3 command line. */
3345   args[i++] = diff3_cmd;
3346   if (user_args)
3347     {
3348       int j;
3349       for (j = 0; j < user_args->nelts; ++j)
3350         args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
3351 #ifndef NDEBUG
3352       nargs += user_args->nelts;
3353 #endif
3354     }
3355   else
3356     {
3357       args[i++] = "-E";             /* We tried "-A" here, but that caused
3358                                        overlapping identical changes to
3359                                        conflict.  See issue #682. */
3360 #ifndef NDEBUG
3361       ++nargs;
3362 #endif
3363     }
3364   args[i++] = "-m";
3365   args[i++] = "-L";
3366   args[i++] = mine_label;
3367   args[i++] = "-L";
3368   args[i++] = older_label;      /* note:  this label is ignored if
3369                                    using 2-part markers, which is the
3370                                    case with "-E". */
3371   args[i++] = "-L";
3372   args[i++] = yours_label;
3373 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
3374   {
3375     svn_boolean_t has_arg;
3376
3377     /* ### FIXME: we really shouldn't be reading the config here;
3378        instead, the necessary bits should be passed in by the caller.
3379        But should we add another parameter to this function, when the
3380        whole external diff3 thing might eventually go away?  */
3381     apr_hash_t *config;
3382     svn_config_t *cfg;
3383
3384     SVN_ERR(svn_config_get_config(&config, pool));
3385     cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
3386     SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS,
3387                                 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG,
3388                                 TRUE));
3389     if (has_arg)
3390       {
3391         const char *diff_cmd, *diff_utf8;
3392         svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
3393                        SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF);
3394         SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool));
3395         args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8,
3396                                 SVN_VA_NULL);
3397 #ifndef NDEBUG
3398         ++nargs;
3399 #endif
3400       }
3401   }
3402 #endif
3403   args[i++] = svn_dirent_local_style(mine, pool);
3404   args[i++] = svn_dirent_local_style(older, pool);
3405   args[i++] = svn_dirent_local_style(yours, pool);
3406   args[i++] = NULL;
3407 #ifndef NDEBUG
3408   SVN_ERR_ASSERT(i == nargs);
3409 #endif
3410
3411   /* Run diff3, output the merged text into the scratch file. */
3412   SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
3413                          exitcode, NULL,
3414                          TRUE, /* keep environment */
3415                          NULL, merged, NULL,
3416                          pool));
3417
3418   /* According to the diff3 docs, a '0' means the merge was clean, and
3419      '1' means conflict markers were found.  Anything else is real
3420      error. */
3421   if ((*exitcode != 0) && (*exitcode != 1))
3422     return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3423                              _("Error running '%s':  exitcode was %d, "
3424                                "args were:"
3425                                "\nin directory '%s', basenames:\n%s\n%s\n%s"),
3426                              svn_dirent_local_style(diff3_cmd, pool),
3427                              *exitcode,
3428                              svn_dirent_local_style(dir, pool),
3429                              /* Don't call svn_path_local_style() on
3430                                 the basenames.  We don't want them to
3431                                 be absolute, and we don't need the
3432                                 separator conversion. */
3433                              mine, older, yours);
3434
3435   return SVN_NO_ERROR;
3436 }
3437
3438
3439 /* Canonicalize a string for hashing.  Modifies KEY in place. */
3440 static APR_INLINE char *
3441 fileext_tolower(char *key)
3442 {
3443   register char *p;
3444   for (p = key; *p != 0; ++p)
3445     *p = (char)apr_tolower(*p);
3446   return key;
3447 }
3448
3449
3450 svn_error_t *
3451 svn_io_parse_mimetypes_file(apr_hash_t **type_map,
3452                             const char *mimetypes_file,
3453                             apr_pool_t *pool)
3454 {
3455   svn_error_t *err = SVN_NO_ERROR;
3456   apr_hash_t *types = apr_hash_make(pool);
3457   svn_boolean_t eof = FALSE;
3458   svn_stringbuf_t *buf;
3459   apr_pool_t *subpool = svn_pool_create(pool);
3460   apr_file_t *types_file;
3461   svn_stream_t *mimetypes_stream;
3462
3463   SVN_ERR(svn_io_file_open(&types_file, mimetypes_file,
3464                            APR_READ, APR_OS_DEFAULT, pool));
3465   mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool);
3466
3467   while (1)
3468     {
3469       apr_array_header_t *tokens;
3470       const char *type;
3471
3472       svn_pool_clear(subpool);
3473
3474       /* Read a line. */
3475       if ((err = svn_stream_readline(mimetypes_stream, &buf,
3476                                      APR_EOL_STR, &eof, subpool)))
3477         break;
3478
3479       /* Only pay attention to non-empty, non-comment lines. */
3480       if (buf->len)
3481         {
3482           int i;
3483
3484           if (buf->data[0] == '#')
3485             continue;
3486
3487           /* Tokenize (into our return pool). */
3488           tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
3489           if (tokens->nelts < 2)
3490             continue;
3491
3492           /* The first token in a multi-token line is the media type.
3493              Subsequent tokens are filename extensions associated with
3494              that media type. */
3495           type = APR_ARRAY_IDX(tokens, 0, const char *);
3496           for (i = 1; i < tokens->nelts; i++)
3497             {
3498               /* We can safely address 'ext' as a non-const string because
3499                * we know svn_cstring_split() allocated it in 'pool' for us. */
3500               char *ext = APR_ARRAY_IDX(tokens, i, char *);
3501               fileext_tolower(ext);
3502               svn_hash_sets(types, ext, type);
3503             }
3504         }
3505       if (eof)
3506         break;
3507     }
3508   svn_pool_destroy(subpool);
3509
3510   /* If there was an error above, close the file (ignoring any error
3511      from *that*) and return the originally error. */
3512   if (err)
3513     {
3514       svn_error_clear(svn_stream_close(mimetypes_stream));
3515       return err;
3516     }
3517
3518   /* Close the stream (which closes the underlying file, too). */
3519   SVN_ERR(svn_stream_close(mimetypes_stream));
3520
3521   *type_map = types;
3522   return SVN_NO_ERROR;
3523 }
3524
3525
3526 svn_error_t *
3527 svn_io_detect_mimetype2(const char **mimetype,
3528                         const char *file,
3529                         apr_hash_t *mimetype_map,
3530                         apr_pool_t *pool)
3531 {
3532   static const char * const generic_binary = "application/octet-stream";
3533
3534   svn_node_kind_t kind;
3535   apr_file_t *fh;
3536   svn_error_t *err;
3537   unsigned char block[1024];
3538   apr_size_t amt_read = sizeof(block);
3539
3540   /* Default return value is NULL. */
3541   *mimetype = NULL;
3542
3543   /* If there is a mimetype_map provided, we'll first try to look up
3544      our file's extension in the map.  Failing that, we'll run the
3545      heuristic. */
3546   if (mimetype_map)
3547     {
3548       const char *type_from_map;
3549       char *path_ext; /* Can point to physical const memory but only when
3550                          svn_path_splitext sets it to "". */
3551       svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
3552       fileext_tolower(path_ext);
3553       if ((type_from_map = svn_hash_gets(mimetype_map, path_ext)))
3554         {
3555           *mimetype = type_from_map;
3556           return SVN_NO_ERROR;
3557         }
3558     }
3559
3560   /* See if this file even exists, and make sure it really is a file. */
3561   SVN_ERR(svn_io_check_path(file, &kind, pool));
3562   if (kind != svn_node_file)
3563     return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
3564                              _("Can't detect MIME type of non-file '%s'"),
3565                              svn_dirent_local_style(file, pool));
3566
3567   SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
3568
3569   /* Read a block of data from FILE. */
3570   err = svn_io_file_read(fh, block, &amt_read, pool);
3571   if (err && ! APR_STATUS_IS_EOF(err->apr_err))
3572     return err;
3573   svn_error_clear(err);
3574
3575   /* Now close the file.  No use keeping it open any more.  */
3576   SVN_ERR(svn_io_file_close(fh, pool));
3577
3578   if (svn_io_is_binary_data(block, amt_read))
3579     *mimetype = generic_binary;
3580
3581   return SVN_NO_ERROR;
3582 }
3583
3584
3585 svn_boolean_t
3586 svn_io_is_binary_data(const void *data, apr_size_t len)
3587 {
3588   const unsigned char *buf = data;
3589
3590   if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
3591     {
3592       /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
3593        * Treat it as plain text. */
3594       return FALSE;
3595     }
3596
3597   /* Right now, this function is going to be really stupid.  It's
3598      going to examine the block of data, and make sure that 15%
3599      of the bytes are such that their value is in the ranges 0x07-0x0D
3600      or 0x20-0x7F, and that none of those bytes is 0x00.  If those
3601      criteria are not met, we're calling it binary.
3602
3603      NOTE:  Originally, I intended to target 85% of the bytes being in
3604      the specified ranges, but I flubbed the condition.  At any rate,
3605      folks aren't complaining, so I'm not sure that it's worth
3606      adjusting this retroactively now.  --cmpilato  */
3607   if (len > 0)
3608     {
3609       apr_size_t i;
3610       apr_size_t binary_count = 0;
3611
3612       /* Run through the data we've read, counting the 'binary-ish'
3613          bytes.  HINT: If we see a 0x00 byte, we'll set our count to its
3614          max and stop reading the file. */
3615       for (i = 0; i < len; i++)
3616         {
3617           if (buf[i] == 0)
3618             {
3619               binary_count = len;
3620               break;
3621             }
3622           if ((buf[i] < 0x07)
3623               || ((buf[i] > 0x0D) && (buf[i] < 0x20))
3624               || (buf[i] > 0x7F))
3625             {
3626               binary_count++;
3627             }
3628         }
3629
3630       return (((binary_count * 1000) / len) > 850);
3631     }
3632
3633   return FALSE;
3634 }
3635
3636
3637 svn_error_t *
3638 svn_io_detect_mimetype(const char **mimetype,
3639                        const char *file,
3640                        apr_pool_t *pool)
3641 {
3642   return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
3643 }
3644
3645
3646 svn_error_t *
3647 svn_io_file_open(apr_file_t **new_file, const char *fname,
3648                  apr_int32_t flag, apr_fileperms_t perm,
3649                  apr_pool_t *pool)
3650 {
3651   const char *fname_apr;
3652   apr_status_t status;
3653
3654   SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3655   status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
3656                      pool);
3657
3658   if (status)
3659     return svn_error_wrap_apr(status, _("Can't open file '%s'"),
3660                               svn_dirent_local_style(fname, pool));
3661   else
3662     return SVN_NO_ERROR;
3663 }
3664
3665
3666 static APR_INLINE svn_error_t *
3667 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
3668                            const char *msg, const char *msg_no_name,
3669                            apr_pool_t *pool)
3670 {
3671   const char *name;
3672   svn_error_t *err;
3673
3674   if (! status)
3675     return SVN_NO_ERROR;
3676
3677   err = svn_io_file_name_get(&name, file, pool);
3678   if (err)
3679     name = NULL;
3680   svn_error_clear(err);
3681
3682   /* ### Issue #3014: Return a specific error for broken pipes,
3683    * ### with a single element in the error chain. */
3684   if (SVN__APR_STATUS_IS_EPIPE(status))
3685     return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
3686
3687   if (name)
3688     return svn_error_wrap_apr(status, _(msg),
3689                               try_utf8_from_internal_style(name, pool));
3690   else
3691     return svn_error_wrap_apr(status, "%s", _(msg_no_name));
3692 }
3693
3694
3695 svn_error_t *
3696 svn_io_file_close(apr_file_t *file, apr_pool_t *pool)
3697 {
3698   return do_io_file_wrapper_cleanup(file, apr_file_close(file),
3699                                     N_("Can't close file '%s'"),
3700                                     N_("Can't close stream"),
3701                                     pool);
3702 }
3703
3704
3705 svn_error_t *
3706 svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
3707 {
3708   return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file),
3709                                     N_("Can't read file '%s'"),
3710                                     N_("Can't read stream"),
3711                                     pool);
3712 }
3713
3714
3715 svn_error_t *
3716 svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
3717 {
3718   return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file),
3719                                     N_("Can't write file '%s'"),
3720                                     N_("Can't write stream"),
3721                                     pool);
3722 }
3723
3724
3725 svn_error_t *
3726 svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
3727                      apr_file_t *file, apr_pool_t *pool)
3728 {
3729   /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3730   wanted &= ~SVN__APR_FINFO_MASK_OUT;
3731
3732   return do_io_file_wrapper_cleanup(
3733              file, apr_file_info_get(finfo, wanted, file),
3734              N_("Can't get attribute information from file '%s'"),
3735              N_("Can't get attribute information from stream"),
3736              pool);
3737 }
3738
3739
3740 svn_error_t *
3741 svn_io_file_read(apr_file_t *file, void *buf,
3742                  apr_size_t *nbytes, apr_pool_t *pool)
3743 {
3744   return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes),
3745                                     N_("Can't read file '%s'"),
3746                                     N_("Can't read stream"),
3747                                     pool);
3748 }
3749
3750
3751 svn_error_t *
3752 svn_io_file_read_full2(apr_file_t *file, void *buf,
3753                        apr_size_t nbytes, apr_size_t *bytes_read,
3754                        svn_boolean_t *hit_eof,
3755                        apr_pool_t *pool)
3756 {
3757   apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
3758   if (hit_eof)
3759     {
3760       if (APR_STATUS_IS_EOF(status))
3761         {
3762           *hit_eof = TRUE;
3763           return SVN_NO_ERROR;
3764         }
3765       else
3766         *hit_eof = FALSE;
3767     }
3768
3769   return do_io_file_wrapper_cleanup(file, status,
3770                                     N_("Can't read file '%s'"),
3771                                     N_("Can't read stream"),
3772                                     pool);
3773 }
3774
3775
3776 svn_error_t *
3777 svn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
3778                  apr_off_t *offset, apr_pool_t *pool)
3779 {
3780   return do_io_file_wrapper_cleanup(
3781              file, apr_file_seek(file, where, offset),
3782              N_("Can't set position pointer in file '%s'"),
3783              N_("Can't set position pointer in stream"),
3784              pool);
3785 }
3786
3787 svn_error_t *
3788 svn_io_file_aligned_seek(apr_file_t *file,
3789                          apr_off_t block_size,
3790                          apr_off_t *buffer_start,
3791                          apr_off_t offset,
3792                          apr_pool_t *scratch_pool)
3793 {
3794   const apr_size_t apr_default_buffer_size = 4096;
3795   apr_size_t file_buffer_size = apr_default_buffer_size;
3796   apr_off_t desired_offset = 0;
3797   apr_off_t current = 0;
3798   apr_off_t aligned_offset = 0;
3799   svn_boolean_t fill_buffer = FALSE;
3800
3801   /* paranoia check: huge blocks on 32 bit machines may cause overflows */
3802   SVN_ERR_ASSERT(block_size == (apr_size_t)block_size);
3803
3804   /* default for invalid block sizes */
3805   if (block_size == 0)
3806     block_size = apr_default_buffer_size;
3807
3808   file_buffer_size = apr_file_buffer_size_get(file);
3809
3810   /* don't try to set a buffer size for non-buffered files! */
3811   if (file_buffer_size == 0)
3812     {
3813       aligned_offset = offset;
3814     }
3815   else if (file_buffer_size != (apr_size_t)block_size)
3816     {
3817       /* FILE has the wrong buffer size. correct it */
3818       char *buffer;
3819       file_buffer_size = (apr_size_t)block_size;
3820       buffer = apr_palloc(apr_file_pool_get(file), file_buffer_size);
3821       apr_file_buffer_set(file, buffer, file_buffer_size);
3822
3823       /* seek to the start of the block and cause APR to read 1 block */
3824       aligned_offset = offset - (offset % block_size);
3825       fill_buffer = TRUE;
3826     }
3827   else
3828     {
3829       aligned_offset = offset - (offset % file_buffer_size);
3830
3831       /* We have no way to determine the block start of an APR file.
3832          Furthermore, we don't want to throw away the current buffer
3833          contents.  Thus, we re-align the buffer only if the CURRENT
3834          offset definitely lies outside the desired, aligned buffer.
3835          This covers the typical case of linear reads getting very
3836          close to OFFSET but reading the previous / following block.
3837
3838          Note that ALIGNED_OFFSET may still be within the current
3839          buffer and no I/O will actually happen in the FILL_BUFFER
3840          section below.
3841        */
3842       SVN_ERR(svn_io_file_seek(file, APR_CUR, &current, scratch_pool));
3843       fill_buffer = aligned_offset + file_buffer_size <= current
3844                  || current <= aligned_offset;
3845     }
3846
3847   if (fill_buffer)
3848     {
3849       char dummy;
3850       apr_status_t status;
3851
3852       /* seek to the start of the block and cause APR to read 1 block */
3853       SVN_ERR(svn_io_file_seek(file, APR_SET, &aligned_offset,
3854                                scratch_pool));
3855       status = apr_file_getc(&dummy, file);
3856
3857       /* read may fail if we seek to or behind EOF.  That's ok then. */
3858       if (status != APR_SUCCESS && !APR_STATUS_IS_EOF(status))
3859         return do_io_file_wrapper_cleanup(file, status,
3860                                           N_("Can't read file '%s'"),
3861                                           N_("Can't read stream"),
3862                                           scratch_pool);
3863     }
3864
3865   /* finally, seek to the OFFSET the caller wants */
3866   desired_offset = offset;
3867   SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, scratch_pool));
3868   if (desired_offset != offset)
3869     return do_io_file_wrapper_cleanup(file, APR_EOF,
3870                                       N_("Can't seek in file '%s'"),
3871                                       N_("Can't seek in stream"),
3872                                       scratch_pool);
3873
3874   /* return the buffer start that we (probably) enforced */
3875   if (buffer_start)
3876     *buffer_start = aligned_offset;
3877
3878   return SVN_NO_ERROR;
3879 }
3880
3881
3882 svn_error_t *
3883 svn_io_file_write(apr_file_t *file, const void *buf,
3884                   apr_size_t *nbytes, apr_pool_t *pool)
3885 {
3886   return svn_error_trace(do_io_file_wrapper_cleanup(
3887      file, apr_file_write(file, buf, nbytes),
3888      N_("Can't write to file '%s'"),
3889      N_("Can't write to stream"),
3890      pool));
3891 }
3892
3893 svn_error_t *
3894 svn_io_file_flush(apr_file_t *file,
3895                   apr_pool_t *scratch_pool)
3896 {
3897   return svn_error_trace(do_io_file_wrapper_cleanup(
3898      file, apr_file_flush(file),
3899      N_("Can't flush file '%s'"),
3900      N_("Can't flush stream"),
3901      scratch_pool));
3902 }
3903
3904 svn_error_t *
3905 svn_io_file_write_full(apr_file_t *file, const void *buf,
3906                        apr_size_t nbytes, apr_size_t *bytes_written,
3907                        apr_pool_t *pool)
3908 {
3909   /* We cannot simply call apr_file_write_full on Win32 as it may fail
3910      for larger values of NBYTES. In that case, we have to emulate the
3911      "_full" part here. Thus, always call apr_file_write directly on
3912      Win32 as this minimizes overhead for small data buffers. */
3913 #ifdef WIN32
3914 #define MAXBUFSIZE 30*1024
3915   apr_size_t bw = nbytes;
3916   apr_size_t to_write = nbytes;
3917
3918   /* try a simple "write everything at once" first */
3919   apr_status_t rv = apr_file_write(file, buf, &bw);
3920   buf = (char *)buf + bw;
3921   to_write -= bw;
3922
3923   /* if the OS cannot handle that, use smaller chunks */
3924   if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
3925       && nbytes > MAXBUFSIZE)
3926     {
3927       do {
3928         bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
3929         rv = apr_file_write(file, buf, &bw);
3930         buf = (char *)buf + bw;
3931         to_write -= bw;
3932       } while (rv == APR_SUCCESS && to_write > 0);
3933     }
3934
3935   /* bytes_written may actually be NULL */
3936   if (bytes_written)
3937     *bytes_written = nbytes - to_write;
3938 #undef MAXBUFSIZE
3939 #else
3940   apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
3941 #endif
3942
3943   return svn_error_trace(do_io_file_wrapper_cleanup(
3944      file, rv,
3945      N_("Can't write to file '%s'"),
3946      N_("Can't write to stream"),
3947      pool));
3948 }
3949
3950
3951 svn_error_t *
3952 svn_io_write_unique(const char **tmp_path,
3953                     const char *dirpath,
3954                     const void *buf,
3955                     apr_size_t nbytes,
3956                     svn_io_file_del_t delete_when,
3957                     apr_pool_t *pool)
3958 {
3959   apr_file_t *new_file;
3960   svn_error_t *err;
3961
3962   SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
3963                                    delete_when, pool, pool));
3964
3965   err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
3966
3967   if (!err)
3968     {
3969       /* svn_io_file_flush_to_disk() can be very expensive, so use the
3970          cheaper standard flush if the file is created as temporary file
3971          anyway */
3972       if (delete_when == svn_io_file_del_none)
3973         err = svn_io_file_flush_to_disk(new_file, pool);
3974       else
3975         err = svn_io_file_flush(new_file, pool);
3976     }
3977
3978   return svn_error_trace(
3979                   svn_error_compose_create(err,
3980                                            svn_io_file_close(new_file, pool)));
3981 }
3982
3983 svn_error_t *
3984 svn_io_write_atomic(const char *final_path,
3985                     const void *buf,
3986                     apr_size_t nbytes,
3987                     const char *copy_perms_path,
3988                     apr_pool_t *scratch_pool)
3989 {
3990   apr_file_t *tmp_file;
3991   const char *tmp_path;
3992   svn_error_t *err;
3993   const char *dirname = svn_dirent_dirname(final_path, scratch_pool);
3994
3995   SVN_ERR(svn_io_open_unique_file3(&tmp_file, &tmp_path, dirname,
3996                                    svn_io_file_del_none,
3997                                    scratch_pool, scratch_pool));
3998
3999   err = svn_io_file_write_full(tmp_file, buf, nbytes, NULL, scratch_pool);
4000
4001   if (!err)
4002     err = svn_io_file_flush_to_disk(tmp_file, scratch_pool);
4003
4004   err = svn_error_compose_create(err,
4005                                  svn_io_file_close(tmp_file, scratch_pool));
4006
4007   if (!err && copy_perms_path)
4008     err = svn_io_copy_perms(copy_perms_path, tmp_path, scratch_pool);
4009
4010   if (!err)
4011     err = svn_io_file_rename(tmp_path, final_path, scratch_pool);
4012
4013   if (err)
4014     {
4015       err = svn_error_compose_create(err,
4016                                      svn_io_remove_file2(tmp_path, TRUE,
4017                                                          scratch_pool));
4018
4019       return svn_error_createf(err->apr_err, err,
4020                                _("Can't write '%s' atomically"),
4021                                svn_dirent_local_style(final_path,
4022                                                       scratch_pool));
4023     }
4024
4025 #ifdef __linux__
4026   {
4027     /* Linux has the unusual feature that fsync() on a file is not
4028        enough to ensure that a file's directory entries have been
4029        flushed to disk; you have to fsync the directory as well.
4030        On other operating systems, we'd only be asking for trouble
4031        by trying to open and fsync a directory. */
4032     apr_file_t *file;
4033
4034     SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
4035                              scratch_pool));
4036     SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool));
4037     SVN_ERR(svn_io_file_close(file, scratch_pool));
4038   }
4039 #endif
4040
4041   return SVN_NO_ERROR;
4042 }
4043
4044 svn_error_t *
4045 svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
4046 {
4047   /* This is a work-around. APR would flush the write buffer
4048      _after_ truncating the file causing now invalid buffered
4049      data to be written behind OFFSET. */
4050   SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
4051                                      N_("Can't flush file '%s'"),
4052                                      N_("Can't flush stream"),
4053                                      pool));
4054
4055   return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset),
4056                                     N_("Can't truncate file '%s'"),
4057                                     N_("Can't truncate stream"),
4058                                     pool);
4059 }
4060
4061
4062 svn_error_t *
4063 svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
4064                         apr_pool_t *pool)
4065 {
4066   /* variables */
4067   apr_size_t total_read = 0;
4068   svn_boolean_t eof = FALSE;
4069   const char *name;
4070   svn_error_t *err;
4071   apr_size_t buf_size = *limit;
4072
4073   while (buf_size > 0)
4074     {
4075       /* read a fair chunk of data at once. But don't get too ambitious
4076        * as that would result in too much waste. Also make sure we can
4077        * put a NUL after the last byte read.
4078        */
4079       apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
4080       apr_size_t bytes_read = 0;
4081       char *eol;
4082
4083       if (to_read == 0)
4084         break;
4085
4086       /* read data block (or just a part of it) */
4087       SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
4088                                      &bytes_read, &eof, pool));
4089
4090       /* look or a newline char */
4091       buf[bytes_read] = 0;
4092       eol = strchr(buf, '\n');
4093       if (eol)
4094         {
4095           apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
4096
4097           *eol = 0;
4098           *limit = total_read + (eol - buf);
4099
4100           /* correct the file pointer:
4101            * appear as though we just had read the newline char
4102            */
4103           SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
4104
4105           return SVN_NO_ERROR;
4106         }
4107       else if (eof)
4108         {
4109           /* no EOL found but we hit the end of the file.
4110            * Generate a nice EOF error object and return it.
4111            */
4112           char dummy;
4113           SVN_ERR(svn_io_file_getc(&dummy, file, pool));
4114         }
4115
4116       /* next data chunk */
4117       buf_size -= bytes_read;
4118       buf += bytes_read;
4119       total_read += bytes_read;
4120     }
4121
4122   /* buffer limit has been exceeded without finding the EOL */
4123   err = svn_io_file_name_get(&name, file, pool);
4124   if (err)
4125     name = NULL;
4126   svn_error_clear(err);
4127
4128   if (name)
4129     return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
4130                              _("Can't read length line in file '%s'"),
4131                              svn_dirent_local_style(name, pool));
4132   else
4133     return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
4134                             _("Can't read length line in stream"));
4135 }
4136
4137
4138 svn_error_t *
4139 svn_io_stat(apr_finfo_t *finfo, const char *fname,
4140             apr_int32_t wanted, apr_pool_t *pool)
4141 {
4142   apr_status_t status;
4143   const char *fname_apr;
4144
4145   /* APR doesn't like "" directories */
4146   if (fname[0] == '\0')
4147     fname = ".";
4148
4149   SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
4150
4151   /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
4152   wanted &= ~SVN__APR_FINFO_MASK_OUT;
4153
4154   status = apr_stat(finfo, fname_apr, wanted, pool);
4155   if (status)
4156     return svn_error_wrap_apr(status, _("Can't stat '%s'"),
4157                               svn_dirent_local_style(fname, pool));
4158
4159   return SVN_NO_ERROR;
4160 }
4161
4162
4163 svn_error_t *
4164 svn_io_file_rename(const char *from_path, const char *to_path,
4165                    apr_pool_t *pool)
4166 {
4167   apr_status_t status = APR_SUCCESS;
4168   const char *from_path_apr, *to_path_apr;
4169
4170   SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
4171   SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
4172
4173   status = apr_file_rename(from_path_apr, to_path_apr, pool);
4174
4175 #if defined(WIN32) || defined(__OS2__)
4176   /* If the target file is read only NTFS reports EACCESS and
4177      FAT/FAT32 reports EEXIST */
4178   if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
4179     {
4180       /* Set the destination file writable because Windows will not
4181          allow us to rename when to_path is read-only, but will
4182          allow renaming when from_path is read only. */
4183       SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
4184
4185       status = apr_file_rename(from_path_apr, to_path_apr, pool);
4186     }
4187   WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool));
4188 #endif /* WIN32 || __OS2__ */
4189
4190   if (status)
4191     return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
4192                               svn_dirent_local_style(from_path, pool),
4193                               svn_dirent_local_style(to_path, pool));
4194
4195   return SVN_NO_ERROR;
4196 }
4197
4198
4199 svn_error_t *
4200 svn_io_file_move(const char *from_path, const char *to_path,
4201                  apr_pool_t *pool)
4202 {
4203   svn_error_t *err = svn_io_file_rename(from_path, to_path, pool);
4204
4205   if (err && APR_STATUS_IS_EXDEV(err->apr_err))
4206     {
4207       const char *tmp_to_path;
4208
4209       svn_error_clear(err);
4210
4211       SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_to_path,
4212                                        svn_dirent_dirname(to_path, pool),
4213                                        svn_io_file_del_none,
4214                                        pool, pool));
4215
4216       err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool);
4217       if (err)
4218         goto failed_tmp;
4219
4220       err = svn_io_file_rename(tmp_to_path, to_path, pool);
4221       if (err)
4222         goto failed_tmp;
4223
4224       err = svn_io_remove_file2(from_path, FALSE, pool);
4225       if (! err)
4226         return SVN_NO_ERROR;
4227
4228       svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool));
4229
4230       return err;
4231
4232     failed_tmp:
4233       svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool));
4234     }
4235
4236   return err;
4237 }
4238
4239 /* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
4240    HIDDEN determines if the hidden attribute
4241    should be set on the newly created directory. */
4242 static svn_error_t *
4243 dir_make(const char *path, apr_fileperms_t perm,
4244          svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
4245 {
4246   apr_status_t status;
4247   const char *path_apr;
4248
4249   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4250
4251   /* APR doesn't like "" directories */
4252   if (path_apr[0] == '\0')
4253     path_apr = ".";
4254
4255 #if (APR_OS_DEFAULT & APR_WSTICKY)
4256   /* The APR shipped with httpd 2.0.50 contains a bug where
4257      APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
4258      There is a special case for file creation, but not directory
4259      creation, so directories wind up getting created with the sticky
4260      bit set.  (There is no such thing as a setuid directory, and the
4261      setgid bit is apparently ignored at mkdir() time.)  If we detect
4262      this problem, work around it by unsetting those bits if we are
4263      passed APR_OS_DEFAULT. */
4264   if (perm == APR_OS_DEFAULT)
4265     perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY);
4266 #endif
4267
4268   status = apr_dir_make(path_apr, perm, pool);
4269
4270 #ifdef WIN32
4271   /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a
4272      permanent error */
4273   if (status == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
4274     WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
4275 #endif
4276
4277   if (status)
4278     return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
4279                               svn_dirent_local_style(path, pool));
4280
4281 #ifdef APR_FILE_ATTR_HIDDEN
4282   if (hidden)
4283     {
4284 #ifndef WIN32
4285       status = apr_file_attrs_set(path_apr,
4286                                   APR_FILE_ATTR_HIDDEN,
4287                                   APR_FILE_ATTR_HIDDEN,
4288                                   pool);
4289       if (status)
4290         return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
4291                                   svn_dirent_local_style(path, pool));
4292 #else
4293     /* on Windows, use our wrapper so we can also set the
4294        FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */
4295       svn_error_t *err =
4296           io_win_file_attrs_set(path_apr,
4297                                 FILE_ATTRIBUTE_HIDDEN |
4298                                 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
4299                                 FILE_ATTRIBUTE_HIDDEN |
4300                                 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
4301                                 pool);
4302       if (err)
4303         return svn_error_createf(err->apr_err, err,
4304                                  _("Can't hide directory '%s'"),
4305                                  svn_dirent_local_style(path, pool));
4306 #endif /* WIN32 */
4307     }
4308 #endif /* APR_FILE_ATTR_HIDDEN */
4309
4310 /* Windows does not implement sgid. Skip here because retrieving
4311    the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented
4312    to be 'incredibly expensive'. */
4313 #ifndef WIN32
4314   if (sgid)
4315     {
4316       apr_finfo_t finfo;
4317
4318       /* Per our contract, don't do error-checking.  Some filesystems
4319        * don't support the sgid bit, and that's okay. */
4320       status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool);
4321
4322       if (!status)
4323         apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
4324     }
4325 #endif
4326
4327   return SVN_NO_ERROR;
4328 }
4329
4330 svn_error_t *
4331 svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
4332 {
4333   return dir_make(path, perm, FALSE, FALSE, pool);
4334 }
4335
4336 svn_error_t *
4337 svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
4338                        apr_pool_t *pool)
4339 {
4340   return dir_make(path, perm, TRUE, FALSE, pool);
4341 }
4342
4343 svn_error_t *
4344 svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
4345                      apr_pool_t *pool)
4346 {
4347   return dir_make(path, perm, FALSE, TRUE, pool);
4348 }
4349
4350
4351 svn_error_t *
4352 svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
4353 {
4354   apr_status_t status;
4355   const char *dirname_apr;
4356
4357   /* APR doesn't like "" directories */
4358   if (dirname[0] == '\0')
4359     dirname = ".";
4360
4361   SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4362
4363   status = apr_dir_open(new_dir, dirname_apr, pool);
4364   if (status)
4365     return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
4366                               svn_dirent_local_style(dirname, pool));
4367
4368   return SVN_NO_ERROR;
4369 }
4370
4371 svn_error_t *
4372 svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
4373 {
4374   apr_status_t status;
4375   const char *dirname_apr;
4376
4377   SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4378
4379   status = apr_dir_remove(dirname_apr, pool);
4380
4381 #ifdef WIN32
4382   {
4383     svn_boolean_t retry = TRUE;
4384
4385     if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY)
4386       {
4387         apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
4388
4389         if (APR_STATUS_IS_ENOTEMPTY(empty_status))
4390           retry = FALSE;
4391       }
4392
4393     if (retry)
4394       {
4395         WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
4396       }
4397   }
4398 #endif
4399   if (status)
4400     return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
4401                               svn_dirent_local_style(dirname, pool));
4402
4403   return SVN_NO_ERROR;
4404 }
4405
4406
4407 svn_error_t *
4408 svn_io_dir_read(apr_finfo_t *finfo,
4409                 apr_int32_t wanted,
4410                 apr_dir_t *thedir,
4411                 apr_pool_t *pool)
4412 {
4413   apr_status_t status;
4414
4415   status = apr_dir_read(finfo, wanted, thedir);
4416
4417   if (status)
4418     return svn_error_wrap_apr(status, _("Can't read directory"));
4419
4420   /* It would be nice to use entry_name_to_utf8() below, but can we
4421      get the dir's path out of an apr_dir_t?  I don't see a reliable
4422      way to do it. */
4423
4424   if (finfo->fname)
4425     SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
4426
4427   if (finfo->name)
4428     SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
4429
4430   return SVN_NO_ERROR;
4431 }
4432
4433 svn_error_t *
4434 svn_io_dir_close(apr_dir_t *thedir)
4435 {
4436   apr_status_t apr_err = apr_dir_close(thedir);
4437   if (apr_err)
4438     return svn_error_wrap_apr(apr_err, _("Error closing directory"));
4439
4440   return SVN_NO_ERROR;
4441 }
4442
4443 svn_error_t *
4444 svn_io_dir_walk2(const char *dirname,
4445                  apr_int32_t wanted,
4446                  svn_io_walk_func_t walk_func,
4447                  void *walk_baton,
4448                  apr_pool_t *pool)
4449 {
4450   apr_status_t apr_err;
4451   apr_dir_t *handle;
4452   apr_pool_t *subpool;
4453   const char *dirname_apr;
4454   apr_finfo_t finfo;
4455
4456   wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
4457
4458   /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
4459   wanted &= ~SVN__APR_FINFO_MASK_OUT;
4460
4461   /* The documentation for apr_dir_read used to state that "." and ".."
4462      will be returned as the first two files, but it doesn't
4463      work that way in practice, in particular ext3 on Linux-2.6 doesn't
4464      follow the rules.  For details see
4465      http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
4466
4467      If APR ever does implement "dot-first" then it would be possible to
4468      remove the svn_io_stat and walk_func calls and use the walk_func
4469      inside the loop.
4470
4471      Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
4472      documented to provide it, so we have to do a bit extra. */
4473   SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool));
4474   SVN_ERR(cstring_from_utf8(&finfo.name,
4475                             svn_dirent_basename(dirname, pool),
4476                             pool));
4477   finfo.valid |= APR_FINFO_NAME;
4478   SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
4479
4480   SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4481
4482   /* APR doesn't like "" directories */
4483   if (dirname_apr[0] == '\0')
4484     dirname_apr = ".";
4485
4486   apr_err = apr_dir_open(&handle, dirname_apr, pool);
4487   if (apr_err)
4488     return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
4489                               svn_dirent_local_style(dirname, pool));
4490
4491   /* iteration subpool */
4492   subpool = svn_pool_create(pool);
4493
4494   while (1)
4495     {
4496       const char *name_utf8;
4497       const char *full_path;
4498
4499       svn_pool_clear(subpool);
4500
4501       apr_err = apr_dir_read(&finfo, wanted, handle);
4502       if (APR_STATUS_IS_ENOENT(apr_err))
4503         break;
4504       else if (apr_err)
4505         {
4506           return svn_error_wrap_apr(apr_err,
4507                                     _("Can't read directory entry in '%s'"),
4508                                     svn_dirent_local_style(dirname, pool));
4509         }
4510
4511       if (finfo.filetype == APR_DIR)
4512         {
4513           if (finfo.name[0] == '.'
4514               && (finfo.name[1] == '\0'
4515                   || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
4516             /* skip "." and ".." */
4517             continue;
4518
4519           /* some other directory. recurse. it will be passed to the
4520              callback inside the recursion. */
4521           SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
4522                                      subpool));
4523           full_path = svn_dirent_join(dirname, name_utf8, subpool);
4524           SVN_ERR(svn_io_dir_walk2(full_path,
4525                                    wanted,
4526                                    walk_func,
4527                                    walk_baton,
4528                                    subpool));
4529         }
4530       else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
4531         {
4532           /* some other directory. pass it to the callback. */
4533           SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
4534                                      subpool));
4535           full_path = svn_dirent_join(dirname, name_utf8, subpool);
4536           SVN_ERR((*walk_func)(walk_baton,
4537                                full_path,
4538                                &finfo,
4539                                subpool));
4540         }
4541       /* else:
4542          Some other type of file; skip it for now.  We've reserved the
4543          right to expand our coverage here in the future, though,
4544          without revving this API.
4545       */
4546     }
4547
4548   svn_pool_destroy(subpool);
4549
4550   apr_err = apr_dir_close(handle);
4551   if (apr_err)
4552     return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
4553                               svn_dirent_local_style(dirname, pool));
4554
4555   return SVN_NO_ERROR;
4556 }
4557
4558
4559 \f
4560 /**
4561  * Determine if a directory is empty or not.
4562  * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
4563  * @param path The directory.
4564  * @param pool Used for temporary allocation.
4565  * @remark If path is not a directory, or some other error occurs,
4566  * then return the appropriate apr status code.
4567  *
4568  * (This function is written in APR style, in anticipation of
4569  * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
4570  */
4571 static apr_status_t
4572 dir_is_empty(const char *dir, apr_pool_t *pool)
4573 {
4574   apr_status_t apr_err;
4575   apr_dir_t *dir_handle;
4576   apr_finfo_t finfo;
4577   apr_status_t retval = APR_SUCCESS;
4578
4579   /* APR doesn't like "" directories */
4580   if (dir[0] == '\0')
4581     dir = ".";
4582
4583   apr_err = apr_dir_open(&dir_handle, dir, pool);
4584   if (apr_err != APR_SUCCESS)
4585     return apr_err;
4586
4587   for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle);
4588        apr_err == APR_SUCCESS;
4589        apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle))
4590     {
4591       /* Ignore entries for this dir and its parent, robustly.
4592          (APR promises that they'll come first, so technically
4593          this guard could be moved outside the loop.  But Ryan Bloom
4594          says he doesn't believe it, and I believe him. */
4595       if (! (finfo.name[0] == '.'
4596              && (finfo.name[1] == '\0'
4597                  || (finfo.name[1] == '.' && finfo.name[2] == '\0'))))
4598         {
4599           retval = APR_ENOTEMPTY;
4600           break;
4601         }
4602     }
4603
4604   /* Make sure we broke out of the loop for the right reason. */
4605   if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
4606     return apr_err;
4607
4608   apr_err = apr_dir_close(dir_handle);
4609   if (apr_err != APR_SUCCESS)
4610     return apr_err;
4611
4612   return retval;
4613 }
4614
4615
4616 svn_error_t *
4617 svn_io_dir_empty(svn_boolean_t *is_empty_p,
4618                  const char *path,
4619                  apr_pool_t *pool)
4620 {
4621   apr_status_t status;
4622   const char *path_apr;
4623
4624   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4625
4626   status = dir_is_empty(path_apr, pool);
4627
4628   if (!status)
4629     *is_empty_p = TRUE;
4630   else if (APR_STATUS_IS_ENOTEMPTY(status))
4631     *is_empty_p = FALSE;
4632   else
4633     return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
4634                               svn_dirent_local_style(path, pool));
4635
4636   return SVN_NO_ERROR;
4637 }
4638
4639
4640 \f
4641 /*** Version/format files ***/
4642
4643 svn_error_t *
4644 svn_io_write_version_file(const char *path,
4645                           int version,
4646                           apr_pool_t *pool)
4647 {
4648   const char *path_tmp;
4649   const char *format_contents = apr_psprintf(pool, "%d\n", version);
4650
4651   SVN_ERR_ASSERT(version >= 0);
4652
4653   SVN_ERR(svn_io_write_unique(&path_tmp,
4654                               svn_dirent_dirname(path, pool),
4655                               format_contents, strlen(format_contents),
4656                               svn_io_file_del_none, pool));
4657
4658 #if defined(WIN32) || defined(__OS2__)
4659   /* make the destination writable, but only on Windows, because
4660      Windows does not let us replace read-only files. */
4661   SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
4662 #endif /* WIN32 || __OS2__ */
4663
4664   /* rename the temp file as the real destination */
4665   SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
4666
4667   /* And finally remove the perms to make it read only */
4668   return svn_io_set_file_read_only(path, FALSE, pool);
4669 }
4670
4671
4672 svn_error_t *
4673 svn_io_read_version_file(int *version,
4674                          const char *path,
4675                          apr_pool_t *pool)
4676 {
4677   apr_file_t *format_file;
4678   char buf[80];
4679   apr_size_t len;
4680   svn_error_t *err;
4681
4682   /* Read a chunk of data from PATH */
4683   SVN_ERR(svn_io_file_open(&format_file, path, APR_READ,
4684                            APR_OS_DEFAULT, pool));
4685   len = sizeof(buf);
4686   err = svn_io_file_read(format_file, buf, &len, pool);
4687
4688   /* Close the file. */
4689   SVN_ERR(svn_error_compose_create(err,
4690                                    svn_io_file_close(format_file, pool)));
4691
4692   /* If there was no data in PATH, return an error. */
4693   if (len == 0)
4694     return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
4695                              _("Reading '%s'"),
4696                              svn_dirent_local_style(path, pool));
4697
4698   /* Check that the first line contains only digits. */
4699   {
4700     apr_size_t i;
4701
4702     for (i = 0; i < len; ++i)
4703       {
4704         char c = buf[i];
4705
4706         if (i > 0 && (c == '\r' || c == '\n'))
4707           {
4708             buf[i] = '\0';
4709             break;
4710           }
4711         if (! svn_ctype_isdigit(c))
4712           return svn_error_createf
4713             (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
4714              _("First line of '%s' contains non-digit"),
4715              svn_dirent_local_style(path, pool));
4716       }
4717   }
4718
4719   /* Convert to integer. */
4720   SVN_ERR(svn_cstring_atoi(version, buf));
4721
4722   return SVN_NO_ERROR;
4723 }
4724
4725
4726 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
4727 static svn_error_t *
4728 contents_identical_p(svn_boolean_t *identical_p,
4729                      const char *file1,
4730                      const char *file2,
4731                      apr_pool_t *pool)
4732 {
4733   svn_error_t *err;
4734   apr_size_t bytes_read1, bytes_read2;
4735   char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4736   char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4737   apr_file_t *file1_h;
4738   apr_file_t *file2_h;
4739   svn_boolean_t eof1 = FALSE;
4740   svn_boolean_t eof2 = FALSE;
4741
4742   SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4743                            pool));
4744
4745   err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4746                          pool);
4747
4748   if (err)
4749     return svn_error_trace(
4750                svn_error_compose_create(err,
4751                                         svn_io_file_close(file1_h, pool)));
4752
4753   *identical_p = TRUE;  /* assume TRUE, until disproved below */
4754   while (!err && !eof1 && !eof2)
4755     {
4756       err = svn_io_file_read_full2(file1_h, buf1,
4757                                    SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4758                                    &eof1, pool);
4759       if (err)
4760           break;
4761
4762       err = svn_io_file_read_full2(file2_h, buf2,
4763                                    SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4764                                    &eof2, pool);
4765       if (err)
4766           break;
4767
4768       if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
4769         {
4770           *identical_p = FALSE;
4771           break;
4772         }
4773     }
4774
4775   /* Special case: one file being a prefix of the other and the shorter
4776    * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */
4777   if (!err && (eof1 != eof2))
4778     *identical_p = FALSE;
4779
4780   return svn_error_trace(
4781            svn_error_compose_create(
4782                 err,
4783                 svn_error_compose_create(svn_io_file_close(file1_h, pool),
4784                                          svn_io_file_close(file2_h, pool))));
4785 }
4786
4787
4788
4789 /* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
4790 static svn_error_t *
4791 contents_three_identical_p(svn_boolean_t *identical_p12,
4792                            svn_boolean_t *identical_p23,
4793                            svn_boolean_t *identical_p13,
4794                            const char *file1,
4795                            const char *file2,
4796                            const char *file3,
4797                            apr_pool_t *scratch_pool)
4798 {
4799   svn_error_t *err;
4800   char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4801   char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4802   char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4803   apr_file_t *file1_h;
4804   apr_file_t *file2_h;
4805   apr_file_t *file3_h;
4806   svn_boolean_t eof1 = FALSE;
4807   svn_boolean_t eof2 = FALSE;
4808   svn_boolean_t eof3 = FALSE;
4809
4810   SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4811                            scratch_pool));
4812
4813   err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4814                          scratch_pool);
4815
4816   if (err)
4817     return svn_error_trace(
4818                svn_error_compose_create(err,
4819                                         svn_io_file_close(file1_h, scratch_pool)));
4820
4821   err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
4822                          scratch_pool);
4823
4824   if (err)
4825       return svn_error_trace(
4826                svn_error_compose_create(
4827                     err,
4828                     svn_error_compose_create(svn_io_file_close(file1_h,
4829                                                           scratch_pool),
4830                                              svn_io_file_close(file2_h,
4831                                                           scratch_pool))));
4832
4833   /* assume TRUE, until disproved below */
4834   *identical_p12 = *identical_p23 = *identical_p13 = TRUE;
4835   /* We need to read as long as no error occurs, and as long as one of the
4836    * flags could still change due to a read operation */
4837   while (!err
4838         && ((*identical_p12 && !eof1 && !eof2)
4839             || (*identical_p23 && !eof2 && !eof3)
4840             || (*identical_p13 && !eof1 && !eof3)))
4841     {
4842       apr_size_t bytes_read1, bytes_read2, bytes_read3;
4843       svn_boolean_t read_1, read_2, read_3;
4844
4845       read_1 = read_2 = read_3 = FALSE;
4846
4847       /* As long as a file is not at the end yet, and it is still
4848        * potentially identical to another file, we read the next chunk.*/
4849       if (!eof1 && (*identical_p12 || *identical_p13))
4850         {
4851           err = svn_io_file_read_full2(file1_h, buf1,
4852                                    SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4853                                    &eof1, scratch_pool);
4854           if (err)
4855               break;
4856           read_1 = TRUE;
4857         }
4858
4859       if (!eof2 && (*identical_p12 || *identical_p23))
4860         {
4861           err = svn_io_file_read_full2(file2_h, buf2,
4862                                    SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4863                                    &eof2, scratch_pool);
4864           if (err)
4865               break;
4866           read_2 = TRUE;
4867         }
4868
4869       if (!eof3 && (*identical_p13 || *identical_p23))
4870         {
4871           err = svn_io_file_read_full2(file3_h, buf3,
4872                                    SVN__STREAM_CHUNK_SIZE, &bytes_read3,
4873                                    &eof3, scratch_pool);
4874           if (err)
4875               break;
4876           read_3 = TRUE;
4877         }
4878
4879       /* If the files are still marked identical, and at least one of them
4880        * is not at the end of file, we check whether they differ, and set
4881        * their flag to false then. */
4882       if (*identical_p12
4883           && (read_1 || read_2)
4884           && ((eof1 != eof2)
4885               || (bytes_read1 != bytes_read2)
4886               || memcmp(buf1, buf2, bytes_read1)))
4887         {
4888           *identical_p12 = FALSE;
4889         }
4890
4891       if (*identical_p23
4892           && (read_2 || read_3)
4893           && ((eof2 != eof3)
4894               || (bytes_read2 != bytes_read3)
4895               || memcmp(buf2, buf3, bytes_read2)))
4896         {
4897           *identical_p23 = FALSE;
4898         }
4899
4900       if (*identical_p13
4901           && (read_1 || read_3)
4902           && ((eof1 != eof3)
4903               || (bytes_read1 != bytes_read3)
4904               || memcmp(buf1, buf3, bytes_read3)))
4905         {
4906           *identical_p13 = FALSE;
4907         }
4908     }
4909
4910   return svn_error_trace(
4911            svn_error_compose_create(
4912                 err,
4913                 svn_error_compose_create(
4914                     svn_io_file_close(file1_h, scratch_pool),
4915                     svn_error_compose_create(
4916                         svn_io_file_close(file2_h, scratch_pool),
4917                         svn_io_file_close(file3_h, scratch_pool)))));
4918 }
4919
4920
4921
4922 svn_error_t *
4923 svn_io_files_contents_same_p(svn_boolean_t *same,
4924                              const char *file1,
4925                              const char *file2,
4926                              apr_pool_t *pool)
4927 {
4928   svn_boolean_t q;
4929
4930   SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
4931
4932   if (q)
4933     {
4934       *same = FALSE;
4935       return SVN_NO_ERROR;
4936     }
4937
4938   SVN_ERR(contents_identical_p(&q, file1, file2, pool));
4939
4940   if (q)
4941     *same = TRUE;
4942   else
4943     *same = FALSE;
4944
4945   return SVN_NO_ERROR;
4946 }
4947
4948 svn_error_t *
4949 svn_io_files_contents_three_same_p(svn_boolean_t *same12,
4950                                    svn_boolean_t *same23,
4951                                    svn_boolean_t *same13,
4952                                    const char *file1,
4953                                    const char *file2,
4954                                    const char *file3,
4955                                    apr_pool_t *scratch_pool)
4956 {
4957   svn_boolean_t diff_size12, diff_size23, diff_size13;
4958
4959   SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
4960                                              &diff_size23,
4961                                              &diff_size13,
4962                                              file1,
4963                                              file2,
4964                                              file3,
4965                                              scratch_pool));
4966
4967   if (diff_size12 && diff_size23 && diff_size13)
4968     {
4969       *same12 = *same23 = *same13 = FALSE;
4970     }
4971   else if (diff_size12 && diff_size23)
4972     {
4973       *same12 = *same23 = FALSE;
4974       SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
4975     }
4976   else if (diff_size23 && diff_size13)
4977     {
4978       *same23 = *same13 = FALSE;
4979       SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
4980     }
4981   else if (diff_size12 && diff_size13)
4982     {
4983       *same12 = *same13 = FALSE;
4984       SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
4985     }
4986   else
4987     {
4988       SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
4989       SVN_ERR(contents_three_identical_p(same12, same23, same13,
4990                                          file1, file2, file3,
4991                                          scratch_pool));
4992     }
4993
4994   return SVN_NO_ERROR;
4995 }
4996
4997 #ifdef WIN32
4998 /* Counter value of file_mktemp request (used in a threadsafe way), to make
4999    sure that a single process normally never generates the same tempname
5000    twice */
5001 static volatile apr_uint32_t tempname_counter = 0;
5002 #endif
5003
5004 /* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
5005    Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name.
5006    Perform temporary allocations in SCRATCH_POOL and the result in
5007    RESULT_POOL. */
5008 static svn_error_t *
5009 temp_file_create(apr_file_t **new_file,
5010                  const char **new_file_name,
5011                  const char *directory,
5012                  apr_int32_t flags,
5013                  apr_pool_t *result_pool,
5014                  apr_pool_t *scratch_pool)
5015 {
5016 #ifndef WIN32
5017   const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
5018   const char *templ_apr;
5019   apr_status_t status;
5020
5021   SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
5022
5023   /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
5024          data available in POOL and we need a non-const pointer here,
5025          as apr changes the template to return the new filename. */
5026   status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool);
5027
5028   if (status)
5029     return svn_error_wrap_apr(status, _("Can't create temporary file from "
5030                               "template '%s'"), templ);
5031
5032   /* Translate the returned path back to utf-8 before returning it */
5033   return svn_error_trace(svn_path_cstring_to_utf8(new_file_name,
5034                                                   templ_apr,
5035                                                   result_pool));
5036 #else
5037   /* The Windows implementation of apr_file_mktemp doesn't handle access
5038      denied errors correctly. Therefore we implement our own temp file
5039      creation function here. */
5040
5041   /* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
5042      ### the function we used before. But we try to guess a more unique
5043      ### name before trying if it exists. */
5044
5045   /* Offset by some time value and a unique request nr to make the number
5046      +- unique for both this process and on the computer */
5047   int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter)
5048                + GetCurrentProcessId();
5049   int i;
5050
5051   /* ### Maybe use an iterpool? */
5052   for (i = 0; i <= 99999; i++)
5053     {
5054       apr_uint32_t unique_nr;
5055       const char *unique_name;
5056       const char *unique_name_apr;
5057       apr_file_t *try_file;
5058       apr_status_t apr_err;
5059
5060       /* Generate a number that should be unique for this application and
5061          usually for the entire computer to reduce the number of cycles
5062          through this loop. (A bit of calculation is much cheaper then
5063          disk io) */
5064       unique_nr = baseNr + 3 * i;
5065
5066       unique_name = svn_dirent_join(directory,
5067                                     apr_psprintf(scratch_pool, "svn-%X",
5068                                                  unique_nr),
5069                                     scratch_pool);
5070
5071       SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
5072
5073       apr_err = file_open(&try_file, unique_name_apr, flags,
5074                           APR_OS_DEFAULT, FALSE, scratch_pool);
5075
5076       if (APR_STATUS_IS_EEXIST(apr_err))
5077           continue;
5078       else if (apr_err)
5079         {
5080           /* On Win32, CreateFile fails with an "Access Denied" error
5081              code, rather than "File Already Exists", if the colliding
5082              name belongs to a directory. */
5083
5084           if (APR_STATUS_IS_EACCES(apr_err))
5085             {
5086               apr_finfo_t finfo;
5087               apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
5088                                                 APR_FINFO_TYPE, scratch_pool);
5089
5090               if (!apr_err_2 && finfo.filetype == APR_DIR)
5091                 continue;
5092
5093               apr_err_2 = APR_TO_OS_ERROR(apr_err);
5094
5095               if (apr_err_2 == ERROR_ACCESS_DENIED ||
5096                   apr_err_2 == ERROR_SHARING_VIOLATION)
5097                 {
5098                   /* The file is in use by another process or is hidden;
5099                      create a new name, but don't do this 99999 times in
5100                      case the folder is not writable */
5101                   i += 797;
5102                   continue;
5103                 }
5104
5105               /* Else fall through and return the original error. */
5106             }
5107
5108           return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
5109                                     svn_dirent_local_style(unique_name,
5110                                                            scratch_pool));
5111         }
5112       else
5113         {
5114           /* Move file to the right pool */
5115           apr_err = apr_file_setaside(new_file, try_file, result_pool);
5116
5117           if (apr_err)
5118             return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
5119                                       svn_dirent_local_style(unique_name,
5120                                                              scratch_pool));
5121
5122           *new_file_name = apr_pstrdup(result_pool, unique_name);
5123
5124           return SVN_NO_ERROR;
5125         }
5126     }
5127
5128   return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
5129                            NULL,
5130                            _("Unable to make name in '%s'"),
5131                            svn_dirent_local_style(directory, scratch_pool));
5132 #endif
5133 }
5134
5135 /* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
5136 svn_error_t *
5137 svn_io_file_name_get(const char **filename,
5138                      apr_file_t *file,
5139                      apr_pool_t *pool)
5140 {
5141   const char *fname_apr;
5142   apr_status_t status;
5143
5144   status = apr_file_name_get(&fname_apr, file);
5145   if (status)
5146     return svn_error_wrap_apr(status, _("Can't get file name"));
5147
5148   if (fname_apr)
5149     SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
5150   else
5151     *filename = NULL;
5152
5153   return SVN_NO_ERROR;
5154 }
5155
5156
5157 svn_error_t *
5158 svn_io_open_unique_file3(apr_file_t **file,
5159                          const char **unique_path,
5160                          const char *dirpath,
5161                          svn_io_file_del_t delete_when,
5162                          apr_pool_t *result_pool,
5163                          apr_pool_t *scratch_pool)
5164 {
5165   apr_file_t *tempfile;
5166   const char *tempname;
5167   struct temp_file_cleanup_s *baton = NULL;
5168   apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
5169                        APR_BUFFERED | APR_BINARY);
5170 #if !defined(WIN32) && !defined(__OS2__)
5171   apr_fileperms_t perms;
5172   svn_boolean_t using_system_temp_dir = FALSE;
5173 #endif
5174
5175   SVN_ERR_ASSERT(file || unique_path);
5176   if (file)
5177     *file = NULL;
5178   if (unique_path)
5179     *unique_path = NULL;
5180
5181   if (dirpath == NULL)
5182     {
5183 #if !defined(WIN32) && !defined(__OS2__)
5184       using_system_temp_dir = TRUE;
5185 #endif
5186       SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
5187     }
5188
5189   switch (delete_when)
5190     {
5191       case svn_io_file_del_on_pool_cleanup:
5192         baton = apr_palloc(result_pool, sizeof(*baton));
5193         baton->pool = result_pool;
5194         baton->fname_apr = NULL;
5195
5196         /* Because cleanups are run LIFO, we need to make sure to register
5197            our cleanup before the apr_file_close cleanup:
5198
5199            On Windows, you can't remove an open file.
5200         */
5201         apr_pool_cleanup_register(result_pool, baton,
5202                                   temp_file_plain_cleanup_handler,
5203                                   temp_file_child_cleanup_handler);
5204
5205         break;
5206       case svn_io_file_del_on_close:
5207         flags |= APR_DELONCLOSE;
5208         break;
5209       default:
5210         break;
5211     }
5212
5213   SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
5214                            result_pool, scratch_pool));
5215
5216 #if !defined(WIN32) && !defined(__OS2__)
5217   /* apr_file_mktemp() creates files with mode 0600.
5218    * This is appropriate if we're using a system temp dir since we don't
5219    * want to leak sensitive data into temp files other users can read.
5220    * If we're not using a system temp dir we're probably using the
5221    * .svn/tmp area and it's likely that the tempfile will end up being
5222    * copied or renamed into the working copy.
5223    * This would cause working files having mode 0600 while users might
5224    * expect to see 0644 or 0664. So we tweak perms of the tempfile in this
5225    * case, but only if the umask allows it. */
5226   if (!using_system_temp_dir)
5227     {
5228       svn_error_t *err;
5229
5230       SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
5231       err = file_perms_set2(tempfile, perms, scratch_pool);
5232       if (err)
5233         {
5234           if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
5235               APR_STATUS_IS_ENOTIMPL(err->apr_err))
5236             svn_error_clear(err);
5237           else
5238             {
5239               return svn_error_quick_wrapf(
5240                        err, _("Can't set permissions on '%s'"),
5241                        svn_dirent_local_style(tempname, scratch_pool));
5242             }
5243         }
5244     }
5245 #endif
5246
5247   if (file)
5248     *file = tempfile;
5249   else
5250     SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
5251
5252   if (unique_path)
5253     *unique_path = tempname; /* Was allocated in result_pool */
5254
5255   if (baton)
5256     SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
5257
5258   return SVN_NO_ERROR;
5259 }
5260
5261 svn_error_t *
5262 svn_io_file_readline(apr_file_t *file,
5263                      svn_stringbuf_t **stringbuf,
5264                      const char **eol,
5265                      svn_boolean_t *eof,
5266                      apr_size_t max_len,
5267                      apr_pool_t *result_pool,
5268                      apr_pool_t *scratch_pool)
5269 {
5270   svn_stringbuf_t *str;
5271   const char *eol_str;
5272   apr_size_t numbytes;
5273   char c;
5274   apr_size_t len;
5275   svn_boolean_t found_eof;
5276
5277   str = svn_stringbuf_create_ensure(80, result_pool);
5278
5279   /* Read bytes into STR up to and including, but not storing,
5280    * the next EOL sequence. */
5281   eol_str = NULL;
5282   numbytes = 1;
5283   len = 0;
5284   found_eof = FALSE;
5285   while (!found_eof)
5286     {
5287       if (len < max_len)
5288         SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
5289                                        &found_eof, scratch_pool));
5290       len++;
5291       if (numbytes != 1 || len > max_len)
5292         {
5293           found_eof = TRUE;
5294           break;
5295         }
5296
5297       if (c == '\n')
5298         {
5299           eol_str = "\n";
5300         }
5301       else if (c == '\r')
5302         {
5303           eol_str = "\r";
5304
5305           if (!found_eof && len < max_len)
5306             {
5307               apr_off_t pos;
5308
5309               /* Check for "\r\n" by peeking at the next byte. */
5310               pos = 0;
5311               SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool));
5312               SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
5313                                              &found_eof, scratch_pool));
5314               if (numbytes == 1 && c == '\n')
5315                 {
5316                   eol_str = "\r\n";
5317                   len++;
5318                 }
5319               else
5320                 {
5321                   /* Pretend we never peeked. */
5322                   SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
5323                   found_eof = FALSE;
5324                   numbytes = 1;
5325                 }
5326             }
5327         }
5328       else
5329         svn_stringbuf_appendbyte(str, c);
5330
5331       if (eol_str)
5332         break;
5333     }
5334
5335   if (eol)
5336     *eol = eol_str;
5337   if (eof)
5338     *eof = found_eof;
5339   *stringbuf = str;
5340
5341   return SVN_NO_ERROR;
5342 }