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