]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_subr/io.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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 some sub-second resolution.  On Windows
1247               it is likely to be sub-millisecond; on Linux systems it depends
1248               on the filesystem, ext4 is typically 1ms, 4ms or 10ms resolution.
1249
1250              ## Perhaps find a better algorithm here. This will fail once
1251                 in every 1000 cases on a millisecond precision filesystem
1252                 if the mtime happens to be an exact second.
1253
1254                 But better to fail once in every thousand cases than every
1255                 time, like we did before.
1256
1257              Note for further research on algorithm:
1258                FAT32 has < 1 sec precision on ctime, but 2 sec on mtime.
1259
1260                Linux/ext4 with CONFIG_HZ=250 has high resolution
1261                apr_time_now and although the filesystem timestamps
1262                have similar high precision they are only updated with
1263                a coarser 4ms resolution. */
1264
1265           /* 10 milliseconds after now. */
1266 #ifndef SVN_HI_RES_SLEEP_MS
1267 #define SVN_HI_RES_SLEEP_MS 10
1268 #endif
1269           then = now + apr_time_from_msec(SVN_HI_RES_SLEEP_MS);
1270         }
1271
1272       /* Remove time taken to do stat() from sleep. */
1273       now = apr_time_now();
1274     }
1275
1276   if (now >= then)
1277     return; /* Passing negative values may suspend indefinitely (Windows) */
1278
1279   /* (t < 1000 will be round to 0 in apr) */
1280   if (then - now < 1000)
1281     apr_sleep(1000);
1282   else
1283     apr_sleep(then - now);
1284 }
1285
1286
1287 svn_error_t *
1288 svn_io_filesizes_different_p(svn_boolean_t *different_p,
1289                              const char *file1,
1290                              const char *file2,
1291                              apr_pool_t *pool)
1292 {
1293   apr_finfo_t finfo1;
1294   apr_finfo_t finfo2;
1295   apr_status_t status;
1296   const char *file1_apr, *file2_apr;
1297
1298   /* Not using svn_io_stat() because don't want to generate
1299      svn_error_t objects for non-error conditions. */
1300
1301   SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool));
1302   SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool));
1303
1304   /* Stat both files */
1305   status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool);
1306   if (status)
1307     {
1308       /* If we got an error stat'ing a file, it could be because the
1309          file was removed... or who knows.  Whatever the case, we
1310          don't know if the filesizes are definitely different, so
1311          assume that they're not. */
1312       *different_p = FALSE;
1313       return SVN_NO_ERROR;
1314     }
1315
1316   status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool);
1317   if (status)
1318     {
1319       /* See previous comment. */
1320       *different_p = FALSE;
1321       return SVN_NO_ERROR;
1322     }
1323
1324   /* Examine file sizes */
1325   if (finfo1.size == finfo2.size)
1326     *different_p = FALSE;
1327   else
1328     *different_p = TRUE;
1329
1330   return SVN_NO_ERROR;
1331 }
1332
1333
1334 svn_error_t *
1335 svn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
1336                                    svn_boolean_t *different_p23,
1337                                    svn_boolean_t *different_p13,
1338                                    const char *file1,
1339                                    const char *file2,
1340                                    const char *file3,
1341                                    apr_pool_t *scratch_pool)
1342 {
1343   apr_finfo_t finfo1, finfo2, finfo3;
1344   apr_status_t status1, status2, status3;
1345   const char *file1_apr, *file2_apr, *file3_apr;
1346
1347   /* Not using svn_io_stat() because don't want to generate
1348      svn_error_t objects for non-error conditions. */
1349
1350   SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool));
1351   SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool));
1352   SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool));
1353
1354   /* Stat all three files */
1355   status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool);
1356   status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool);
1357   status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool);
1358
1359   /* If we got an error stat'ing a file, it could be because the
1360      file was removed... or who knows.  Whatever the case, we
1361      don't know if the filesizes are definitely different, so
1362      assume that they're not. */
1363   *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size;
1364   *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size;
1365   *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size;
1366
1367   return SVN_NO_ERROR;
1368 }
1369
1370
1371 svn_error_t *
1372 svn_io_file_checksum2(svn_checksum_t **checksum,
1373                       const char *file,
1374                       svn_checksum_kind_t kind,
1375                       apr_pool_t *pool)
1376 {
1377   svn_stream_t *file_stream;
1378   svn_stream_t *checksum_stream;
1379   apr_file_t* f;
1380
1381   SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool));
1382   file_stream = svn_stream_from_aprfile2(f, FALSE, pool);
1383   checksum_stream = svn_stream_checksummed2(file_stream, checksum, NULL, kind,
1384                                             TRUE, pool);
1385
1386   /* Because the checksummed stream will force the reading (and
1387      checksumming) of all the file's bytes, we can just close the stream
1388      and let its magic work. */
1389   return svn_stream_close(checksum_stream);
1390 }
1391
1392
1393 svn_error_t *
1394 svn_io_file_checksum(unsigned char digest[],
1395                      const char *file,
1396                      apr_pool_t *pool)
1397 {
1398   svn_checksum_t *checksum;
1399
1400   SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool));
1401   memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE);
1402
1403   return SVN_NO_ERROR;
1404 }
1405
1406
1407 \f
1408 /*** Permissions and modes. ***/
1409
1410 #if !defined(WIN32) && !defined(__OS2__)
1411 /* Given the file specified by PATH, attempt to create an
1412    identical version of it owned by the current user.  This is done by
1413    moving it to a temporary location, copying the file back to its old
1414    path, then deleting the temporarily moved version.  All temporary
1415    allocations are done in POOL. */
1416 static svn_error_t *
1417 reown_file(const char *path,
1418            apr_pool_t *pool)
1419 {
1420   const char *unique_name;
1421
1422   SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name,
1423                                    svn_dirent_dirname(path, pool),
1424                                    svn_io_file_del_none, pool, pool));
1425   SVN_ERR(svn_io_file_rename(path, unique_name, pool));
1426   SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool));
1427   return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool));
1428 }
1429
1430 /* Determine what the PERMS for a new file should be by looking at the
1431    permissions of a temporary file that we create.
1432    Unfortunately, umask() as defined in POSIX provides no thread-safe way
1433    to get at the current value of the umask, so what we're doing here is
1434    the only way we have to determine which combination of write bits
1435    (User/Group/World) should be set by default.
1436    Make temporary allocations in SCRATCH_POOL.  */
1437 static svn_error_t *
1438 get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool)
1439 {
1440   /* the default permissions as read from the temp folder */
1441   static apr_fileperms_t default_perms = 0;
1442
1443   /* Technically, this "racy": Multiple threads may use enter here and
1444      try to figure out the default permission concurrently. That's fine
1445      since they will end up with the same results. Even more technical,
1446      apr_fileperms_t is an atomic type on 32+ bit machines.
1447    */
1448   if (default_perms == 0)
1449     {
1450       apr_finfo_t finfo;
1451       apr_file_t *fd;
1452       const char *fname_base, *fname;
1453       apr_uint32_t randomish;
1454       svn_error_t *err;
1455
1456       /* Get the perms for a newly created file to find out what bits
1457         should be set.
1458
1459         Explictly delete the file because we want this file to be as
1460         short-lived as possible since its presence means other
1461         processes may have to try multiple names.
1462
1463         Using svn_io_open_uniquely_named() here because other tempfile
1464         creation functions tweak the permission bits of files they create.
1465       */
1466       randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool
1467                    + (apr_uint32_t)apr_time_now());
1468       fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish);
1469
1470       SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, NULL, fname_base,
1471                                          NULL, svn_io_file_del_none,
1472                                          scratch_pool, scratch_pool));
1473       err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool);
1474       err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool));
1475       err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE,
1476                                                               scratch_pool));
1477       SVN_ERR(err);
1478       *perms = finfo.protection;
1479       default_perms = finfo.protection;
1480     }
1481   else
1482     *perms = default_perms;
1483
1484   return SVN_NO_ERROR;
1485 }
1486
1487 /* OR together permission bits of the file FD and the default permissions
1488    of a file as determined by get_default_file_perms(). Do temporary
1489    allocations in SCRATCH_POOL. */
1490 static svn_error_t *
1491 merge_default_file_perms(apr_file_t *fd, apr_fileperms_t *perms,
1492                          apr_pool_t *scratch_pool)
1493 {
1494   apr_finfo_t finfo;
1495   apr_fileperms_t default_perms;
1496
1497   SVN_ERR(get_default_file_perms(&default_perms, scratch_pool));
1498   SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool));
1499
1500   /* Glom the perms together. */
1501   *perms = default_perms | finfo.protection;
1502   return SVN_NO_ERROR;
1503 }
1504
1505 /* This is a helper function for the svn_io_set_file_read* functions
1506    that attempts to honor the users umask when dealing with
1507    permission changes.  It is a no-op when invoked on a symlink. */
1508 static svn_error_t *
1509 io_set_file_perms(const char *path,
1510                   svn_boolean_t change_readwrite,
1511                   svn_boolean_t enable_write,
1512                   svn_boolean_t change_executable,
1513                   svn_boolean_t executable,
1514                   svn_boolean_t ignore_enoent,
1515                   apr_pool_t *pool)
1516 {
1517   apr_status_t status;
1518   const char *path_apr;
1519   apr_finfo_t finfo;
1520   apr_fileperms_t perms_to_set;
1521
1522   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1523
1524   /* Try to change only a minimal amount of the perms first
1525      by getting the current perms and adding bits
1526      only on where read perms are granted.  If this fails
1527      fall through to just setting file attributes. */
1528   status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool);
1529   if (status)
1530     {
1531       if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1532         return SVN_NO_ERROR;
1533       else if (status != APR_ENOTIMPL)
1534         return svn_error_wrap_apr(status,
1535                                   _("Can't change perms of file '%s'"),
1536                                   svn_dirent_local_style(path, pool));
1537       return SVN_NO_ERROR;
1538     }
1539
1540   if (finfo.filetype == APR_LNK)
1541     return SVN_NO_ERROR;
1542
1543   perms_to_set = finfo.protection;
1544   if (change_readwrite)
1545     {
1546       if (enable_write) /* Make read-write. */
1547         {
1548           /* Tweak the owner bits only. The group/other bits aren't safe to
1549            * touch because we may end up setting them in undesired ways. */
1550           perms_to_set |= (APR_UREAD|APR_UWRITE);
1551         }
1552       else
1553         {
1554           if (finfo.protection & APR_UREAD)
1555             perms_to_set &= ~APR_UWRITE;
1556           if (finfo.protection & APR_GREAD)
1557             perms_to_set &= ~APR_GWRITE;
1558           if (finfo.protection & APR_WREAD)
1559             perms_to_set &= ~APR_WWRITE;
1560         }
1561     }
1562
1563   if (change_executable)
1564     {
1565       if (executable)
1566         {
1567           if (finfo.protection & APR_UREAD)
1568             perms_to_set |= APR_UEXECUTE;
1569           if (finfo.protection & APR_GREAD)
1570             perms_to_set |= APR_GEXECUTE;
1571           if (finfo.protection & APR_WREAD)
1572             perms_to_set |= APR_WEXECUTE;
1573         }
1574       else
1575         {
1576           if (finfo.protection & APR_UREAD)
1577             perms_to_set &= ~APR_UEXECUTE;
1578           if (finfo.protection & APR_GREAD)
1579             perms_to_set &= ~APR_GEXECUTE;
1580           if (finfo.protection & APR_WREAD)
1581             perms_to_set &= ~APR_WEXECUTE;
1582         }
1583     }
1584
1585   /* If we aren't changing anything then just return, this saves
1586      some system calls and helps with shared working copies */
1587   if (perms_to_set == finfo.protection)
1588     return SVN_NO_ERROR;
1589
1590   status = apr_file_perms_set(path_apr, perms_to_set);
1591   if (!status)
1592     return SVN_NO_ERROR;
1593
1594   if (APR_STATUS_IS_EPERM(status))
1595     {
1596       /* We don't have permissions to change the
1597          permissions!  Try a move, copy, and delete
1598          workaround to see if we can get the file owned by
1599          us.  If these succeed, try the permissions set
1600          again.
1601
1602          Note that we only attempt this in the
1603          stat-available path.  This assumes that the
1604          move-copy workaround will only be helpful on
1605          platforms that implement apr_stat. */
1606       SVN_ERR(reown_file(path, pool));
1607       status = apr_file_perms_set(path_apr, perms_to_set);
1608     }
1609
1610   if (!status)
1611     return SVN_NO_ERROR;
1612
1613   if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1614     return SVN_NO_ERROR;
1615   else if (status == APR_ENOTIMPL)
1616     {
1617       /* At least try to set the attributes. */
1618       apr_fileattrs_t attrs = 0;
1619       apr_fileattrs_t attrs_values = 0;
1620
1621       if (change_readwrite)
1622         {
1623           attrs = APR_FILE_ATTR_READONLY;
1624           if (!enable_write)
1625             attrs_values = APR_FILE_ATTR_READONLY;
1626         }
1627       if (change_executable)
1628         {
1629           attrs = APR_FILE_ATTR_EXECUTABLE;
1630           if (executable)
1631             attrs_values = APR_FILE_ATTR_EXECUTABLE;
1632         }
1633       status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
1634     }
1635
1636   return svn_error_wrap_apr(status,
1637                             _("Can't change perms of file '%s'"),
1638                             svn_dirent_local_style(path, pool));
1639 }
1640 #endif /* !WIN32 && !__OS2__ */
1641
1642 #ifdef WIN32
1643 #if APR_HAS_UNICODE_FS
1644 /* copy of the apr function utf8_to_unicode_path since apr doesn't export this one */
1645 static apr_status_t io_utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen,
1646                                             const char* srcstr)
1647 {
1648     /* TODO: The computations could preconvert the string to determine
1649      * the true size of the retstr, but that's a memory over speed
1650      * tradeoff that isn't appropriate this early in development.
1651      *
1652      * Allocate the maximum string length based on leading 4
1653      * characters of \\?\ (allowing nearly unlimited path lengths)
1654      * plus the trailing null, then transform /'s into \\'s since
1655      * the \\?\ form doesn't allow '/' path separators.
1656      *
1657      * Note that the \\?\ form only works for local drive paths, and
1658      * \\?\UNC\ is needed UNC paths.
1659      */
1660     apr_size_t srcremains = strlen(srcstr) + 1;
1661     apr_wchar_t *t = retstr;
1662     apr_status_t rv;
1663
1664     /* This is correct, we don't twist the filename if it will
1665      * definitely be shorter than 248 characters.  It merits some
1666      * performance testing to see if this has any effect, but there
1667      * seem to be applications that get confused by the resulting
1668      * Unicode \\?\ style file names, especially if they use argv[0]
1669      * or call the Win32 API functions such as GetModuleName, etc.
1670      * Not every application is prepared to handle such names.
1671      *
1672      * Note also this is shorter than MAX_PATH, as directory paths
1673      * are actually limited to 248 characters.
1674      *
1675      * Note that a utf-8 name can never result in more wide chars
1676      * than the original number of utf-8 narrow chars.
1677      */
1678     if (srcremains > 248) {
1679         if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
1680             wcscpy (retstr, L"\\\\?\\");
1681             retlen -= 4;
1682             t += 4;
1683         }
1684         else if ((srcstr[0] == '/' || srcstr[0] == '\\')
1685               && (srcstr[1] == '/' || srcstr[1] == '\\')
1686               && (srcstr[2] != '?')) {
1687             /* Skip the slashes */
1688             srcstr += 2;
1689             srcremains -= 2;
1690             wcscpy (retstr, L"\\\\?\\UNC\\");
1691             retlen -= 8;
1692             t += 8;
1693         }
1694     }
1695
1696     if (rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen)) {
1697         return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
1698     }
1699     if (srcremains) {
1700         return APR_ENAMETOOLONG;
1701     }
1702     for (; *t; ++t)
1703         if (*t == L'/')
1704             *t = L'\\';
1705     return APR_SUCCESS;
1706 }
1707 #endif
1708
1709 static apr_status_t io_win_file_attrs_set(const char *fname,
1710                                           DWORD attributes,
1711                                           DWORD attr_mask,
1712                                           apr_pool_t *pool)
1713 {
1714     /* this is an implementation of apr_file_attrs_set() but one
1715        that uses the proper Windows attributes instead of the apr
1716        attributes. This way, we can apply any Windows file and
1717        folder attributes even if apr doesn't implement them */
1718     DWORD flags;
1719     apr_status_t rv;
1720 #if APR_HAS_UNICODE_FS
1721     apr_wchar_t wfname[APR_PATH_MAX];
1722 #endif
1723
1724 #if APR_HAS_UNICODE_FS
1725     IF_WIN_OS_IS_UNICODE
1726     {
1727         if (rv = io_utf8_to_unicode_path(wfname,
1728                                          sizeof(wfname) / sizeof(wfname[0]),
1729                                          fname))
1730             return rv;
1731         flags = GetFileAttributesW(wfname);
1732     }
1733 #endif
1734 #if APR_HAS_ANSI_FS
1735     ELSE_WIN_OS_IS_ANSI
1736     {
1737         flags = GetFileAttributesA(fname);
1738     }
1739 #endif
1740
1741     if (flags == 0xFFFFFFFF)
1742         return apr_get_os_error();
1743
1744     flags &= ~attr_mask;
1745     flags |= (attributes & attr_mask);
1746
1747 #if APR_HAS_UNICODE_FS
1748     IF_WIN_OS_IS_UNICODE
1749     {
1750         rv = SetFileAttributesW(wfname, flags);
1751     }
1752 #endif
1753 #if APR_HAS_ANSI_FS
1754     ELSE_WIN_OS_IS_ANSI
1755     {
1756         rv = SetFileAttributesA(fname, flags);
1757     }
1758 #endif
1759
1760     if (rv == 0)
1761         return apr_get_os_error();
1762
1763     return APR_SUCCESS;
1764 }
1765
1766 #endif
1767
1768 svn_error_t *
1769 svn_io_set_file_read_write_carefully(const char *path,
1770                                      svn_boolean_t enable_write,
1771                                      svn_boolean_t ignore_enoent,
1772                                      apr_pool_t *pool)
1773 {
1774   if (enable_write)
1775     return svn_io_set_file_read_write(path, ignore_enoent, pool);
1776   return svn_io_set_file_read_only(path, ignore_enoent, pool);
1777 }
1778
1779 svn_error_t *
1780 svn_io_set_file_read_only(const char *path,
1781                           svn_boolean_t ignore_enoent,
1782                           apr_pool_t *pool)
1783 {
1784   /* On Windows and OS/2, just set the file attributes -- on unix call
1785      our internal function which attempts to honor the umask. */
1786 #if !defined(WIN32) && !defined(__OS2__)
1787   return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE,
1788                            ignore_enoent, pool);
1789 #else
1790   apr_status_t status;
1791   const char *path_apr;
1792
1793   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1794
1795   status = apr_file_attrs_set(path_apr,
1796                               APR_FILE_ATTR_READONLY,
1797                               APR_FILE_ATTR_READONLY,
1798                               pool);
1799
1800   if (status && status != APR_ENOTIMPL)
1801     if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
1802       return svn_error_wrap_apr(status,
1803                                 _("Can't set file '%s' read-only"),
1804                                 svn_dirent_local_style(path, pool));
1805
1806   return SVN_NO_ERROR;
1807 #endif
1808 }
1809
1810
1811 svn_error_t *
1812 svn_io_set_file_read_write(const char *path,
1813                            svn_boolean_t ignore_enoent,
1814                            apr_pool_t *pool)
1815 {
1816   /* On Windows and OS/2, just set the file attributes -- on unix call
1817      our internal function which attempts to honor the umask. */
1818 #if !defined(WIN32) && !defined(__OS2__)
1819   return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE,
1820                            ignore_enoent, pool);
1821 #else
1822   apr_status_t status;
1823   const char *path_apr;
1824
1825   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1826
1827   status = apr_file_attrs_set(path_apr,
1828                               0,
1829                               APR_FILE_ATTR_READONLY,
1830                               pool);
1831
1832   if (status && status != APR_ENOTIMPL)
1833     if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
1834       return svn_error_wrap_apr(status,
1835                                 _("Can't set file '%s' read-write"),
1836                                 svn_dirent_local_style(path, pool));
1837
1838   return SVN_NO_ERROR;
1839 #endif
1840 }
1841
1842 svn_error_t *
1843 svn_io_set_file_executable(const char *path,
1844                            svn_boolean_t executable,
1845                            svn_boolean_t ignore_enoent,
1846                            apr_pool_t *pool)
1847 {
1848   /* On Windows and OS/2, just exit -- on unix call our internal function
1849   which attempts to honor the umask. */
1850 #if (!defined(WIN32) && !defined(__OS2__))
1851   return io_set_file_perms(path, FALSE, FALSE, TRUE, executable,
1852                            ignore_enoent, pool);
1853 #else
1854   return SVN_NO_ERROR;
1855 #endif
1856 }
1857
1858
1859 svn_error_t *
1860 svn_io__is_finfo_read_only(svn_boolean_t *read_only,
1861                            apr_finfo_t *file_info,
1862                            apr_pool_t *pool)
1863 {
1864 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1865   apr_status_t apr_err;
1866   apr_uid_t uid;
1867   apr_gid_t gid;
1868
1869   *read_only = FALSE;
1870
1871   apr_err = apr_uid_current(&uid, &gid, pool);
1872
1873   if (apr_err)
1874     return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
1875
1876   /* Check write bit for current user. */
1877   if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
1878     *read_only = !(file_info->protection & APR_UWRITE);
1879
1880   else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1881     *read_only = !(file_info->protection & APR_GWRITE);
1882
1883   else
1884     *read_only = !(file_info->protection & APR_WWRITE);
1885
1886 #else  /* WIN32 || __OS2__ || !APR_HAS_USER */
1887   *read_only = (file_info->protection & APR_FREADONLY);
1888 #endif
1889
1890   return SVN_NO_ERROR;
1891 }
1892
1893 svn_error_t *
1894 svn_io__is_finfo_executable(svn_boolean_t *executable,
1895                             apr_finfo_t *file_info,
1896                             apr_pool_t *pool)
1897 {
1898 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1899   apr_status_t apr_err;
1900   apr_uid_t uid;
1901   apr_gid_t gid;
1902
1903   *executable = FALSE;
1904
1905   apr_err = apr_uid_current(&uid, &gid, pool);
1906
1907   if (apr_err)
1908     return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
1909
1910   /* Check executable bit for current user. */
1911   if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
1912     *executable = (file_info->protection & APR_UEXECUTE);
1913
1914   else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1915     *executable = (file_info->protection & APR_GEXECUTE);
1916
1917   else
1918     *executable = (file_info->protection & APR_WEXECUTE);
1919
1920 #else  /* WIN32 || __OS2__ || !APR_HAS_USER */
1921   *executable = FALSE;
1922 #endif
1923
1924   return SVN_NO_ERROR;
1925 }
1926
1927 svn_error_t *
1928 svn_io_is_file_executable(svn_boolean_t *executable,
1929                           const char *path,
1930                           apr_pool_t *pool)
1931 {
1932 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1933   apr_finfo_t file_info;
1934
1935   SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
1936                       pool));
1937   SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
1938
1939 #else  /* WIN32 || __OS2__ || !APR_HAS_USER */
1940   *executable = FALSE;
1941 #endif
1942
1943   return SVN_NO_ERROR;
1944 }
1945
1946 \f
1947 /*** File locking. ***/
1948 #if !defined(WIN32) && !defined(__OS2__)
1949 /* Clear all outstanding locks on ARG, an open apr_file_t *. */
1950 static apr_status_t
1951 file_clear_locks(void *arg)
1952 {
1953   apr_status_t apr_err;
1954   apr_file_t *f = arg;
1955
1956   /* Remove locks. */
1957   apr_err = apr_file_unlock(f);
1958   if (apr_err)
1959     return apr_err;
1960
1961   return 0;
1962 }
1963 #endif
1964
1965 svn_error_t *
1966 svn_io_lock_open_file(apr_file_t *lockfile_handle,
1967                       svn_boolean_t exclusive,
1968                       svn_boolean_t nonblocking,
1969                       apr_pool_t *pool)
1970 {
1971   int locktype = APR_FLOCK_SHARED;
1972   apr_status_t apr_err;
1973   const char *fname;
1974
1975   if (exclusive)
1976     locktype = APR_FLOCK_EXCLUSIVE;
1977   if (nonblocking)
1978     locktype |= APR_FLOCK_NONBLOCK;
1979
1980   /* We need this only in case of an error but this is cheap to get -
1981    * so we do it here for clarity. */
1982   apr_err = apr_file_name_get(&fname, lockfile_handle);
1983   if (apr_err)
1984     return svn_error_wrap_apr(apr_err, _("Can't get file name"));
1985
1986   /* Get lock on the filehandle. */
1987   apr_err = apr_file_lock(lockfile_handle, locktype);
1988
1989   /* In deployments with two or more multithreaded servers running on
1990      the same system serving two or more fsfs repositories it is
1991      possible for a deadlock to occur when getting a write lock on
1992      db/txn-current-lock:
1993
1994      Process 1                         Process 2
1995      ---------                         ---------
1996      thread 1: get lock in repos A
1997                                        thread 1: get lock in repos B
1998                                        thread 2: block getting lock in repos A
1999      thread 2: try to get lock in B *** deadlock ***
2000
2001      Retry for a while for the deadlock to clear. */
2002   FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
2003
2004   if (apr_err)
2005     {
2006       switch (locktype & APR_FLOCK_TYPEMASK)
2007         {
2008         case APR_FLOCK_SHARED:
2009           return svn_error_wrap_apr(apr_err,
2010                                     _("Can't get shared lock on file '%s'"),
2011                                     try_utf8_from_internal_style(fname, pool));
2012         case APR_FLOCK_EXCLUSIVE:
2013           return svn_error_wrap_apr(apr_err,
2014                                     _("Can't get exclusive lock on file '%s'"),
2015                                     try_utf8_from_internal_style(fname, pool));
2016         default:
2017           SVN_ERR_MALFUNCTION();
2018         }
2019     }
2020
2021 /* On Windows and OS/2 file locks are automatically released when
2022    the file handle closes */
2023 #if !defined(WIN32) && !defined(__OS2__)
2024   apr_pool_cleanup_register(pool, lockfile_handle,
2025                             file_clear_locks,
2026                             apr_pool_cleanup_null);
2027 #endif
2028
2029   return SVN_NO_ERROR;
2030 }
2031
2032 svn_error_t *
2033 svn_io_unlock_open_file(apr_file_t *lockfile_handle,
2034                         apr_pool_t *pool)
2035 {
2036   const char *fname;
2037   apr_status_t apr_err;
2038
2039   /* We need this only in case of an error but this is cheap to get -
2040    * so we do it here for clarity. */
2041   apr_err = apr_file_name_get(&fname, lockfile_handle);
2042   if (apr_err)
2043     return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2044
2045   /* The actual unlock attempt. */
2046   apr_err = apr_file_unlock(lockfile_handle);
2047   if (apr_err)
2048     return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
2049                               try_utf8_from_internal_style(fname, pool));
2050
2051 /* On Windows and OS/2 file locks are automatically released when
2052    the file handle closes */
2053 #if !defined(WIN32) && !defined(__OS2__)
2054   apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks);
2055 #endif
2056
2057   return SVN_NO_ERROR;
2058 }
2059
2060 svn_error_t *
2061 svn_io_file_lock2(const char *lock_file,
2062                   svn_boolean_t exclusive,
2063                   svn_boolean_t nonblocking,
2064                   apr_pool_t *pool)
2065 {
2066   int locktype = APR_FLOCK_SHARED;
2067   apr_file_t *lockfile_handle;
2068   apr_int32_t flags;
2069
2070   if (exclusive)
2071     locktype = APR_FLOCK_EXCLUSIVE;
2072
2073   flags = APR_READ;
2074   if (locktype == APR_FLOCK_EXCLUSIVE)
2075     flags |= APR_WRITE;
2076
2077   /* locktype is never read after this block, so we don't need to bother
2078      setting it.  If that were to ever change, uncomment the following
2079      block.
2080   if (nonblocking)
2081     locktype |= APR_FLOCK_NONBLOCK;
2082   */
2083
2084   SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
2085                            APR_OS_DEFAULT,
2086                            pool));
2087
2088   /* Get lock on the filehandle. */
2089   return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
2090 }
2091
2092
2093 \f
2094 /* Data consistency/coherency operations. */
2095
2096 svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
2097                                        apr_pool_t *pool)
2098 {
2099   apr_os_file_t filehand;
2100
2101   /* First make sure that any user-space buffered data is flushed. */
2102   SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
2103                                      N_("Can't flush file '%s'"),
2104                                      N_("Can't flush stream"),
2105                                      pool));
2106
2107   apr_os_file_get(&filehand, file);
2108
2109   /* Call the operating system specific function to actually force the
2110      data to disk. */
2111   {
2112 #ifdef WIN32
2113
2114     if (! FlushFileBuffers(filehand))
2115         return svn_error_wrap_apr(apr_get_os_error(),
2116                                   _("Can't flush file to disk"));
2117
2118 #else
2119       int rv;
2120
2121       do {
2122         rv = fsync(filehand);
2123       } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
2124
2125       /* If the file is in a memory filesystem, fsync() may return
2126          EINVAL.  Presumably the user knows the risks, and we can just
2127          ignore the error. */
2128       if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
2129         return SVN_NO_ERROR;
2130
2131       if (rv == -1)
2132         return svn_error_wrap_apr(apr_get_os_error(),
2133                                   _("Can't flush file to disk"));
2134
2135 #endif
2136   }
2137   return SVN_NO_ERROR;
2138 }
2139
2140
2141 \f
2142 /* TODO write test for these two functions, then refactor. */
2143
2144 /* Set RESULT to an svn_stringbuf_t containing the contents of FILE.
2145    FILENAME is the FILE's on-disk APR-safe name, or NULL if that name
2146    isn't known.  If CHECK_SIZE is TRUE, the function will attempt to
2147    first stat() the file to determine it's size before sucking its
2148    contents into the stringbuf.  (Doing so can prevent unnecessary
2149    memory usage, an unwanted side effect of the stringbuf growth and
2150    reallocation mechanism.)  */
2151 static svn_error_t *
2152 stringbuf_from_aprfile(svn_stringbuf_t **result,
2153                        const char *filename,
2154                        apr_file_t *file,
2155                        svn_boolean_t check_size,
2156                        apr_pool_t *pool)
2157 {
2158   apr_size_t len;
2159   svn_error_t *err;
2160   svn_stringbuf_t *res = NULL;
2161   apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE;
2162   char *buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
2163
2164   /* If our caller wants us to check the size of the file for
2165      efficient memory handling, we'll try to do so. */
2166   if (check_size)
2167     {
2168       apr_status_t status;
2169
2170       /* If our caller didn't tell us the file's name, we'll ask APR
2171          if it knows the name.  No problem if we can't figure it out.  */
2172       if (! filename)
2173         {
2174           const char *filename_apr;
2175           if (! (status = apr_file_name_get(&filename_apr, file)))
2176             filename = filename_apr;
2177         }
2178
2179       /* If we now know the filename, try to stat().  If we succeed,
2180          we know how to allocate our stringbuf.  */
2181       if (filename)
2182         {
2183           apr_finfo_t finfo;
2184           if (! (status = apr_stat(&finfo, filename, APR_FINFO_MIN, pool)))
2185             res_initial_len = (apr_size_t)finfo.size;
2186         }
2187     }
2188
2189
2190   /* XXX: We should check the incoming data for being of type binary. */
2191
2192   res = svn_stringbuf_create_ensure(res_initial_len, pool);
2193
2194   /* apr_file_read will not return data and eof in the same call. So this loop
2195    * is safe from missing read data.  */
2196   len = SVN__STREAM_CHUNK_SIZE;
2197   err = svn_io_file_read(file, buf, &len, pool);
2198   while (! err)
2199     {
2200       svn_stringbuf_appendbytes(res, buf, len);
2201       len = SVN__STREAM_CHUNK_SIZE;
2202       err = svn_io_file_read(file, buf, &len, pool);
2203     }
2204
2205   /* Having read all the data we *expect* EOF */
2206   if (err && !APR_STATUS_IS_EOF(err->apr_err))
2207     return err;
2208   svn_error_clear(err);
2209
2210   *result = res;
2211   return SVN_NO_ERROR;
2212 }
2213
2214 svn_error_t *
2215 svn_stringbuf_from_file2(svn_stringbuf_t **result,
2216                          const char *filename,
2217                          apr_pool_t *pool)
2218 {
2219   apr_file_t *f;
2220
2221   if (filename[0] == '-' && filename[1] == '\0')
2222     {
2223       apr_status_t apr_err;
2224       if ((apr_err = apr_file_open_stdin(&f, pool)))
2225         return svn_error_wrap_apr(apr_err, _("Can't open stdin"));
2226       SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool));
2227     }
2228   else
2229     {
2230       SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool));
2231       SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool));
2232     }
2233   return svn_io_file_close(f, pool);
2234 }
2235
2236
2237 svn_error_t *
2238 svn_stringbuf_from_file(svn_stringbuf_t **result,
2239                         const char *filename,
2240                         apr_pool_t *pool)
2241 {
2242   if (filename[0] == '-' && filename[1] == '\0')
2243     return svn_error_create
2244         (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2245          _("Reading from stdin is disallowed"));
2246   return svn_stringbuf_from_file2(result, filename, pool);
2247 }
2248
2249 svn_error_t *
2250 svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
2251                            apr_file_t *file,
2252                            apr_pool_t *pool)
2253 {
2254   return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
2255 }
2256
2257
2258 \f
2259 /* Deletion. */
2260
2261 svn_error_t *
2262 svn_io_remove_file2(const char *path,
2263                     svn_boolean_t ignore_enoent,
2264                     apr_pool_t *scratch_pool)
2265 {
2266   apr_status_t apr_err;
2267   const char *path_apr;
2268
2269   SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
2270
2271   apr_err = apr_file_remove(path_apr, scratch_pool);
2272   if (!apr_err
2273       || (ignore_enoent
2274           && (APR_STATUS_IS_ENOENT(apr_err)
2275               || SVN__APR_STATUS_IS_ENOTDIR(apr_err))))
2276     return SVN_NO_ERROR;
2277
2278 #ifdef WIN32
2279   /* If the target is read only NTFS reports EACCESS and FAT/FAT32
2280      reports EEXIST */
2281   if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
2282     {
2283       /* Set the destination file writable because Windows will not
2284          allow us to delete when path is read-only */
2285       SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool));
2286       apr_err = apr_file_remove(path_apr, scratch_pool);
2287
2288       if (!apr_err)
2289         return SVN_NO_ERROR;
2290     }
2291
2292     {
2293       apr_status_t os_err = APR_TO_OS_ERROR(apr_err);
2294       /* Check to make sure we aren't trying to delete a directory */
2295       if (os_err == ERROR_ACCESS_DENIED || os_err == ERROR_SHARING_VIOLATION)
2296         {
2297           apr_finfo_t finfo;
2298
2299           if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
2300               && finfo.filetype == APR_REG)
2301             {
2302               WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr,
2303                                                         scratch_pool));
2304             }
2305         }
2306
2307       /* Just return the delete error */
2308     }
2309 #endif
2310
2311   if (apr_err)
2312     return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
2313                               svn_dirent_local_style(path, scratch_pool));
2314
2315   return SVN_NO_ERROR;
2316 }
2317
2318
2319 svn_error_t *
2320 svn_io_remove_dir(const char *path, apr_pool_t *pool)
2321 {
2322   return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
2323 }
2324
2325 /*
2326  Mac OS X has a bug where if you're reading the contents of a
2327  directory via readdir in a loop, and you remove one of the entries in
2328  the directory and the directory has 338 or more files in it you will
2329  skip over some of the entries in the directory.  Needless to say,
2330  this causes problems if you are using this kind of loop inside a
2331  function that is recursively deleting a directory, because when you
2332  get around to removing the directory it will still have something in
2333  it. A similar problem has been observed in other BSDs. This bug has
2334  since been fixed. See http://www.vnode.ch/fixing_seekdir for details.
2335
2336  The workaround is to delete the files only _after_ the initial
2337  directory scan.  A previous workaround involving rewinddir is
2338  problematic on Win32 and some NFS clients, notably NetBSD.
2339
2340  See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
2341  http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
2342 */
2343
2344 /* Neither windows nor unix allows us to delete a non-empty
2345    directory.
2346
2347    This is a function to perform the equivalent of 'rm -rf'. */
2348 svn_error_t *
2349 svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
2350                    svn_cancel_func_t cancel_func, void *cancel_baton,
2351                    apr_pool_t *pool)
2352 {
2353   svn_error_t *err;
2354   apr_pool_t *subpool;
2355   apr_hash_t *dirents;
2356   apr_hash_index_t *hi;
2357
2358   /* Check for pending cancellation request.
2359      If we need to bail out, do so early. */
2360
2361   if (cancel_func)
2362     SVN_ERR((*cancel_func)(cancel_baton));
2363
2364   subpool = svn_pool_create(pool);
2365
2366   err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
2367   if (err)
2368     {
2369       /* if the directory doesn't exist, our mission is accomplished */
2370       if (ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err))
2371         {
2372           svn_error_clear(err);
2373           return SVN_NO_ERROR;
2374         }
2375       return svn_error_trace(err);
2376     }
2377
2378   for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
2379     {
2380       const char *name = svn__apr_hash_index_key(hi);
2381       const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
2382       const char *fullpath;
2383
2384       fullpath = svn_dirent_join(path, name, subpool);
2385       if (dirent->kind == svn_node_dir)
2386         {
2387           /* Don't check for cancellation, the callee will immediately do so */
2388           SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func,
2389                                      cancel_baton, subpool));
2390         }
2391       else
2392         {
2393           if (cancel_func)
2394             SVN_ERR((*cancel_func)(cancel_baton));
2395
2396           err = svn_io_remove_file2(fullpath, FALSE, subpool);
2397           if (err)
2398             return svn_error_createf
2399               (err->apr_err, err, _("Can't remove '%s'"),
2400                svn_dirent_local_style(fullpath, subpool));
2401         }
2402     }
2403
2404   svn_pool_destroy(subpool);
2405
2406   return svn_io_dir_remove_nonrecursive(path, pool);
2407 }
2408
2409 svn_error_t *
2410 svn_io_get_dir_filenames(apr_hash_t **dirents,
2411                          const char *path,
2412                          apr_pool_t *pool)
2413 {
2414   return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
2415                                              pool, pool));
2416 }
2417
2418 svn_io_dirent2_t *
2419 svn_io_dirent2_create(apr_pool_t *result_pool)
2420 {
2421   svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
2422
2423   /*dirent->kind = svn_node_none;
2424   dirent->special = FALSE;*/
2425   dirent->filesize = SVN_INVALID_FILESIZE;
2426   /*dirent->mtime = 0;*/
2427
2428   return dirent;
2429 }
2430
2431 svn_io_dirent2_t *
2432 svn_io_dirent2_dup(const svn_io_dirent2_t *item,
2433                    apr_pool_t *result_pool)
2434 {
2435   return apr_pmemdup(result_pool,
2436                      item,
2437                      sizeof(*item));
2438 }
2439
2440 svn_error_t *
2441 svn_io_get_dirents3(apr_hash_t **dirents,
2442                     const char *path,
2443                     svn_boolean_t only_check_type,
2444                     apr_pool_t *result_pool,
2445                     apr_pool_t *scratch_pool)
2446 {
2447   apr_status_t status;
2448   apr_dir_t *this_dir;
2449   apr_finfo_t this_entry;
2450   apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
2451
2452   if (!only_check_type)
2453     flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
2454
2455   *dirents = apr_hash_make(result_pool);
2456
2457   SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
2458
2459   for (status = apr_dir_read(&this_entry, flags, this_dir);
2460        status == APR_SUCCESS;
2461        status = apr_dir_read(&this_entry, flags, this_dir))
2462     {
2463       if ((this_entry.name[0] == '.')
2464           && ((this_entry.name[1] == '\0')
2465               || ((this_entry.name[1] == '.')
2466                   && (this_entry.name[2] == '\0'))))
2467         {
2468           continue;
2469         }
2470       else
2471         {
2472           const char *name;
2473           svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
2474
2475           SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
2476
2477           map_apr_finfo_to_node_kind(&(dirent->kind),
2478                                      &(dirent->special),
2479                                      &this_entry);
2480
2481           if (!only_check_type)
2482             {
2483               dirent->filesize = this_entry.size;
2484               dirent->mtime = this_entry.mtime;
2485             }
2486
2487           svn_hash_sets(*dirents, name, dirent);
2488         }
2489     }
2490
2491   if (! (APR_STATUS_IS_ENOENT(status)))
2492     return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
2493                               svn_dirent_local_style(path, scratch_pool));
2494
2495   status = apr_dir_close(this_dir);
2496   if (status)
2497     return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
2498                               svn_dirent_local_style(path, scratch_pool));
2499
2500   return SVN_NO_ERROR;
2501 }
2502
2503 svn_error_t *
2504 svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
2505                     const char *path,
2506                     svn_boolean_t verify_truename,
2507                     svn_boolean_t ignore_enoent,
2508                     apr_pool_t *result_pool,
2509                     apr_pool_t *scratch_pool)
2510 {
2511   apr_finfo_t finfo;
2512   svn_io_dirent2_t *dirent;
2513   svn_error_t *err;
2514   apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
2515                        | APR_FINFO_SIZE | APR_FINFO_MTIME;
2516
2517 #if defined(WIN32) || defined(__OS2__)
2518   if (verify_truename)
2519     wanted |= APR_FINFO_NAME;
2520 #endif
2521
2522   err = svn_io_stat(&finfo, path, wanted, scratch_pool);
2523
2524   if (err && ignore_enoent &&
2525       (APR_STATUS_IS_ENOENT(err->apr_err)
2526        || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2527     {
2528       svn_error_clear(err);
2529       dirent = svn_io_dirent2_create(result_pool);
2530       SVN_ERR_ASSERT(dirent->kind == svn_node_none);
2531
2532       *dirent_p = dirent;
2533       return SVN_NO_ERROR;
2534     }
2535   SVN_ERR(err);
2536
2537 #if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
2538   if (verify_truename)
2539     {
2540       const char *requested_name = svn_dirent_basename(path, NULL);
2541
2542       if (requested_name[0] == '\0')
2543         {
2544           /* No parent directory. No need to stat/verify */
2545         }
2546 #if defined(WIN32) || defined(__OS2__)
2547       else if (finfo.name)
2548         {
2549           const char *name_on_disk;
2550           SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
2551                                      scratch_pool));
2552
2553           if (strcmp(name_on_disk, requested_name) /* != 0 */)
2554             {
2555               if (ignore_enoent)
2556                 {
2557                   *dirent_p = svn_io_dirent2_create(result_pool);
2558                   return SVN_NO_ERROR;
2559                 }
2560               else
2561                 return svn_error_createf(APR_ENOENT, NULL,
2562                           _("Path '%s' not found, case obstructed by '%s'"),
2563                           svn_dirent_local_style(path, scratch_pool),
2564                           name_on_disk);
2565             }
2566         }
2567 #elif defined(DARWIN)
2568       /* Currently apr doesn't set finfo.name on DARWIN, returning
2569                    APR_INCOMPLETE.
2570          ### Can we optimize this in another way? */
2571       else
2572         {
2573           apr_hash_t *dirents;
2574
2575           err = svn_io_get_dirents3(&dirents,
2576                                     svn_dirent_dirname(path, scratch_pool),
2577                                     TRUE /* only_check_type */,
2578                                     scratch_pool, scratch_pool);
2579
2580           if (err && ignore_enoent
2581               && (APR_STATUS_IS_ENOENT(err->apr_err)
2582                   || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2583             {
2584               svn_error_clear(err);
2585
2586               *dirent_p = svn_io_dirent2_create(result_pool);
2587               return SVN_NO_ERROR;
2588             }
2589           else
2590             SVN_ERR(err);
2591
2592           if (! svn_hash_gets(dirents, requested_name))
2593             {
2594               if (ignore_enoent)
2595                 {
2596                   *dirent_p = svn_io_dirent2_create(result_pool);
2597                   return SVN_NO_ERROR;
2598                 }
2599               else
2600                 return svn_error_createf(APR_ENOENT, NULL,
2601                           _("Path '%s' not found"),
2602                           svn_dirent_local_style(path, scratch_pool));
2603             }
2604         }
2605 #endif
2606     }
2607 #endif
2608
2609   dirent = svn_io_dirent2_create(result_pool);
2610   map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
2611
2612   dirent->filesize = finfo.size;
2613   dirent->mtime = finfo.mtime;
2614
2615   *dirent_p = dirent;
2616
2617   return SVN_NO_ERROR;
2618 }
2619
2620 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2621 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2622
2623 /* Handle an error from the child process (before command execution) by
2624    printing DESC and the error string corresponding to STATUS to stderr. */
2625 static void
2626 handle_child_process_error(apr_pool_t *pool, apr_status_t status,
2627                            const char *desc)
2628 {
2629   char errbuf[256];
2630   apr_file_t *errfile;
2631   void *p;
2632
2633   /* We can't do anything if we get an error here, so just return. */
2634   if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool))
2635     return;
2636   errfile = p;
2637
2638   if (errfile)
2639     /* What we get from APR is in native encoding. */
2640     apr_file_printf(errfile, "%s: %s",
2641                     desc, apr_strerror(status, errbuf,
2642                                        sizeof(errbuf)));
2643 }
2644
2645
2646 svn_error_t *
2647 svn_io_start_cmd3(apr_proc_t *cmd_proc,
2648                   const char *path,
2649                   const char *cmd,
2650                   const char *const *args,
2651                   const char *const *env,
2652                   svn_boolean_t inherit,
2653                   svn_boolean_t infile_pipe,
2654                   apr_file_t *infile,
2655                   svn_boolean_t outfile_pipe,
2656                   apr_file_t *outfile,
2657                   svn_boolean_t errfile_pipe,
2658                   apr_file_t *errfile,
2659                   apr_pool_t *pool)
2660 {
2661   apr_status_t apr_err;
2662   apr_procattr_t *cmdproc_attr;
2663   int num_args;
2664   const char **args_native;
2665   const char *cmd_apr;
2666
2667   SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
2668   SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
2669   SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
2670
2671   /* Create the process attributes. */
2672   apr_err = apr_procattr_create(&cmdproc_attr, pool);
2673   if (apr_err)
2674     return svn_error_wrap_apr(apr_err,
2675                               _("Can't create process '%s' attributes"),
2676                               cmd);
2677
2678   /* Make sure we invoke cmd directly, not through a shell. */
2679   apr_err = apr_procattr_cmdtype_set(cmdproc_attr,
2680                                      inherit ? APR_PROGRAM_PATH : APR_PROGRAM);
2681   if (apr_err)
2682     return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
2683                               cmd);
2684
2685   /* Set the process's working directory. */
2686   if (path)
2687     {
2688       const char *path_apr;
2689
2690       SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2691       apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
2692       if (apr_err)
2693         return svn_error_wrap_apr(apr_err,
2694                                   _("Can't set process '%s' directory"),
2695                                   cmd);
2696     }
2697
2698   /* Use requested inputs and outputs.
2699
2700      ### Unfortunately each of these apr functions creates a pipe and then
2701      overwrites the pipe file descriptor with the descriptor we pass
2702      in. The pipes can then never be closed. This is an APR bug. */
2703   if (infile)
2704     {
2705       apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
2706       if (apr_err)
2707         return svn_error_wrap_apr(apr_err,
2708                                   _("Can't set process '%s' child input"),
2709                                   cmd);
2710     }
2711   if (outfile)
2712     {
2713       apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
2714       if (apr_err)
2715         return svn_error_wrap_apr(apr_err,
2716                                   _("Can't set process '%s' child outfile"),
2717                                   cmd);
2718     }
2719   if (errfile)
2720     {
2721       apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
2722       if (apr_err)
2723         return svn_error_wrap_apr(apr_err,
2724                                   _("Can't set process '%s' child errfile"),
2725                                   cmd);
2726     }
2727
2728   /* Forward request for pipes to APR. */
2729   if (infile_pipe || outfile_pipe || errfile_pipe)
2730     {
2731       apr_err = apr_procattr_io_set(cmdproc_attr,
2732                                     infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
2733                                     outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
2734                                     errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE);
2735
2736       if (apr_err)
2737         return svn_error_wrap_apr(apr_err,
2738                                   _("Can't set process '%s' stdio pipes"),
2739                                   cmd);
2740     }
2741
2742   /* Have the child print any problems executing its program to errfile. */
2743   apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
2744   if (apr_err)
2745     return svn_error_wrap_apr(apr_err,
2746                               _("Can't set process '%s' child errfile for "
2747                                 "error handler"),
2748                               cmd);
2749   apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
2750                                          handle_child_process_error);
2751   if (apr_err)
2752     return svn_error_wrap_apr(apr_err,
2753                               _("Can't set process '%s' error handler"),
2754                               cmd);
2755
2756   /* Convert cmd and args from UTF-8 */
2757   SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool));
2758   for (num_args = 0; args[num_args]; num_args++)
2759     ;
2760   args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
2761   args_native[num_args] = NULL;
2762   while (num_args--)
2763     {
2764       /* ### Well, it turns out that on APR on Windows expects all
2765              program args to be in UTF-8. Callers of svn_io_run_cmd
2766              should be aware of that. */
2767       SVN_ERR(cstring_from_utf8(&args_native[num_args],
2768                                 args[num_args], pool));
2769     }
2770
2771
2772   /* Start the cmd command. */
2773   apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
2774                             inherit ? NULL : env, cmdproc_attr, pool);
2775   if (apr_err)
2776     return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
2777
2778   return SVN_NO_ERROR;
2779 }
2780
2781 #undef ERRFILE_KEY
2782
2783 svn_error_t *
2784 svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
2785                     const char *cmd,
2786                     int *exitcode,
2787                     apr_exit_why_e *exitwhy,
2788                     apr_pool_t *pool)
2789 {
2790   apr_status_t apr_err;
2791   apr_exit_why_e exitwhy_val;
2792   int exitcode_val;
2793
2794   /* The Win32 apr_proc_wait doesn't set this... */
2795   exitwhy_val = APR_PROC_EXIT;
2796
2797   /* Wait for the cmd command to finish. */
2798   apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT);
2799   if (!APR_STATUS_IS_CHILD_DONE(apr_err))
2800     return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"),
2801                               cmd);
2802
2803   if (exitwhy)
2804     *exitwhy = exitwhy_val;
2805   else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)
2806            && APR_PROC_CHECK_CORE_DUMP(exitwhy_val))
2807     return svn_error_createf
2808       (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2809        _("Process '%s' failed (signal %d, core dumped)"),
2810        cmd, exitcode_val);
2811   else if (APR_PROC_CHECK_SIGNALED(exitwhy_val))
2812     return svn_error_createf
2813       (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2814        _("Process '%s' failed (signal %d)"),
2815        cmd, exitcode_val);
2816   else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
2817     /* Don't really know what happened here. */
2818     return svn_error_createf
2819       (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2820        _("Process '%s' failed (exitwhy %d, exitcode %d)"),
2821        cmd, exitwhy_val, exitcode_val);
2822
2823   if (exitcode)
2824     *exitcode = exitcode_val;
2825   else if (exitcode_val != 0)
2826     return svn_error_createf
2827       (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2828        _("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
2829
2830   return SVN_NO_ERROR;
2831 }
2832
2833
2834 svn_error_t *
2835 svn_io_run_cmd(const char *path,
2836                const char *cmd,
2837                const char *const *args,
2838                int *exitcode,
2839                apr_exit_why_e *exitwhy,
2840                svn_boolean_t inherit,
2841                apr_file_t *infile,
2842                apr_file_t *outfile,
2843                apr_file_t *errfile,
2844                apr_pool_t *pool)
2845 {
2846   apr_proc_t cmd_proc;
2847
2848   SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
2849                             FALSE, infile, FALSE, outfile, FALSE, errfile,
2850                             pool));
2851
2852   return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
2853 }
2854
2855
2856 svn_error_t *
2857 svn_io_run_diff2(const char *dir,
2858                  const char *const *user_args,
2859                  int num_user_args,
2860                  const char *label1,
2861                  const char *label2,
2862                  const char *from,
2863                  const char *to,
2864                  int *pexitcode,
2865                  apr_file_t *outfile,
2866                  apr_file_t *errfile,
2867                  const char *diff_cmd,
2868                  apr_pool_t *pool)
2869 {
2870   const char **args;
2871   int i;
2872   int exitcode;
2873   int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
2874   apr_pool_t *subpool = svn_pool_create(pool);
2875
2876   if (pexitcode == NULL)
2877     pexitcode = &exitcode;
2878
2879   if (user_args != NULL)
2880     nargs += num_user_args;
2881   else
2882     nargs += 1; /* -u */
2883
2884   if (label1 != NULL)
2885     nargs += 2; /* the -L and the label itself */
2886   if (label2 != NULL)
2887     nargs += 2; /* the -L and the label itself */
2888
2889   args = apr_palloc(subpool, nargs * sizeof(char *));
2890
2891   i = 0;
2892   args[i++] = diff_cmd;
2893
2894   if (user_args != NULL)
2895     {
2896       int j;
2897       for (j = 0; j < num_user_args; ++j)
2898         args[i++] = user_args[j];
2899     }
2900   else
2901     args[i++] = "-u"; /* assume -u if the user didn't give us any args */
2902
2903   if (label1 != NULL)
2904     {
2905       args[i++] = "-L";
2906       args[i++] = label1;
2907     }
2908   if (label2 != NULL)
2909     {
2910       args[i++] = "-L";
2911       args[i++] = label2;
2912     }
2913
2914   args[i++] = svn_dirent_local_style(from, subpool);
2915   args[i++] = svn_dirent_local_style(to, subpool);
2916   args[i++] = NULL;
2917
2918   SVN_ERR_ASSERT(i == nargs);
2919
2920   SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
2921                          NULL, outfile, errfile, subpool));
2922
2923   /* The man page for (GNU) diff describes the return value as:
2924
2925        "An exit status of 0 means no differences were found, 1 means
2926         some differences were found, and 2 means trouble."
2927
2928      A return value of 2 typically occurs when diff cannot read its input
2929      or write to its output, but in any case we probably ought to return an
2930      error for anything other than 0 or 1 as the output is likely to be
2931      corrupt.
2932    */
2933   if (*pexitcode != 0 && *pexitcode != 1)
2934     return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
2935                              _("'%s' returned %d"),
2936                              svn_dirent_local_style(diff_cmd, pool),
2937                              *pexitcode);
2938
2939   svn_pool_destroy(subpool);
2940
2941   return SVN_NO_ERROR;
2942 }
2943
2944
2945 svn_error_t *
2946 svn_io_run_diff3_3(int *exitcode,
2947                    const char *dir,
2948                    const char *mine,
2949                    const char *older,
2950                    const char *yours,
2951                    const char *mine_label,
2952                    const char *older_label,
2953                    const char *yours_label,
2954                    apr_file_t *merged,
2955                    const char *diff3_cmd,
2956                    const apr_array_header_t *user_args,
2957                    apr_pool_t *pool)
2958 {
2959   const char **args = apr_palloc(pool,
2960                                  sizeof(char*) * (13
2961                                                   + (user_args
2962                                                      ? user_args->nelts
2963                                                      : 1)));
2964 #ifndef NDEBUG
2965   int nargs = 12;
2966 #endif
2967   int i = 0;
2968
2969   /* Labels fall back to sensible defaults if not specified. */
2970   if (mine_label == NULL)
2971     mine_label = ".working";
2972   if (older_label == NULL)
2973     older_label = ".old";
2974   if (yours_label == NULL)
2975     yours_label = ".new";
2976
2977   /* Set up diff3 command line. */
2978   args[i++] = diff3_cmd;
2979   if (user_args)
2980     {
2981       int j;
2982       for (j = 0; j < user_args->nelts; ++j)
2983         args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
2984 #ifndef NDEBUG
2985       nargs += user_args->nelts;
2986 #endif
2987     }
2988   else
2989     {
2990       args[i++] = "-E";             /* We tried "-A" here, but that caused
2991                                        overlapping identical changes to
2992                                        conflict.  See issue #682. */
2993 #ifndef NDEBUG
2994       ++nargs;
2995 #endif
2996     }
2997   args[i++] = "-m";
2998   args[i++] = "-L";
2999   args[i++] = mine_label;
3000   args[i++] = "-L";
3001   args[i++] = older_label;      /* note:  this label is ignored if
3002                                    using 2-part markers, which is the
3003                                    case with "-E". */
3004   args[i++] = "-L";
3005   args[i++] = yours_label;
3006 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
3007   {
3008     svn_boolean_t has_arg;
3009
3010     /* ### FIXME: we really shouldn't be reading the config here;
3011        instead, the necessary bits should be passed in by the caller.
3012        But should we add another parameter to this function, when the
3013        whole external diff3 thing might eventually go away?  */
3014     apr_hash_t *config;
3015     svn_config_t *cfg;
3016
3017     SVN_ERR(svn_config_get_config(&config, pool));
3018     cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
3019     SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS,
3020                                 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG,
3021                                 TRUE));
3022     if (has_arg)
3023       {
3024         const char *diff_cmd, *diff_utf8;
3025         svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
3026                        SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF);
3027         SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool));
3028         args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8, NULL);
3029 #ifndef NDEBUG
3030         ++nargs;
3031 #endif
3032       }
3033   }
3034 #endif
3035   args[i++] = svn_dirent_local_style(mine, pool);
3036   args[i++] = svn_dirent_local_style(older, pool);
3037   args[i++] = svn_dirent_local_style(yours, pool);
3038   args[i++] = NULL;
3039 #ifndef NDEBUG
3040   SVN_ERR_ASSERT(i == nargs);
3041 #endif
3042
3043   /* Run diff3, output the merged text into the scratch file. */
3044   SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
3045                          exitcode, NULL,
3046                          TRUE, /* keep environment */
3047                          NULL, merged, NULL,
3048                          pool));
3049
3050   /* According to the diff3 docs, a '0' means the merge was clean, and
3051      '1' means conflict markers were found.  Anything else is real
3052      error. */
3053   if ((*exitcode != 0) && (*exitcode != 1))
3054     return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3055                              _("Error running '%s':  exitcode was %d, "
3056                                "args were:"
3057                                "\nin directory '%s', basenames:\n%s\n%s\n%s"),
3058                              svn_dirent_local_style(diff3_cmd, pool),
3059                              *exitcode,
3060                              svn_dirent_local_style(dir, pool),
3061                              /* Don't call svn_path_local_style() on
3062                                 the basenames.  We don't want them to
3063                                 be absolute, and we don't need the
3064                                 separator conversion. */
3065                              mine, older, yours);
3066
3067   return SVN_NO_ERROR;
3068 }
3069
3070
3071 /* Canonicalize a string for hashing.  Modifies KEY in place. */
3072 static APR_INLINE char *
3073 fileext_tolower(char *key)
3074 {
3075   register char *p;
3076   for (p = key; *p != 0; ++p)
3077     *p = (char)apr_tolower(*p);
3078   return key;
3079 }
3080
3081
3082 svn_error_t *
3083 svn_io_parse_mimetypes_file(apr_hash_t **type_map,
3084                             const char *mimetypes_file,
3085                             apr_pool_t *pool)
3086 {
3087   svn_error_t *err = SVN_NO_ERROR;
3088   apr_hash_t *types = apr_hash_make(pool);
3089   svn_boolean_t eof = FALSE;
3090   svn_stringbuf_t *buf;
3091   apr_pool_t *subpool = svn_pool_create(pool);
3092   apr_file_t *types_file;
3093   svn_stream_t *mimetypes_stream;
3094
3095   SVN_ERR(svn_io_file_open(&types_file, mimetypes_file,
3096                            APR_READ, APR_OS_DEFAULT, pool));
3097   mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool);
3098
3099   while (1)
3100     {
3101       apr_array_header_t *tokens;
3102       const char *type;
3103
3104       svn_pool_clear(subpool);
3105
3106       /* Read a line. */
3107       if ((err = svn_stream_readline(mimetypes_stream, &buf,
3108                                      APR_EOL_STR, &eof, subpool)))
3109         break;
3110
3111       /* Only pay attention to non-empty, non-comment lines. */
3112       if (buf->len)
3113         {
3114           int i;
3115
3116           if (buf->data[0] == '#')
3117             continue;
3118
3119           /* Tokenize (into our return pool). */
3120           tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
3121           if (tokens->nelts < 2)
3122             continue;
3123
3124           /* The first token in a multi-token line is the media type.
3125              Subsequent tokens are filename extensions associated with
3126              that media type. */
3127           type = APR_ARRAY_IDX(tokens, 0, const char *);
3128           for (i = 1; i < tokens->nelts; i++)
3129             {
3130               /* We can safely address 'ext' as a non-const string because
3131                * we know svn_cstring_split() allocated it in 'pool' for us. */
3132               char *ext = APR_ARRAY_IDX(tokens, i, char *);
3133               fileext_tolower(ext);
3134               svn_hash_sets(types, ext, type);
3135             }
3136         }
3137       if (eof)
3138         break;
3139     }
3140   svn_pool_destroy(subpool);
3141
3142   /* If there was an error above, close the file (ignoring any error
3143      from *that*) and return the originally error. */
3144   if (err)
3145     {
3146       svn_error_clear(svn_stream_close(mimetypes_stream));
3147       return err;
3148     }
3149
3150   /* Close the stream (which closes the underlying file, too). */
3151   SVN_ERR(svn_stream_close(mimetypes_stream));
3152
3153   *type_map = types;
3154   return SVN_NO_ERROR;
3155 }
3156
3157
3158 svn_error_t *
3159 svn_io_detect_mimetype2(const char **mimetype,
3160                         const char *file,
3161                         apr_hash_t *mimetype_map,
3162                         apr_pool_t *pool)
3163 {
3164   static const char * const generic_binary = "application/octet-stream";
3165
3166   svn_node_kind_t kind;
3167   apr_file_t *fh;
3168   svn_error_t *err;
3169   unsigned char block[1024];
3170   apr_size_t amt_read = sizeof(block);
3171
3172   /* Default return value is NULL. */
3173   *mimetype = NULL;
3174
3175   /* If there is a mimetype_map provided, we'll first try to look up
3176      our file's extension in the map.  Failing that, we'll run the
3177      heuristic. */
3178   if (mimetype_map)
3179     {
3180       const char *type_from_map;
3181       char *path_ext; /* Can point to physical const memory but only when
3182                          svn_path_splitext sets it to "". */
3183       svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
3184       fileext_tolower(path_ext);
3185       if ((type_from_map = svn_hash_gets(mimetype_map, path_ext)))
3186         {
3187           *mimetype = type_from_map;
3188           return SVN_NO_ERROR;
3189         }
3190     }
3191
3192   /* See if this file even exists, and make sure it really is a file. */
3193   SVN_ERR(svn_io_check_path(file, &kind, pool));
3194   if (kind != svn_node_file)
3195     return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
3196                              _("Can't detect MIME type of non-file '%s'"),
3197                              svn_dirent_local_style(file, pool));
3198
3199   SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
3200
3201   /* Read a block of data from FILE. */
3202   err = svn_io_file_read(fh, block, &amt_read, pool);
3203   if (err && ! APR_STATUS_IS_EOF(err->apr_err))
3204     return err;
3205   svn_error_clear(err);
3206
3207   /* Now close the file.  No use keeping it open any more.  */
3208   SVN_ERR(svn_io_file_close(fh, pool));
3209
3210   if (svn_io_is_binary_data(block, amt_read))
3211     *mimetype = generic_binary;
3212
3213   return SVN_NO_ERROR;
3214 }
3215
3216
3217 svn_boolean_t
3218 svn_io_is_binary_data(const void *data, apr_size_t len)
3219 {
3220   const unsigned char *buf = data;
3221
3222   if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
3223     {
3224       /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
3225        * Treat it as plain text. */
3226       return FALSE;
3227     }
3228
3229   /* Right now, this function is going to be really stupid.  It's
3230      going to examine the block of data, and make sure that 15%
3231      of the bytes are such that their value is in the ranges 0x07-0x0D
3232      or 0x20-0x7F, and that none of those bytes is 0x00.  If those
3233      criteria are not met, we're calling it binary.
3234
3235      NOTE:  Originally, I intended to target 85% of the bytes being in
3236      the specified ranges, but I flubbed the condition.  At any rate,
3237      folks aren't complaining, so I'm not sure that it's worth
3238      adjusting this retroactively now.  --cmpilato  */
3239   if (len > 0)
3240     {
3241       apr_size_t i;
3242       apr_size_t binary_count = 0;
3243
3244       /* Run through the data we've read, counting the 'binary-ish'
3245          bytes.  HINT: If we see a 0x00 byte, we'll set our count to its
3246          max and stop reading the file. */
3247       for (i = 0; i < len; i++)
3248         {
3249           if (buf[i] == 0)
3250             {
3251               binary_count = len;
3252               break;
3253             }
3254           if ((buf[i] < 0x07)
3255               || ((buf[i] > 0x0D) && (buf[i] < 0x20))
3256               || (buf[i] > 0x7F))
3257             {
3258               binary_count++;
3259             }
3260         }
3261
3262       return (((binary_count * 1000) / len) > 850);
3263     }
3264
3265   return FALSE;
3266 }
3267
3268
3269 svn_error_t *
3270 svn_io_detect_mimetype(const char **mimetype,
3271                        const char *file,
3272                        apr_pool_t *pool)
3273 {
3274   return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
3275 }
3276
3277
3278 svn_error_t *
3279 svn_io_file_open(apr_file_t **new_file, const char *fname,
3280                  apr_int32_t flag, apr_fileperms_t perm,
3281                  apr_pool_t *pool)
3282 {
3283   const char *fname_apr;
3284   apr_status_t status;
3285
3286   SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3287   status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
3288                      pool);
3289
3290   if (status)
3291     return svn_error_wrap_apr(status, _("Can't open file '%s'"),
3292                               svn_dirent_local_style(fname, pool));
3293   else
3294     return SVN_NO_ERROR;
3295 }
3296
3297
3298 static APR_INLINE svn_error_t *
3299 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
3300                            const char *msg, const char *msg_no_name,
3301                            apr_pool_t *pool)
3302 {
3303   const char *name;
3304   svn_error_t *err;
3305
3306   if (! status)
3307     return SVN_NO_ERROR;
3308
3309   err = svn_io_file_name_get(&name, file, pool);
3310   if (err)
3311     name = NULL;
3312   svn_error_clear(err);
3313
3314   /* ### Issue #3014: Return a specific error for broken pipes,
3315    * ### with a single element in the error chain. */
3316   if (SVN__APR_STATUS_IS_EPIPE(status))
3317     return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
3318
3319   if (name)
3320     return svn_error_wrap_apr(status, _(msg),
3321                               try_utf8_from_internal_style(name, pool));
3322   else
3323     return svn_error_wrap_apr(status, "%s", _(msg_no_name));
3324 }
3325
3326
3327 svn_error_t *
3328 svn_io_file_close(apr_file_t *file, apr_pool_t *pool)
3329 {
3330   return do_io_file_wrapper_cleanup(file, apr_file_close(file),
3331                                     N_("Can't close file '%s'"),
3332                                     N_("Can't close stream"),
3333                                     pool);
3334 }
3335
3336
3337 svn_error_t *
3338 svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
3339 {
3340   return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file),
3341                                     N_("Can't read file '%s'"),
3342                                     N_("Can't read stream"),
3343                                     pool);
3344 }
3345
3346
3347 svn_error_t *
3348 svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
3349 {
3350   return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file),
3351                                     N_("Can't write file '%s'"),
3352                                     N_("Can't write stream"),
3353                                     pool);
3354 }
3355
3356
3357 svn_error_t *
3358 svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
3359                      apr_file_t *file, apr_pool_t *pool)
3360 {
3361   /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3362   wanted &= ~SVN__APR_FINFO_MASK_OUT;
3363
3364   return do_io_file_wrapper_cleanup(
3365              file, apr_file_info_get(finfo, wanted, file),
3366              N_("Can't get attribute information from file '%s'"),
3367              N_("Can't get attribute information from stream"),
3368              pool);
3369 }
3370
3371
3372 svn_error_t *
3373 svn_io_file_read(apr_file_t *file, void *buf,
3374                  apr_size_t *nbytes, apr_pool_t *pool)
3375 {
3376   return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes),
3377                                     N_("Can't read file '%s'"),
3378                                     N_("Can't read stream"),
3379                                     pool);
3380 }
3381
3382
3383 svn_error_t *
3384 svn_io_file_read_full2(apr_file_t *file, void *buf,
3385                        apr_size_t nbytes, apr_size_t *bytes_read,
3386                        svn_boolean_t *hit_eof,
3387                        apr_pool_t *pool)
3388 {
3389   apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
3390   if (hit_eof)
3391     {
3392       if (APR_STATUS_IS_EOF(status))
3393         {
3394           *hit_eof = TRUE;
3395           return SVN_NO_ERROR;
3396         }
3397       else
3398         *hit_eof = FALSE;
3399     }
3400
3401   return do_io_file_wrapper_cleanup(file, status,
3402                                     N_("Can't read file '%s'"),
3403                                     N_("Can't read stream"),
3404                                     pool);
3405 }
3406
3407
3408 svn_error_t *
3409 svn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
3410                  apr_off_t *offset, apr_pool_t *pool)
3411 {
3412   return do_io_file_wrapper_cleanup(
3413              file, apr_file_seek(file, where, offset),
3414              N_("Can't set position pointer in file '%s'"),
3415              N_("Can't set position pointer in stream"),
3416              pool);
3417 }
3418
3419
3420 svn_error_t *
3421 svn_io_file_write(apr_file_t *file, const void *buf,
3422                   apr_size_t *nbytes, apr_pool_t *pool)
3423 {
3424   return svn_error_trace(do_io_file_wrapper_cleanup(
3425      file, apr_file_write(file, buf, nbytes),
3426      N_("Can't write to file '%s'"),
3427      N_("Can't write to stream"),
3428      pool));
3429 }
3430
3431
3432 svn_error_t *
3433 svn_io_file_write_full(apr_file_t *file, const void *buf,
3434                        apr_size_t nbytes, apr_size_t *bytes_written,
3435                        apr_pool_t *pool)
3436 {
3437   /* We cannot simply call apr_file_write_full on Win32 as it may fail
3438      for larger values of NBYTES. In that case, we have to emulate the
3439      "_full" part here. Thus, always call apr_file_write directly on
3440      Win32 as this minimizes overhead for small data buffers. */
3441 #ifdef WIN32
3442 #define MAXBUFSIZE 30*1024
3443   apr_size_t bw = nbytes;
3444   apr_size_t to_write = nbytes;
3445
3446   /* try a simple "write everything at once" first */
3447   apr_status_t rv = apr_file_write(file, buf, &bw);
3448   buf = (char *)buf + bw;
3449   to_write -= bw;
3450
3451   /* if the OS cannot handle that, use smaller chunks */
3452   if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
3453       && nbytes > MAXBUFSIZE)
3454     {
3455       do {
3456         bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
3457         rv = apr_file_write(file, buf, &bw);
3458         buf = (char *)buf + bw;
3459         to_write -= bw;
3460       } while (rv == APR_SUCCESS && to_write > 0);
3461     }
3462
3463   /* bytes_written may actually be NULL */
3464   if (bytes_written)
3465     *bytes_written = nbytes - to_write;
3466 #undef MAXBUFSIZE
3467 #else
3468   apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
3469 #endif
3470
3471   return svn_error_trace(do_io_file_wrapper_cleanup(
3472      file, rv,
3473      N_("Can't write to file '%s'"),
3474      N_("Can't write to stream"),
3475      pool));
3476 }
3477
3478
3479 svn_error_t *
3480 svn_io_write_unique(const char **tmp_path,
3481                     const char *dirpath,
3482                     const void *buf,
3483                     apr_size_t nbytes,
3484                     svn_io_file_del_t delete_when,
3485                     apr_pool_t *pool)
3486 {
3487   apr_file_t *new_file;
3488   svn_error_t *err;
3489
3490   SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
3491                                    delete_when, pool, pool));
3492
3493   err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
3494
3495   if (!err)
3496     err = svn_io_file_flush_to_disk(new_file, pool);
3497
3498   return svn_error_trace(
3499                   svn_error_compose_create(err,
3500                                            svn_io_file_close(new_file, pool)));
3501 }
3502
3503
3504 svn_error_t *
3505 svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
3506 {
3507   /* This is a work-around. APR would flush the write buffer
3508      _after_ truncating the file causing now invalid buffered
3509      data to be written behind OFFSET. */
3510   SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
3511                                      N_("Can't flush file '%s'"),
3512                                      N_("Can't flush stream"),
3513                                      pool));
3514
3515   return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset),
3516                                     N_("Can't truncate file '%s'"),
3517                                     N_("Can't truncate stream"),
3518                                     pool);
3519 }
3520
3521
3522 svn_error_t *
3523 svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
3524                         apr_pool_t *pool)
3525 {
3526   /* variables */
3527   apr_size_t total_read = 0;
3528   svn_boolean_t eof = FALSE;
3529   const char *name;
3530   svn_error_t *err;
3531   apr_size_t buf_size = *limit;
3532
3533   while (buf_size > 0)
3534     {
3535       /* read a fair chunk of data at once. But don't get too ambitious
3536        * as that would result in too much waste. Also make sure we can
3537        * put a NUL after the last byte read.
3538        */
3539       apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
3540       apr_size_t bytes_read = 0;
3541       char *eol;
3542
3543       if (to_read == 0)
3544         break;
3545
3546       /* read data block (or just a part of it) */
3547       SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
3548                                      &bytes_read, &eof, pool));
3549
3550       /* look or a newline char */
3551       buf[bytes_read] = 0;
3552       eol = strchr(buf, '\n');
3553       if (eol)
3554         {
3555           apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
3556
3557           *eol = 0;
3558           *limit = total_read + (eol - buf);
3559
3560           /* correct the file pointer:
3561            * appear as though we just had read the newline char
3562            */
3563           SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
3564
3565           return SVN_NO_ERROR;
3566         }
3567       else if (eof)
3568         {
3569           /* no EOL found but we hit the end of the file.
3570            * Generate a nice EOF error object and return it.
3571            */
3572           char dummy;
3573           SVN_ERR(svn_io_file_getc(&dummy, file, pool));
3574         }
3575
3576       /* next data chunk */
3577       buf_size -= bytes_read;
3578       buf += bytes_read;
3579       total_read += bytes_read;
3580     }
3581
3582   /* buffer limit has been exceeded without finding the EOL */
3583   err = svn_io_file_name_get(&name, file, pool);
3584   if (err)
3585     name = NULL;
3586   svn_error_clear(err);
3587
3588   if (name)
3589     return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
3590                              _("Can't read length line in file '%s'"),
3591                              svn_dirent_local_style(name, pool));
3592   else
3593     return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
3594                             _("Can't read length line in stream"));
3595 }
3596
3597
3598 svn_error_t *
3599 svn_io_stat(apr_finfo_t *finfo, const char *fname,
3600             apr_int32_t wanted, apr_pool_t *pool)
3601 {
3602   apr_status_t status;
3603   const char *fname_apr;
3604
3605   /* APR doesn't like "" directories */
3606   if (fname[0] == '\0')
3607     fname = ".";
3608
3609   SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3610
3611   /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3612   wanted &= ~SVN__APR_FINFO_MASK_OUT;
3613
3614   status = apr_stat(finfo, fname_apr, wanted, pool);
3615   if (status)
3616     return svn_error_wrap_apr(status, _("Can't stat '%s'"),
3617                               svn_dirent_local_style(fname, pool));
3618
3619   return SVN_NO_ERROR;
3620 }
3621
3622
3623 svn_error_t *
3624 svn_io_file_rename(const char *from_path, const char *to_path,
3625                    apr_pool_t *pool)
3626 {
3627   apr_status_t status = APR_SUCCESS;
3628   const char *from_path_apr, *to_path_apr;
3629
3630   SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
3631   SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
3632
3633   status = apr_file_rename(from_path_apr, to_path_apr, pool);
3634
3635 #if defined(WIN32) || defined(__OS2__)
3636   /* If the target file is read only NTFS reports EACCESS and
3637      FAT/FAT32 reports EEXIST */
3638   if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
3639     {
3640       /* Set the destination file writable because Windows will not
3641          allow us to rename when to_path is read-only, but will
3642          allow renaming when from_path is read only. */
3643       SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
3644
3645       status = apr_file_rename(from_path_apr, to_path_apr, pool);
3646     }
3647   WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool));
3648 #endif /* WIN32 || __OS2__ */
3649
3650   if (status)
3651     return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
3652                               svn_dirent_local_style(from_path, pool),
3653                               svn_dirent_local_style(to_path, pool));
3654
3655   return SVN_NO_ERROR;
3656 }
3657
3658
3659 svn_error_t *
3660 svn_io_file_move(const char *from_path, const char *to_path,
3661                  apr_pool_t *pool)
3662 {
3663   svn_error_t *err = svn_io_file_rename(from_path, to_path, pool);
3664
3665   if (err && APR_STATUS_IS_EXDEV(err->apr_err))
3666     {
3667       const char *tmp_to_path;
3668
3669       svn_error_clear(err);
3670
3671       SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_to_path,
3672                                        svn_dirent_dirname(to_path, pool),
3673                                        svn_io_file_del_none,
3674                                        pool, pool));
3675
3676       err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool);
3677       if (err)
3678         goto failed_tmp;
3679
3680       err = svn_io_file_rename(tmp_to_path, to_path, pool);
3681       if (err)
3682         goto failed_tmp;
3683
3684       err = svn_io_remove_file2(from_path, FALSE, pool);
3685       if (! err)
3686         return SVN_NO_ERROR;
3687
3688       svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool));
3689
3690       return err;
3691
3692     failed_tmp:
3693       svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool));
3694     }
3695
3696   return err;
3697 }
3698
3699 /* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
3700    HIDDEN determines if the hidden attribute
3701    should be set on the newly created directory. */
3702 static svn_error_t *
3703 dir_make(const char *path, apr_fileperms_t perm,
3704          svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
3705 {
3706   apr_status_t status;
3707   const char *path_apr;
3708
3709   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
3710
3711   /* APR doesn't like "" directories */
3712   if (path_apr[0] == '\0')
3713     path_apr = ".";
3714
3715 #if (APR_OS_DEFAULT & APR_WSTICKY)
3716   /* The APR shipped with httpd 2.0.50 contains a bug where
3717      APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
3718      There is a special case for file creation, but not directory
3719      creation, so directories wind up getting created with the sticky
3720      bit set.  (There is no such thing as a setuid directory, and the
3721      setgid bit is apparently ignored at mkdir() time.)  If we detect
3722      this problem, work around it by unsetting those bits if we are
3723      passed APR_OS_DEFAULT. */
3724   if (perm == APR_OS_DEFAULT)
3725     perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY);
3726 #endif
3727
3728   status = apr_dir_make(path_apr, perm, pool);
3729   WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
3730
3731   if (status)
3732     return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
3733                               svn_dirent_local_style(path, pool));
3734
3735 #ifdef APR_FILE_ATTR_HIDDEN
3736   if (hidden)
3737     {
3738 #ifndef WIN32
3739       status = apr_file_attrs_set(path_apr,
3740                                   APR_FILE_ATTR_HIDDEN,
3741                                   APR_FILE_ATTR_HIDDEN,
3742                                   pool);
3743 #else
3744     /* on Windows, use our wrapper so we can also set the
3745        FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */
3746     status = io_win_file_attrs_set(path_apr,
3747                                    FILE_ATTRIBUTE_HIDDEN |
3748                                    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
3749                                    FILE_ATTRIBUTE_HIDDEN |
3750                                    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
3751                                    pool);
3752
3753 #endif
3754       if (status)
3755         return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
3756                                   svn_dirent_local_style(path, pool));
3757     }
3758 #endif
3759
3760 /* Windows does not implement sgid. Skip here because retrieving
3761    the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented
3762    to be 'incredibly expensive'. */
3763 #ifndef WIN32
3764   if (sgid)
3765     {
3766       apr_finfo_t finfo;
3767
3768       /* Per our contract, don't do error-checking.  Some filesystems
3769        * don't support the sgid bit, and that's okay. */
3770       status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool);
3771
3772       if (!status)
3773         apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
3774     }
3775 #endif
3776
3777   return SVN_NO_ERROR;
3778 }
3779
3780 svn_error_t *
3781 svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
3782 {
3783   return dir_make(path, perm, FALSE, FALSE, pool);
3784 }
3785
3786 svn_error_t *
3787 svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
3788                        apr_pool_t *pool)
3789 {
3790   return dir_make(path, perm, TRUE, FALSE, pool);
3791 }
3792
3793 svn_error_t *
3794 svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
3795                      apr_pool_t *pool)
3796 {
3797   return dir_make(path, perm, FALSE, TRUE, pool);
3798 }
3799
3800
3801 svn_error_t *
3802 svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
3803 {
3804   apr_status_t status;
3805   const char *dirname_apr;
3806
3807   /* APR doesn't like "" directories */
3808   if (dirname[0] == '\0')
3809     dirname = ".";
3810
3811   SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3812
3813   status = apr_dir_open(new_dir, dirname_apr, pool);
3814   if (status)
3815     return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
3816                               svn_dirent_local_style(dirname, pool));
3817
3818   return SVN_NO_ERROR;
3819 }
3820
3821 svn_error_t *
3822 svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
3823 {
3824   apr_status_t status;
3825   const char *dirname_apr;
3826
3827   SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3828
3829   status = apr_dir_remove(dirname_apr, pool);
3830
3831 #ifdef WIN32
3832   {
3833     svn_boolean_t retry = TRUE;
3834
3835     if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY)
3836       {
3837         apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
3838
3839         if (APR_STATUS_IS_ENOTEMPTY(empty_status))
3840           retry = FALSE;
3841       }
3842
3843     if (retry)
3844       {
3845         WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
3846       }
3847   }
3848 #endif
3849   if (status)
3850     return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
3851                               svn_dirent_local_style(dirname, pool));
3852
3853   return SVN_NO_ERROR;
3854 }
3855
3856
3857 svn_error_t *
3858 svn_io_dir_read(apr_finfo_t *finfo,
3859                 apr_int32_t wanted,
3860                 apr_dir_t *thedir,
3861                 apr_pool_t *pool)
3862 {
3863   apr_status_t status;
3864
3865   status = apr_dir_read(finfo, wanted, thedir);
3866
3867   if (status)
3868     return svn_error_wrap_apr(status, _("Can't read directory"));
3869
3870   /* It would be nice to use entry_name_to_utf8() below, but can we
3871      get the dir's path out of an apr_dir_t?  I don't see a reliable
3872      way to do it. */
3873
3874   if (finfo->fname)
3875     SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
3876
3877   if (finfo->name)
3878     SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
3879
3880   return SVN_NO_ERROR;
3881 }
3882
3883 svn_error_t *
3884 svn_io_dir_close(apr_dir_t *thedir)
3885 {
3886   apr_status_t apr_err = apr_dir_close(thedir);
3887   if (apr_err)
3888     return svn_error_wrap_apr(apr_err, _("Error closing directory"));
3889
3890   return SVN_NO_ERROR;
3891 }
3892
3893 svn_error_t *
3894 svn_io_dir_walk2(const char *dirname,
3895                  apr_int32_t wanted,
3896                  svn_io_walk_func_t walk_func,
3897                  void *walk_baton,
3898                  apr_pool_t *pool)
3899 {
3900   apr_status_t apr_err;
3901   apr_dir_t *handle;
3902   apr_pool_t *subpool;
3903   const char *dirname_apr;
3904   apr_finfo_t finfo;
3905
3906   wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
3907
3908   /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3909   wanted &= ~SVN__APR_FINFO_MASK_OUT;
3910
3911   /* The documentation for apr_dir_read used to state that "." and ".."
3912      will be returned as the first two files, but it doesn't
3913      work that way in practice, in particular ext3 on Linux-2.6 doesn't
3914      follow the rules.  For details see
3915      http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
3916
3917      If APR ever does implement "dot-first" then it would be possible to
3918      remove the svn_io_stat and walk_func calls and use the walk_func
3919      inside the loop.
3920
3921      Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
3922      documented to provide it, so we have to do a bit extra. */
3923   SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool));
3924   SVN_ERR(cstring_from_utf8(&finfo.name,
3925                             svn_dirent_basename(dirname, pool),
3926                             pool));
3927   finfo.valid |= APR_FINFO_NAME;
3928   SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
3929
3930   SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3931
3932   /* APR doesn't like "" directories */
3933   if (dirname_apr[0] == '\0')
3934     dirname_apr = ".";
3935
3936   apr_err = apr_dir_open(&handle, dirname_apr, pool);
3937   if (apr_err)
3938     return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
3939                               svn_dirent_local_style(dirname, pool));
3940
3941   /* iteration subpool */
3942   subpool = svn_pool_create(pool);
3943
3944   while (1)
3945     {
3946       const char *name_utf8;
3947       const char *full_path;
3948
3949       svn_pool_clear(subpool);
3950
3951       apr_err = apr_dir_read(&finfo, wanted, handle);
3952       if (APR_STATUS_IS_ENOENT(apr_err))
3953         break;
3954       else if (apr_err)
3955         {
3956           return svn_error_wrap_apr(apr_err,
3957                                     _("Can't read directory entry in '%s'"),
3958                                     svn_dirent_local_style(dirname, pool));
3959         }
3960
3961       if (finfo.filetype == APR_DIR)
3962         {
3963           if (finfo.name[0] == '.'
3964               && (finfo.name[1] == '\0'
3965                   || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
3966             /* skip "." and ".." */
3967             continue;
3968
3969           /* some other directory. recurse. it will be passed to the
3970              callback inside the recursion. */
3971           SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
3972                                      subpool));
3973           full_path = svn_dirent_join(dirname, name_utf8, subpool);
3974           SVN_ERR(svn_io_dir_walk2(full_path,
3975                                    wanted,
3976                                    walk_func,
3977                                    walk_baton,
3978                                    subpool));
3979         }
3980       else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
3981         {
3982           /* some other directory. pass it to the callback. */
3983           SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
3984                                      subpool));
3985           full_path = svn_dirent_join(dirname, name_utf8, subpool);
3986           SVN_ERR((*walk_func)(walk_baton,
3987                                full_path,
3988                                &finfo,
3989                                subpool));
3990         }
3991       /* else:
3992          Some other type of file; skip it for now.  We've reserved the
3993          right to expand our coverage here in the future, though,
3994          without revving this API.
3995       */
3996     }
3997
3998   svn_pool_destroy(subpool);
3999
4000   apr_err = apr_dir_close(handle);
4001   if (apr_err)
4002     return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
4003                               svn_dirent_local_style(dirname, pool));
4004
4005   return SVN_NO_ERROR;
4006 }
4007
4008
4009 \f
4010 /**
4011  * Determine if a directory is empty or not.
4012  * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
4013  * @param path The directory.
4014  * @param pool Used for temporary allocation.
4015  * @remark If path is not a directory, or some other error occurs,
4016  * then return the appropriate apr status code.
4017  *
4018  * (This function is written in APR style, in anticipation of
4019  * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
4020  */
4021 static apr_status_t
4022 dir_is_empty(const char *dir, apr_pool_t *pool)
4023 {
4024   apr_status_t apr_err;
4025   apr_dir_t *dir_handle;
4026   apr_finfo_t finfo;
4027   apr_status_t retval = APR_SUCCESS;
4028
4029   /* APR doesn't like "" directories */
4030   if (dir[0] == '\0')
4031     dir = ".";
4032
4033   apr_err = apr_dir_open(&dir_handle, dir, pool);
4034   if (apr_err != APR_SUCCESS)
4035     return apr_err;
4036
4037   for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle);
4038        apr_err == APR_SUCCESS;
4039        apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle))
4040     {
4041       /* Ignore entries for this dir and its parent, robustly.
4042          (APR promises that they'll come first, so technically
4043          this guard could be moved outside the loop.  But Ryan Bloom
4044          says he doesn't believe it, and I believe him. */
4045       if (! (finfo.name[0] == '.'
4046              && (finfo.name[1] == '\0'
4047                  || (finfo.name[1] == '.' && finfo.name[2] == '\0'))))
4048         {
4049           retval = APR_ENOTEMPTY;
4050           break;
4051         }
4052     }
4053
4054   /* Make sure we broke out of the loop for the right reason. */
4055   if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
4056     return apr_err;
4057
4058   apr_err = apr_dir_close(dir_handle);
4059   if (apr_err != APR_SUCCESS)
4060     return apr_err;
4061
4062   return retval;
4063 }
4064
4065
4066 svn_error_t *
4067 svn_io_dir_empty(svn_boolean_t *is_empty_p,
4068                  const char *path,
4069                  apr_pool_t *pool)
4070 {
4071   apr_status_t status;
4072   const char *path_apr;
4073
4074   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4075
4076   status = dir_is_empty(path_apr, pool);
4077
4078   if (!status)
4079     *is_empty_p = TRUE;
4080   else if (APR_STATUS_IS_ENOTEMPTY(status))
4081     *is_empty_p = FALSE;
4082   else
4083     return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
4084                               svn_dirent_local_style(path, pool));
4085
4086   return SVN_NO_ERROR;
4087 }
4088
4089
4090 \f
4091 /*** Version/format files ***/
4092
4093 svn_error_t *
4094 svn_io_write_version_file(const char *path,
4095                           int version,
4096                           apr_pool_t *pool)
4097 {
4098   const char *path_tmp;
4099   const char *format_contents = apr_psprintf(pool, "%d\n", version);
4100
4101   SVN_ERR_ASSERT(version >= 0);
4102
4103   SVN_ERR(svn_io_write_unique(&path_tmp,
4104                               svn_dirent_dirname(path, pool),
4105                               format_contents, strlen(format_contents),
4106                               svn_io_file_del_none, pool));
4107
4108 #if defined(WIN32) || defined(__OS2__)
4109   /* make the destination writable, but only on Windows, because
4110      Windows does not let us replace read-only files. */
4111   SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
4112 #endif /* WIN32 || __OS2__ */
4113
4114   /* rename the temp file as the real destination */
4115   SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
4116
4117   /* And finally remove the perms to make it read only */
4118   return svn_io_set_file_read_only(path, FALSE, pool);
4119 }
4120
4121
4122 svn_error_t *
4123 svn_io_read_version_file(int *version,
4124                          const char *path,
4125                          apr_pool_t *pool)
4126 {
4127   apr_file_t *format_file;
4128   char buf[80];
4129   apr_size_t len;
4130   svn_error_t *err;
4131
4132   /* Read a chunk of data from PATH */
4133   SVN_ERR(svn_io_file_open(&format_file, path, APR_READ,
4134                            APR_OS_DEFAULT, pool));
4135   len = sizeof(buf);
4136   err = svn_io_file_read(format_file, buf, &len, pool);
4137
4138   /* Close the file. */
4139   SVN_ERR(svn_error_compose_create(err,
4140                                    svn_io_file_close(format_file, pool)));
4141
4142   /* If there was no data in PATH, return an error. */
4143   if (len == 0)
4144     return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
4145                              _("Reading '%s'"),
4146                              svn_dirent_local_style(path, pool));
4147
4148   /* Check that the first line contains only digits. */
4149   {
4150     apr_size_t i;
4151
4152     for (i = 0; i < len; ++i)
4153       {
4154         char c = buf[i];
4155
4156         if (i > 0 && (c == '\r' || c == '\n'))
4157           {
4158             buf[i] = '\0';
4159             break;
4160           }
4161         if (! svn_ctype_isdigit(c))
4162           return svn_error_createf
4163             (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
4164              _("First line of '%s' contains non-digit"),
4165              svn_dirent_local_style(path, pool));
4166       }
4167   }
4168
4169   /* Convert to integer. */
4170   SVN_ERR(svn_cstring_atoi(version, buf));
4171
4172   return SVN_NO_ERROR;
4173 }
4174
4175
4176
4177 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
4178 static svn_error_t *
4179 contents_identical_p(svn_boolean_t *identical_p,
4180                      const char *file1,
4181                      const char *file2,
4182                      apr_pool_t *pool)
4183 {
4184   svn_error_t *err;
4185   apr_size_t bytes_read1, bytes_read2;
4186   char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4187   char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4188   apr_file_t *file1_h;
4189   apr_file_t *file2_h;
4190   svn_boolean_t eof1 = FALSE;
4191   svn_boolean_t eof2 = FALSE;
4192
4193   SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4194                            pool));
4195
4196   err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4197                          pool);
4198
4199   if (err)
4200     return svn_error_trace(
4201                svn_error_compose_create(err,
4202                                         svn_io_file_close(file1_h, pool)));
4203
4204   *identical_p = TRUE;  /* assume TRUE, until disproved below */
4205   while (!err && !eof1 && !eof2)
4206     {
4207       err = svn_io_file_read_full2(file1_h, buf1,
4208                                    SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4209                                    &eof1, pool);
4210       if (err)
4211           break;
4212
4213       err = svn_io_file_read_full2(file2_h, buf2,
4214                                    SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4215                                    &eof2, pool);
4216       if (err)
4217           break;
4218
4219       if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
4220         {
4221           *identical_p = FALSE;
4222           break;
4223         }
4224     }
4225
4226   /* Special case: one file being a prefix of the other and the shorter
4227    * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */
4228   if (!err && (eof1 != eof2))
4229     *identical_p = FALSE;
4230
4231   return svn_error_trace(
4232            svn_error_compose_create(
4233                 err,
4234                 svn_error_compose_create(svn_io_file_close(file1_h, pool),
4235                                          svn_io_file_close(file2_h, pool))));
4236 }
4237
4238
4239
4240 /* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
4241 static svn_error_t *
4242 contents_three_identical_p(svn_boolean_t *identical_p12,
4243                            svn_boolean_t *identical_p23,
4244                            svn_boolean_t *identical_p13,
4245                            const char *file1,
4246                            const char *file2,
4247                            const char *file3,
4248                            apr_pool_t *scratch_pool)
4249 {
4250   svn_error_t *err;
4251   apr_size_t bytes_read1, bytes_read2, bytes_read3;
4252   char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4253   char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4254   char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4255   apr_file_t *file1_h;
4256   apr_file_t *file2_h;
4257   apr_file_t *file3_h;
4258   svn_boolean_t eof1 = FALSE;
4259   svn_boolean_t eof2 = FALSE;
4260   svn_boolean_t eof3 = FALSE;
4261   svn_boolean_t read_1, read_2, read_3;
4262
4263   SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4264                            scratch_pool));
4265
4266   err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4267                          scratch_pool);
4268
4269   if (err)
4270     return svn_error_trace(
4271                svn_error_compose_create(err,
4272                                         svn_io_file_close(file1_h, scratch_pool)));
4273
4274   err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
4275                          scratch_pool);
4276
4277   if (err)
4278       return svn_error_trace(
4279                svn_error_compose_create(
4280                     err,
4281                     svn_error_compose_create(svn_io_file_close(file1_h,
4282                                                           scratch_pool),
4283                                              svn_io_file_close(file2_h,
4284                                                           scratch_pool))));
4285
4286   /* assume TRUE, until disproved below */
4287   *identical_p12 = *identical_p23 = *identical_p13 = TRUE;
4288   /* We need to read as long as no error occurs, and as long as one of the
4289    * flags could still change due to a read operation */
4290   while (!err
4291         && ((*identical_p12 && !eof1 && !eof2)
4292             || (*identical_p23 && !eof2 && !eof3)
4293             || (*identical_p13 && !eof1 && !eof3)))
4294     {
4295       read_1 = read_2 = read_3 = FALSE;
4296
4297       /* As long as a file is not at the end yet, and it is still
4298        * potentially identical to another file, we read the next chunk.*/
4299       if (!eof1 && (*identical_p12 || *identical_p13))
4300         {
4301           err = svn_io_file_read_full2(file1_h, buf1,
4302                                    SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4303                                    &eof1, scratch_pool);
4304           if (err)
4305               break;
4306           read_1 = TRUE;
4307         }
4308
4309       if (!eof2 && (*identical_p12 || *identical_p23))
4310         {
4311           err = svn_io_file_read_full2(file2_h, buf2,
4312                                    SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4313                                    &eof2, scratch_pool);
4314           if (err)
4315               break;
4316           read_2 = TRUE;
4317         }
4318
4319       if (!eof3 && (*identical_p13 || *identical_p23))
4320         {
4321           err = svn_io_file_read_full2(file3_h, buf3,
4322                                    SVN__STREAM_CHUNK_SIZE, &bytes_read3,
4323                                    &eof3, scratch_pool);
4324           if (err)
4325               break;
4326           read_3 = TRUE;
4327         }
4328
4329       /* If the files are still marked identical, and at least one of them
4330        * is not at the end of file, we check whether they differ, and set
4331        * their flag to false then. */
4332       if (*identical_p12
4333           && (read_1 || read_2)
4334           && ((eof1 != eof2)
4335               || (bytes_read1 != bytes_read2)
4336               || memcmp(buf1, buf2, bytes_read1)))
4337         {
4338           *identical_p12 = FALSE;
4339         }
4340
4341       if (*identical_p23
4342           && (read_2 || read_3)
4343           && ((eof2 != eof3)
4344               || (bytes_read2 != bytes_read3)
4345               || memcmp(buf2, buf3, bytes_read2)))
4346         {
4347           *identical_p23 = FALSE;
4348         }
4349
4350       if (*identical_p13
4351           && (read_1 || read_3)
4352           && ((eof1 != eof3)
4353               || (bytes_read1 != bytes_read3)
4354               || memcmp(buf1, buf3, bytes_read3)))
4355         {
4356           *identical_p13 = FALSE;
4357         }
4358     }
4359
4360   return svn_error_trace(
4361            svn_error_compose_create(
4362                 err,
4363                 svn_error_compose_create(
4364                     svn_io_file_close(file1_h, scratch_pool),
4365                     svn_error_compose_create(
4366                         svn_io_file_close(file2_h, scratch_pool),
4367                         svn_io_file_close(file3_h, scratch_pool)))));
4368 }
4369
4370
4371
4372 svn_error_t *
4373 svn_io_files_contents_same_p(svn_boolean_t *same,
4374                              const char *file1,
4375                              const char *file2,
4376                              apr_pool_t *pool)
4377 {
4378   svn_boolean_t q;
4379
4380   SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
4381
4382   if (q)
4383     {
4384       *same = FALSE;
4385       return SVN_NO_ERROR;
4386     }
4387
4388   SVN_ERR(contents_identical_p(&q, file1, file2, pool));
4389
4390   if (q)
4391     *same = TRUE;
4392   else
4393     *same = FALSE;
4394
4395   return SVN_NO_ERROR;
4396 }
4397
4398 svn_error_t *
4399 svn_io_files_contents_three_same_p(svn_boolean_t *same12,
4400                                    svn_boolean_t *same23,
4401                                    svn_boolean_t *same13,
4402                                    const char *file1,
4403                                    const char *file2,
4404                                    const char *file3,
4405                                    apr_pool_t *scratch_pool)
4406 {
4407   svn_boolean_t diff_size12, diff_size23, diff_size13;
4408
4409   SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
4410                                              &diff_size23,
4411                                              &diff_size13,
4412                                              file1,
4413                                              file2,
4414                                              file3,
4415                                              scratch_pool));
4416
4417   if (diff_size12 && diff_size23 && diff_size13)
4418     {
4419       *same12 = *same23 = *same13 = FALSE;
4420     }
4421   else if (diff_size12 && diff_size23)
4422     {
4423       *same12 = *same23 = FALSE;
4424       SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
4425     }
4426   else if (diff_size23 && diff_size13)
4427     {
4428       *same23 = *same13 = FALSE;
4429       SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
4430     }
4431   else if (diff_size12 && diff_size13)
4432     {
4433       *same12 = *same13 = FALSE;
4434       SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
4435     }
4436   else
4437     {
4438       SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
4439       SVN_ERR(contents_three_identical_p(same12, same23, same13,
4440                                          file1, file2, file3,
4441                                          scratch_pool));
4442     }
4443
4444   return SVN_NO_ERROR;
4445 }
4446
4447 #ifdef WIN32
4448 /* Counter value of file_mktemp request (used in a threadsafe way), to make
4449    sure that a single process normally never generates the same tempname
4450    twice */
4451 static volatile apr_uint32_t tempname_counter = 0;
4452 #endif
4453
4454 /* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
4455    Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name.
4456    Perform temporary allocations in SCRATCH_POOL and the result in
4457    RESULT_POOL. */
4458 static svn_error_t *
4459 temp_file_create(apr_file_t **new_file,
4460                  const char **new_file_name,
4461                  const char *directory,
4462                  apr_int32_t flags,
4463                  apr_pool_t *result_pool,
4464                  apr_pool_t *scratch_pool)
4465 {
4466 #ifndef WIN32
4467   const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
4468   const char *templ_apr;
4469   apr_status_t status;
4470
4471   SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
4472
4473   /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
4474          data available in POOL and we need a non-const pointer here,
4475          as apr changes the template to return the new filename. */
4476   status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool);
4477
4478   if (status)
4479     return svn_error_wrap_apr(status, _("Can't create temporary file from "
4480                               "template '%s'"), templ);
4481
4482   /* Translate the returned path back to utf-8 before returning it */
4483   return svn_error_trace(svn_path_cstring_to_utf8(new_file_name,
4484                                                   templ_apr,
4485                                                   result_pool));
4486 #else
4487   /* The Windows implementation of apr_file_mktemp doesn't handle access
4488      denied errors correctly. Therefore we implement our own temp file
4489      creation function here. */
4490
4491   /* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
4492      ### the function we used before. But we try to guess a more unique
4493      ### name before trying if it exists. */
4494
4495   /* Offset by some time value and a unique request nr to make the number
4496      +- unique for both this process and on the computer */
4497   int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter)
4498                + GetCurrentProcessId();
4499   int i;
4500
4501   /* ### Maybe use an iterpool? */
4502   for (i = 0; i <= 99999; i++)
4503     {
4504       apr_uint32_t unique_nr;
4505       const char *unique_name;
4506       const char *unique_name_apr;
4507       apr_file_t *try_file;
4508       apr_status_t apr_err;
4509
4510       /* Generate a number that should be unique for this application and
4511          usually for the entire computer to reduce the number of cycles
4512          through this loop. (A bit of calculation is much cheaper then
4513          disk io) */
4514       unique_nr = baseNr + 3 * i;
4515
4516       unique_name = svn_dirent_join(directory,
4517                                     apr_psprintf(scratch_pool, "svn-%X",
4518                                                  unique_nr),
4519                                     scratch_pool);
4520
4521       SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
4522
4523       apr_err = file_open(&try_file, unique_name_apr, flags,
4524                           APR_OS_DEFAULT, FALSE, scratch_pool);
4525
4526       if (APR_STATUS_IS_EEXIST(apr_err))
4527           continue;
4528       else if (apr_err)
4529         {
4530           /* On Win32, CreateFile fails with an "Access Denied" error
4531              code, rather than "File Already Exists", if the colliding
4532              name belongs to a directory. */
4533
4534           if (APR_STATUS_IS_EACCES(apr_err))
4535             {
4536               apr_finfo_t finfo;
4537               apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
4538                                                 APR_FINFO_TYPE, scratch_pool);
4539
4540               if (!apr_err_2 && finfo.filetype == APR_DIR)
4541                 continue;
4542
4543               apr_err_2 = APR_TO_OS_ERROR(apr_err);
4544
4545               if (apr_err_2 == ERROR_ACCESS_DENIED ||
4546                   apr_err_2 == ERROR_SHARING_VIOLATION)
4547                 {
4548                   /* The file is in use by another process or is hidden;
4549                      create a new name, but don't do this 99999 times in
4550                      case the folder is not writable */
4551                   i += 797;
4552                   continue;
4553                 }
4554
4555               /* Else fall through and return the original error. */
4556             }
4557
4558           return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
4559                                     svn_dirent_local_style(unique_name,
4560                                                            scratch_pool));
4561         }
4562       else
4563         {
4564           /* Move file to the right pool */
4565           apr_err = apr_file_setaside(new_file, try_file, result_pool);
4566
4567           if (apr_err)
4568             return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
4569                                       svn_dirent_local_style(unique_name,
4570                                                              scratch_pool));
4571
4572           *new_file_name = apr_pstrdup(result_pool, unique_name);
4573
4574           return SVN_NO_ERROR;
4575         }
4576     }
4577
4578   return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
4579                            NULL,
4580                            _("Unable to make name in '%s'"),
4581                            svn_dirent_local_style(directory, scratch_pool));
4582 #endif
4583 }
4584
4585 /* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
4586 svn_error_t *
4587 svn_io_file_name_get(const char **filename,
4588                      apr_file_t *file,
4589                      apr_pool_t *pool)
4590 {
4591   const char *fname_apr;
4592   apr_status_t status;
4593
4594   status = apr_file_name_get(&fname_apr, file);
4595   if (status)
4596     return svn_error_wrap_apr(status, _("Can't get file name"));
4597
4598   if (fname_apr)
4599     SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
4600   else
4601     *filename = NULL;
4602
4603   return SVN_NO_ERROR;
4604 }
4605
4606
4607 svn_error_t *
4608 svn_io_open_unique_file3(apr_file_t **file,
4609                          const char **unique_path,
4610                          const char *dirpath,
4611                          svn_io_file_del_t delete_when,
4612                          apr_pool_t *result_pool,
4613                          apr_pool_t *scratch_pool)
4614 {
4615   apr_file_t *tempfile;
4616   const char *tempname;
4617   struct temp_file_cleanup_s *baton = NULL;
4618   apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
4619                        APR_BUFFERED | APR_BINARY);
4620 #if !defined(WIN32) && !defined(__OS2__)
4621   apr_fileperms_t perms;
4622   svn_boolean_t using_system_temp_dir = FALSE;
4623 #endif
4624
4625   SVN_ERR_ASSERT(file || unique_path);
4626   if (file)
4627     *file = NULL;
4628   if (unique_path)
4629     *unique_path = NULL;
4630
4631   if (dirpath == NULL)
4632     {
4633 #if !defined(WIN32) && !defined(__OS2__)
4634       using_system_temp_dir = TRUE;
4635 #endif
4636       SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
4637     }
4638
4639   switch (delete_when)
4640     {
4641       case svn_io_file_del_on_pool_cleanup:
4642         baton = apr_palloc(result_pool, sizeof(*baton));
4643         baton->pool = result_pool;
4644         baton->fname_apr = NULL;
4645
4646         /* Because cleanups are run LIFO, we need to make sure to register
4647            our cleanup before the apr_file_close cleanup:
4648
4649            On Windows, you can't remove an open file.
4650         */
4651         apr_pool_cleanup_register(result_pool, baton,
4652                                   temp_file_plain_cleanup_handler,
4653                                   temp_file_child_cleanup_handler);
4654
4655         break;
4656       case svn_io_file_del_on_close:
4657         flags |= APR_DELONCLOSE;
4658         break;
4659       default:
4660         break;
4661     }
4662
4663   SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
4664                            result_pool, scratch_pool));
4665
4666 #if !defined(WIN32) && !defined(__OS2__)
4667   /* apr_file_mktemp() creates files with mode 0600.
4668    * This is appropriate if we're using a system temp dir since we don't
4669    * want to leak sensitive data into temp files other users can read.
4670    * If we're not using a system temp dir we're probably using the
4671    * .svn/tmp area and it's likely that the tempfile will end up being
4672    * copied or renamed into the working copy.
4673    * This would cause working files having mode 0600 while users might
4674    * expect to see 0644 or 0664. So we tweak perms of the tempfile in this
4675    * case, but only if the umask allows it. */
4676   if (!using_system_temp_dir)
4677     {
4678       SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
4679       SVN_ERR(file_perms_set2(tempfile, perms, scratch_pool));
4680     }
4681 #endif
4682
4683   if (file)
4684     *file = tempfile;
4685   else
4686     SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
4687
4688   if (unique_path)
4689     *unique_path = tempname; /* Was allocated in result_pool */
4690
4691   if (baton)
4692     SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
4693
4694   return SVN_NO_ERROR;
4695 }
4696
4697 svn_error_t *
4698 svn_io_file_readline(apr_file_t *file,
4699                      svn_stringbuf_t **stringbuf,
4700                      const char **eol,
4701                      svn_boolean_t *eof,
4702                      apr_size_t max_len,
4703                      apr_pool_t *result_pool,
4704                      apr_pool_t *scratch_pool)
4705 {
4706   svn_stringbuf_t *str;
4707   const char *eol_str;
4708   apr_size_t numbytes;
4709   char c;
4710   apr_size_t len;
4711   svn_boolean_t found_eof;
4712
4713   str = svn_stringbuf_create_ensure(80, result_pool);
4714
4715   /* Read bytes into STR up to and including, but not storing,
4716    * the next EOL sequence. */
4717   eol_str = NULL;
4718   numbytes = 1;
4719   len = 0;
4720   found_eof = FALSE;
4721   while (!found_eof)
4722     {
4723       if (len < max_len)
4724         SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4725                                        &found_eof, scratch_pool));
4726       len++;
4727       if (numbytes != 1 || len > max_len)
4728         {
4729           found_eof = TRUE;
4730           break;
4731         }
4732
4733       if (c == '\n')
4734         {
4735           eol_str = "\n";
4736         }
4737       else if (c == '\r')
4738         {
4739           eol_str = "\r";
4740
4741           if (!found_eof && len < max_len)
4742             {
4743               apr_off_t pos;
4744
4745               /* Check for "\r\n" by peeking at the next byte. */
4746               pos = 0;
4747               SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool));
4748               SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4749                                              &found_eof, scratch_pool));
4750               if (numbytes == 1 && c == '\n')
4751                 {
4752                   eol_str = "\r\n";
4753                   len++;
4754                 }
4755               else
4756                 {
4757                   /* Pretend we never peeked. */
4758                   SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
4759                   found_eof = FALSE;
4760                   numbytes = 1;
4761                 }
4762             }
4763         }
4764       else
4765         svn_stringbuf_appendbyte(str, c);
4766
4767       if (eol_str)
4768         break;
4769     }
4770
4771   if (eol)
4772     *eol = eol_str;
4773   if (eof)
4774     *eof = found_eof;
4775   *stringbuf = str;
4776
4777   return SVN_NO_ERROR;
4778 }