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