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