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