1251881Speter/*
2251881Speter * io.c:   shared file reading, writing, and probing code.
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <stdio.h>
27251881Speter
28251881Speter#ifndef WIN32
29251881Speter#include <unistd.h>
30251881Speter#endif
31251881Speter
32251881Speter#ifndef APR_STATUS_IS_EPERM
33251881Speter#include <errno.h>
34251881Speter#ifdef EPERM
35251881Speter#define APR_STATUS_IS_EPERM(s)   ((s) == EPERM)
36251881Speter#else
37251881Speter#define APR_STATUS_IS_EPERM(s)   (0)
38251881Speter#endif
39251881Speter#endif
40251881Speter
41251881Speter#include <apr_lib.h>
42251881Speter#include <apr_pools.h>
43251881Speter#include <apr_file_io.h>
44251881Speter#include <apr_file_info.h>
45251881Speter#include <apr_general.h>
46251881Speter#include <apr_strings.h>
47251881Speter#include <apr_portable.h>
48251881Speter#include <apr_md5.h>
49251881Speter
50251881Speter#ifdef WIN32
51251881Speter#include <arch/win32/apr_arch_file_io.h>
52251881Speter#endif
53251881Speter
54251881Speter#include "svn_hash.h"
55251881Speter#include "svn_types.h"
56251881Speter#include "svn_dirent_uri.h"
57251881Speter#include "svn_path.h"
58251881Speter#include "svn_string.h"
59251881Speter#include "svn_error.h"
60251881Speter#include "svn_io.h"
61251881Speter#include "svn_pools.h"
62251881Speter#include "svn_utf.h"
63251881Speter#include "svn_config.h"
64251881Speter#include "svn_private_config.h"
65251881Speter#include "svn_ctype.h"
66251881Speter
67251881Speter#include "private/svn_atomic.h"
68251881Speter#include "private/svn_io_private.h"
69251881Speter
70251881Speter#define SVN_SLEEP_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS"
71251881Speter
72251881Speter/*
73251881Speter  Windows is 'aided' by a number of types of applications that
74251881Speter  follow other applications around and open up files they have
75251881Speter  changed for various reasons (the most intrusive are virus
76251881Speter  scanners).  So, if one of these other apps has glommed onto
77251881Speter  our file we may get an 'access denied' error.
78251881Speter
79251881Speter  This retry loop does not completely solve the problem (who
80251881Speter  knows how long the other app is going to hold onto it for), but
81251881Speter  goes a long way towards minimizing it.  It is not an infinite
82251881Speter  loop because there might really be an error.
83251881Speter
84251881Speter  Another reason for retrying delete operations on Windows
85251881Speter  is that they are asynchronous -- the file or directory is not
86251881Speter  actually deleted until the last handle to it is closed.  The
87251881Speter  retry loop cannot completely solve this problem either, but can
88251881Speter  help mitigate it.
89251881Speter*/
90251881Speter#define RETRY_MAX_ATTEMPTS 100
91251881Speter#define RETRY_INITIAL_SLEEP 1000
92251881Speter#define RETRY_MAX_SLEEP 128000
93251881Speter
94251881Speter#define RETRY_LOOP(err, expr, retry_test, sleep_test)                      \
95251881Speter  do                                                                       \
96251881Speter    {                                                                      \
97251881Speter      apr_status_t os_err = APR_TO_OS_ERROR(err);                          \
98251881Speter      int sleep_count = RETRY_INITIAL_SLEEP;                               \
99251881Speter      int retries;                                                         \
100251881Speter      for (retries = 0;                                                    \
101251881Speter           retries < RETRY_MAX_ATTEMPTS && (retry_test);                   \
102251881Speter           os_err = APR_TO_OS_ERROR(err))                                  \
103251881Speter        {                                                                  \
104251881Speter          if (sleep_test)                                                  \
105251881Speter            {                                                              \
106251881Speter              ++retries;                                                   \
107251881Speter              apr_sleep(sleep_count);                                      \
108251881Speter              if (sleep_count < RETRY_MAX_SLEEP)                           \
109251881Speter                sleep_count *= 2;                                          \
110251881Speter            }                                                              \
111251881Speter          (err) = (expr);                                                  \
112251881Speter        }                                                                  \
113251881Speter    }                                                                      \
114251881Speter  while (0)
115251881Speter
116251881Speter#if defined(EDEADLK) && APR_HAS_THREADS
117251881Speter#define FILE_LOCK_RETRY_LOOP(err, expr)                                    \
118251881Speter  RETRY_LOOP(err,                                                          \
119251881Speter             expr,                                                         \
120251881Speter             (APR_STATUS_IS_EINTR(err) || os_err == EDEADLK),              \
121251881Speter             (!APR_STATUS_IS_EINTR(err)))
122251881Speter#else
123251881Speter#define FILE_LOCK_RETRY_LOOP(err, expr)                                    \
124251881Speter  RETRY_LOOP(err,                                                          \
125251881Speter             expr,                                                         \
126251881Speter             (APR_STATUS_IS_EINTR(err)),                                   \
127251881Speter             0)
128251881Speter#endif
129251881Speter
130251881Speter#ifndef WIN32_RETRY_LOOP
131251881Speter#if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP)
132251881Speter#define WIN32_RETRY_LOOP(err, expr)                                        \
133251881Speter  RETRY_LOOP(err, expr, (os_err == ERROR_ACCESS_DENIED                     \
134251881Speter                         || os_err == ERROR_SHARING_VIOLATION              \
135251881Speter                         || os_err == ERROR_DIR_NOT_EMPTY),                \
136251881Speter             1)
137251881Speter#else
138251881Speter#define WIN32_RETRY_LOOP(err, expr) ((void)0)
139251881Speter#endif
140251881Speter#endif
141251881Speter
142251881Speter/* Forward declaration */
143251881Speterstatic apr_status_t
144251881Speterdir_is_empty(const char *dir, apr_pool_t *pool);
145251881Speterstatic APR_INLINE svn_error_t *
146251881Speterdo_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
147251881Speter                           const char *msg, const char *msg_no_name,
148251881Speter                           apr_pool_t *pool);
149251881Speter
150251881Speter/* Local wrapper of svn_path_cstring_to_utf8() that does no copying on
151251881Speter * operating systems where APR always uses utf-8 as native path format */
152251881Speterstatic svn_error_t *
153251881Spetercstring_to_utf8(const char **path_utf8,
154251881Speter                const char *path_apr,
155251881Speter                apr_pool_t *pool)
156251881Speter{
157251881Speter#if defined(WIN32) || defined(DARWIN)
158251881Speter  *path_utf8 = path_apr;
159251881Speter  return SVN_NO_ERROR;
160251881Speter#else
161251881Speter  return svn_path_cstring_to_utf8(path_utf8, path_apr, pool);
162251881Speter#endif
163251881Speter}
164251881Speter
165251881Speter/* Local wrapper of svn_path_cstring_from_utf8() that does no copying on
166251881Speter * operating systems where APR always uses utf-8 as native path format */
167251881Speterstatic svn_error_t *
168251881Spetercstring_from_utf8(const char **path_apr,
169251881Speter                  const char *path_utf8,
170251881Speter                  apr_pool_t *pool)
171251881Speter{
172251881Speter#if defined(WIN32) || defined(DARWIN)
173251881Speter  *path_apr = path_utf8;
174251881Speter  return SVN_NO_ERROR;
175251881Speter#else
176251881Speter  return svn_path_cstring_from_utf8(path_apr, path_utf8, pool);
177251881Speter#endif
178251881Speter}
179251881Speter
180251881Speter/* Helper function that allows to convert an APR-level PATH to something
181251881Speter * that we can pass the svn_error_wrap_apr. Since we use it in context
182251881Speter * of error reporting, having *some* path info may be more useful than
183251881Speter * having none.  Therefore, we use a best effort approach here.
184251881Speter *
185251881Speter * This is different from svn_io_file_name_get in that it uses a different
186251881Speter * signature style and will never fail.
187251881Speter */
188251881Speterstatic const char *
189251881Spetertry_utf8_from_internal_style(const char *path, apr_pool_t *pool)
190251881Speter{
191251881Speter  svn_error_t *error;
192251881Speter  const char *path_utf8;
193251881Speter
194251881Speter  /* Special case. */
195251881Speter  if (path == NULL)
196251881Speter    return "(NULL)";
197251881Speter
198251881Speter  /* (try to) convert PATH to UTF-8. If that fails, continue with the plain
199251881Speter   * PATH because it is the best we have. It may actually be UTF-8 already.
200251881Speter   */
201251881Speter  error = cstring_to_utf8(&path_utf8, path, pool);
202251881Speter  if (error)
203251881Speter    {
204251881Speter      /* fallback to best representation we have */
205251881Speter
206251881Speter      svn_error_clear(error);
207251881Speter      path_utf8 = path;
208251881Speter    }
209251881Speter
210251881Speter  /* Toggle (back-)slashes etc. as necessary.
211251881Speter   */
212251881Speter  return svn_dirent_local_style(path_utf8, pool);
213251881Speter}
214251881Speter
215251881Speter
216251881Speter/* Set *NAME_P to the UTF-8 representation of directory entry NAME.
217251881Speter * NAME is in the internal encoding used by APR; PARENT is in
218251881Speter * UTF-8 and in internal (not local) style.
219251881Speter *
220251881Speter * Use PARENT only for generating an error string if the conversion
221251881Speter * fails because NAME could not be represented in UTF-8.  In that
222251881Speter * case, return a two-level error in which the outer error's message
223251881Speter * mentions PARENT, but the inner error's message does not mention
224251881Speter * NAME (except possibly in hex) since NAME may not be printable.
225251881Speter * Such a compound error at least allows the user to go looking in the
226251881Speter * right directory for the problem.
227251881Speter *
228251881Speter * If there is any other error, just return that error directly.
229251881Speter *
230251881Speter * If there is any error, the effect on *NAME_P is undefined.
231251881Speter *
232251881Speter * *NAME_P and NAME may refer to the same storage.
233251881Speter */
234251881Speterstatic svn_error_t *
235251881Speterentry_name_to_utf8(const char **name_p,
236251881Speter                   const char *name,
237251881Speter                   const char *parent,
238251881Speter                   apr_pool_t *pool)
239251881Speter{
240251881Speter#if defined(WIN32) || defined(DARWIN)
241251881Speter  *name_p = apr_pstrdup(pool, name);
242251881Speter  return SVN_NO_ERROR;
243251881Speter#else
244251881Speter  svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool);
245251881Speter  if (err && err->apr_err == APR_EINVAL)
246251881Speter    {
247251881Speter      return svn_error_createf(err->apr_err, err,
248251881Speter                               _("Error converting entry "
249251881Speter                                 "in directory '%s' to UTF-8"),
250251881Speter                               svn_dirent_local_style(parent, pool));
251251881Speter    }
252251881Speter  return err;
253251881Speter#endif
254251881Speter}
255251881Speter
256251881Speter
257251881Speter
258251881Speterstatic void
259251881Spetermap_apr_finfo_to_node_kind(svn_node_kind_t *kind,
260251881Speter                           svn_boolean_t *is_special,
261251881Speter                           apr_finfo_t *finfo)
262251881Speter{
263251881Speter  *is_special = FALSE;
264251881Speter
265251881Speter  if (finfo->filetype == APR_REG)
266251881Speter    *kind = svn_node_file;
267251881Speter  else if (finfo->filetype == APR_DIR)
268251881Speter    *kind = svn_node_dir;
269251881Speter  else if (finfo->filetype == APR_LNK)
270251881Speter    {
271251881Speter      *is_special = TRUE;
272251881Speter      *kind = svn_node_file;
273251881Speter    }
274251881Speter  else
275251881Speter    *kind = svn_node_unknown;
276251881Speter}
277251881Speter
278251881Speter/* Helper for svn_io_check_path() and svn_io_check_resolved_path();
279251881Speter   essentially the same semantics as those two, with the obvious
280251881Speter   interpretation for RESOLVE_SYMLINKS. */
281251881Speterstatic svn_error_t *
282251881Speterio_check_path(const char *path,
283251881Speter              svn_boolean_t resolve_symlinks,
284251881Speter              svn_boolean_t *is_special_p,
285251881Speter              svn_node_kind_t *kind,
286251881Speter              apr_pool_t *pool)
287251881Speter{
288251881Speter  apr_int32_t flags;
289251881Speter  apr_finfo_t finfo;
290251881Speter  apr_status_t apr_err;
291251881Speter  const char *path_apr;
292251881Speter  svn_boolean_t is_special = FALSE;
293251881Speter
294251881Speter  if (path[0] == '\0')
295251881Speter    path = ".";
296251881Speter
297251881Speter  /* Not using svn_io_stat() here because we want to check the
298251881Speter     apr_err return explicitly. */
299251881Speter  SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
300251881Speter
301251881Speter  flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK);
302251881Speter  apr_err = apr_stat(&finfo, path_apr, flags, pool);
303251881Speter
304251881Speter  if (APR_STATUS_IS_ENOENT(apr_err))
305251881Speter    *kind = svn_node_none;
306251881Speter  else if (SVN__APR_STATUS_IS_ENOTDIR(apr_err))
307251881Speter    *kind = svn_node_none;
308251881Speter  else if (apr_err)
309251881Speter    return svn_error_wrap_apr(apr_err, _("Can't check path '%s'"),
310251881Speter                              svn_dirent_local_style(path, pool));
311251881Speter  else
312251881Speter    map_apr_finfo_to_node_kind(kind, &is_special, &finfo);
313251881Speter
314251881Speter  *is_special_p = is_special;
315251881Speter
316251881Speter  return SVN_NO_ERROR;
317251881Speter}
318251881Speter
319251881Speter
320251881Speter/* Wrapper for apr_file_open(), taking an APR-encoded filename. */
321251881Speterstatic apr_status_t
322251881Speterfile_open(apr_file_t **f,
323251881Speter          const char *fname_apr,
324251881Speter          apr_int32_t flag,
325251881Speter          apr_fileperms_t perm,
326251881Speter          svn_boolean_t retry_on_failure,
327251881Speter          apr_pool_t *pool)
328251881Speter{
329251881Speter  apr_status_t status = apr_file_open(f, fname_apr, flag, perm, pool);
330251881Speter
331251881Speter  if (retry_on_failure)
332251881Speter    {
333251881Speter      WIN32_RETRY_LOOP(status, apr_file_open(f, fname_apr, flag, perm, pool));
334251881Speter    }
335251881Speter  return status;
336251881Speter}
337251881Speter
338251881Speter
339251881Spetersvn_error_t *
340251881Spetersvn_io_check_resolved_path(const char *path,
341251881Speter                           svn_node_kind_t *kind,
342251881Speter                           apr_pool_t *pool)
343251881Speter{
344251881Speter  svn_boolean_t ignored;
345251881Speter  return io_check_path(path, TRUE, &ignored, kind, pool);
346251881Speter}
347251881Speter
348251881Spetersvn_error_t *
349251881Spetersvn_io_check_path(const char *path,
350251881Speter                  svn_node_kind_t *kind,
351251881Speter                  apr_pool_t *pool)
352251881Speter{
353251881Speter  svn_boolean_t ignored;
354251881Speter  return io_check_path(path, FALSE, &ignored, kind, pool);
355251881Speter}
356251881Speter
357251881Spetersvn_error_t *
358251881Spetersvn_io_check_special_path(const char *path,
359251881Speter                          svn_node_kind_t *kind,
360251881Speter                          svn_boolean_t *is_special,
361251881Speter                          apr_pool_t *pool)
362251881Speter{
363251881Speter  return io_check_path(path, FALSE, is_special, kind, pool);
364251881Speter}
365251881Speter
366251881Speterstruct temp_file_cleanup_s
367251881Speter{
368251881Speter  apr_pool_t *pool;
369251881Speter  /* The (APR-encoded) full path of the file to be removed, or NULL if
370251881Speter   * nothing to do. */
371251881Speter  const char *fname_apr;
372251881Speter};
373251881Speter
374251881Speter
375251881Speterstatic apr_status_t
376251881Spetertemp_file_plain_cleanup_handler(void *baton)
377251881Speter{
378251881Speter  struct  temp_file_cleanup_s *b = baton;
379251881Speter  apr_status_t apr_err = APR_SUCCESS;
380251881Speter
381251881Speter  if (b->fname_apr)
382251881Speter    {
383251881Speter      apr_err = apr_file_remove(b->fname_apr, b->pool);
384251881Speter      WIN32_RETRY_LOOP(apr_err, apr_file_remove(b->fname_apr, b->pool));
385251881Speter    }
386251881Speter
387251881Speter  return apr_err;
388251881Speter}
389251881Speter
390251881Speter
391251881Speterstatic apr_status_t
392251881Spetertemp_file_child_cleanup_handler(void *baton)
393251881Speter{
394251881Speter  struct  temp_file_cleanup_s *b = baton;
395251881Speter
396251881Speter  apr_pool_cleanup_kill(b->pool, b,
397251881Speter                        temp_file_plain_cleanup_handler);
398251881Speter
399251881Speter  return APR_SUCCESS;
400251881Speter}
401251881Speter
402251881Speter
403251881Spetersvn_error_t *
404251881Spetersvn_io_open_uniquely_named(apr_file_t **file,
405251881Speter                           const char **unique_path,
406251881Speter                           const char *dirpath,
407251881Speter                           const char *filename,
408251881Speter                           const char *suffix,
409251881Speter                           svn_io_file_del_t delete_when,
410251881Speter                           apr_pool_t *result_pool,
411251881Speter                           apr_pool_t *scratch_pool)
412251881Speter{
413251881Speter  const char *path;
414251881Speter  unsigned int i;
415251881Speter  struct temp_file_cleanup_s *baton = NULL;
416251881Speter
417251881Speter  /* At the beginning, we don't know whether unique_path will need
418251881Speter     UTF8 conversion */
419251881Speter  svn_boolean_t needs_utf8_conversion = TRUE;
420251881Speter
421251881Speter  SVN_ERR_ASSERT(file || unique_path);
422251881Speter
423251881Speter  if (dirpath == NULL)
424251881Speter    SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
425251881Speter  if (filename == NULL)
426251881Speter    filename = "tempfile";
427251881Speter  if (suffix == NULL)
428251881Speter    suffix = ".tmp";
429251881Speter
430251881Speter  path = svn_dirent_join(dirpath, filename, scratch_pool);
431251881Speter
432251881Speter  if (delete_when == svn_io_file_del_on_pool_cleanup)
433251881Speter    {
434251881Speter      baton = apr_palloc(result_pool, sizeof(*baton));
435251881Speter
436251881Speter      baton->pool = result_pool;
437251881Speter      baton->fname_apr = NULL;
438251881Speter
439251881Speter      /* Because cleanups are run LIFO, we need to make sure to register
440251881Speter         our cleanup before the apr_file_close cleanup:
441251881Speter
442251881Speter         On Windows, you can't remove an open file.
443251881Speter      */
444251881Speter      apr_pool_cleanup_register(result_pool, baton,
445251881Speter                                temp_file_plain_cleanup_handler,
446251881Speter                                temp_file_child_cleanup_handler);
447251881Speter    }
448251881Speter
449251881Speter  for (i = 1; i <= 99999; i++)
450251881Speter    {
451251881Speter      const char *unique_name;
452251881Speter      const char *unique_name_apr;
453251881Speter      apr_file_t *try_file;
454251881Speter      apr_status_t apr_err;
455251881Speter      apr_int32_t flag = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL
456251881Speter                          | APR_BUFFERED | APR_BINARY);
457251881Speter
458251881Speter      if (delete_when == svn_io_file_del_on_close)
459251881Speter        flag |= APR_DELONCLOSE;
460251881Speter
461251881Speter      /* Special case the first attempt -- if we can avoid having a
462251881Speter         generated numeric portion at all, that's best.  So first we
463251881Speter         try with just the suffix; then future tries add a number
464251881Speter         before the suffix.  (A do-while loop could avoid the repeated
465251881Speter         conditional, but it's not worth the clarity loss.)
466251881Speter
467251881Speter         If the first attempt fails, the first number will be "2".
468251881Speter         This is good, since "1" would misleadingly imply that
469251881Speter         the second attempt was actually the first... and if someone's
470251881Speter         got conflicts on their conflicts, we probably don't want to
471251881Speter         add to their confusion :-). */
472251881Speter      if (i == 1)
473251881Speter        unique_name = apr_psprintf(scratch_pool, "%s%s", path, suffix);
474251881Speter      else
475251881Speter        unique_name = apr_psprintf(scratch_pool, "%s.%u%s", path, i, suffix);
476251881Speter
477251881Speter      /* Hmmm.  Ideally, we would append to a native-encoding buf
478251881Speter         before starting iteration, then convert back to UTF-8 for
479251881Speter         return. But I suppose that would make the appending code
480251881Speter         sensitive to i18n in a way it shouldn't be... Oh well. */
481251881Speter      if (needs_utf8_conversion)
482251881Speter        {
483251881Speter          SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name,
484251881Speter                                    scratch_pool));
485251881Speter          if (i == 1)
486251881Speter            {
487251881Speter              /* The variable parts of unique_name will not require UTF8
488251881Speter                 conversion. Therefore, if UTF8 conversion had no effect
489251881Speter                 on it in the first iteration, it won't require conversion
490251881Speter                 in any future iteration. */
491251881Speter              needs_utf8_conversion = strcmp(unique_name_apr, unique_name);
492251881Speter            }
493251881Speter        }
494251881Speter      else
495251881Speter        unique_name_apr = unique_name;
496251881Speter
497251881Speter      apr_err = file_open(&try_file, unique_name_apr, flag,
498251881Speter                          APR_OS_DEFAULT, FALSE, result_pool);
499251881Speter
500251881Speter      if (APR_STATUS_IS_EEXIST(apr_err))
501251881Speter        continue;
502251881Speter      else if (apr_err)
503251881Speter        {
504251881Speter          /* On Win32, CreateFile fails with an "Access Denied" error
505251881Speter             code, rather than "File Already Exists", if the colliding
506251881Speter             name belongs to a directory. */
507251881Speter          if (APR_STATUS_IS_EACCES(apr_err))
508251881Speter            {
509251881Speter              apr_finfo_t finfo;
510251881Speter              apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
511251881Speter                                                APR_FINFO_TYPE, scratch_pool);
512251881Speter
513251881Speter              if (!apr_err_2 && finfo.filetype == APR_DIR)
514251881Speter                continue;
515251881Speter
516251881Speter#ifdef WIN32
517251881Speter              apr_err_2 = APR_TO_OS_ERROR(apr_err);
518251881Speter
519251881Speter              if (apr_err_2 == ERROR_ACCESS_DENIED ||
520251881Speter                  apr_err_2 == ERROR_SHARING_VIOLATION)
521251881Speter                {
522251881Speter                  /* The file is in use by another process or is hidden;
523251881Speter                     create a new name, but don't do this 99999 times in
524251881Speter                     case the folder is not writable */
525251881Speter                  i += 797;
526251881Speter                  continue;
527251881Speter                }
528251881Speter#endif
529251881Speter
530251881Speter              /* Else fall through and return the original error. */
531251881Speter            }
532251881Speter
533251881Speter          if (file)
534251881Speter            *file = NULL;
535251881Speter          if (unique_path)
536251881Speter            *unique_path = NULL;
537251881Speter          return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
538251881Speter                                    svn_dirent_local_style(unique_name,
539251881Speter                                                         scratch_pool));
540251881Speter        }
541251881Speter      else
542251881Speter        {
543251881Speter          if (delete_when == svn_io_file_del_on_pool_cleanup)
544251881Speter            baton->fname_apr = apr_pstrdup(result_pool, unique_name_apr);
545251881Speter
546251881Speter          if (file)
547251881Speter            *file = try_file;
548251881Speter          else
549251881Speter            apr_file_close(try_file);
550251881Speter          if (unique_path)
551251881Speter            *unique_path = apr_pstrdup(result_pool, unique_name);
552251881Speter
553251881Speter          return SVN_NO_ERROR;
554251881Speter        }
555251881Speter    }
556251881Speter
557251881Speter  if (file)
558251881Speter    *file = NULL;
559251881Speter  if (unique_path)
560251881Speter    *unique_path = NULL;
561251881Speter  return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
562251881Speter                           NULL,
563251881Speter                           _("Unable to make name for '%s'"),
564251881Speter                           svn_dirent_local_style(path, scratch_pool));
565251881Speter}
566251881Speter
567251881Spetersvn_error_t *
568251881Spetersvn_io_create_unique_link(const char **unique_name_p,
569251881Speter                          const char *path,
570251881Speter                          const char *dest,
571251881Speter                          const char *suffix,
572251881Speter                          apr_pool_t *pool)
573251881Speter{
574251881Speter#ifdef HAVE_SYMLINK
575251881Speter  unsigned int i;
576251881Speter  const char *unique_name;
577251881Speter  const char *unique_name_apr;
578251881Speter  const char *dest_apr;
579251881Speter  int rv;
580251881Speter
581251881Speter  SVN_ERR(cstring_from_utf8(&dest_apr, dest, pool));
582251881Speter  for (i = 1; i <= 99999; i++)
583251881Speter    {
584251881Speter      apr_status_t apr_err;
585251881Speter
586251881Speter      /* Special case the first attempt -- if we can avoid having a
587251881Speter         generated numeric portion at all, that's best.  So first we
588251881Speter         try with just the suffix; then future tries add a number
589251881Speter         before the suffix.  (A do-while loop could avoid the repeated
590251881Speter         conditional, but it's not worth the clarity loss.)
591251881Speter
592251881Speter         If the first attempt fails, the first number will be "2".
593251881Speter         This is good, since "1" would misleadingly imply that
594251881Speter         the second attempt was actually the first... and if someone's
595251881Speter         got conflicts on their conflicts, we probably don't want to
596251881Speter         add to their confusion :-). */
597251881Speter      if (i == 1)
598251881Speter        unique_name = apr_psprintf(pool, "%s%s", path, suffix);
599251881Speter      else
600251881Speter        unique_name = apr_psprintf(pool, "%s.%u%s", path, i, suffix);
601251881Speter
602251881Speter      /* Hmmm.  Ideally, we would append to a native-encoding buf
603251881Speter         before starting iteration, then convert back to UTF-8 for
604251881Speter         return. But I suppose that would make the appending code
605251881Speter         sensitive to i18n in a way it shouldn't be... Oh well. */
606251881Speter      SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, pool));
607251881Speter      do {
608251881Speter        rv = symlink(dest_apr, unique_name_apr);
609251881Speter      } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
610251881Speter
611251881Speter      apr_err = apr_get_os_error();
612251881Speter
613251881Speter      if (rv == -1 && APR_STATUS_IS_EEXIST(apr_err))
614251881Speter        continue;
615251881Speter      else if (rv == -1 && apr_err)
616251881Speter        {
617251881Speter          /* On Win32, CreateFile fails with an "Access Denied" error
618251881Speter             code, rather than "File Already Exists", if the colliding
619251881Speter             name belongs to a directory. */
620251881Speter          if (APR_STATUS_IS_EACCES(apr_err))
621251881Speter            {
622251881Speter              apr_finfo_t finfo;
623251881Speter              apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
624251881Speter                                                APR_FINFO_TYPE, pool);
625251881Speter
626251881Speter              if (!apr_err_2
627251881Speter                  && (finfo.filetype == APR_DIR))
628251881Speter                continue;
629251881Speter
630251881Speter              /* Else ignore apr_err_2; better to fall through and
631251881Speter                 return the original error. */
632251881Speter            }
633251881Speter
634251881Speter          *unique_name_p = NULL;
635251881Speter          return svn_error_wrap_apr(apr_err,
636251881Speter                                    _("Can't create symbolic link '%s'"),
637251881Speter                                    svn_dirent_local_style(unique_name, pool));
638251881Speter        }
639251881Speter      else
640251881Speter        {
641251881Speter          *unique_name_p = unique_name;
642251881Speter          return SVN_NO_ERROR;
643251881Speter        }
644251881Speter    }
645251881Speter
646251881Speter  *unique_name_p = NULL;
647251881Speter  return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
648251881Speter                           NULL,
649251881Speter                           _("Unable to make name for '%s'"),
650251881Speter                           svn_dirent_local_style(path, pool));
651251881Speter#else
652251881Speter  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
653251881Speter                          _("Symbolic links are not supported on this "
654251881Speter                            "platform"));
655251881Speter#endif
656251881Speter}
657251881Speter
658251881Spetersvn_error_t *
659251881Spetersvn_io_read_link(svn_string_t **dest,
660251881Speter                 const char *path,
661251881Speter                 apr_pool_t *pool)
662251881Speter{
663251881Speter#ifdef HAVE_READLINK
664251881Speter  svn_string_t dest_apr;
665251881Speter  const char *path_apr;
666251881Speter  char buf[1025];
667251881Speter  ssize_t rv;
668251881Speter
669251881Speter  SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
670251881Speter  do {
671251881Speter    rv = readlink(path_apr, buf, sizeof(buf) - 1);
672251881Speter  } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
673251881Speter
674251881Speter  if (rv == -1)
675251881Speter    return svn_error_wrap_apr(apr_get_os_error(),
676251881Speter                              _("Can't read contents of link"));
677251881Speter
678251881Speter  buf[rv] = '\0';
679251881Speter  dest_apr.data = buf;
680251881Speter  dest_apr.len = rv;
681251881Speter
682251881Speter  /* ### Cast needed, one of these interfaces is wrong */
683251881Speter  return svn_utf_string_to_utf8((const svn_string_t **)dest, &dest_apr, pool);
684251881Speter#else
685251881Speter  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
686251881Speter                          _("Symbolic links are not supported on this "
687251881Speter                            "platform"));
688251881Speter#endif
689251881Speter}
690251881Speter
691251881Speter
692251881Spetersvn_error_t *
693251881Spetersvn_io_copy_link(const char *src,
694251881Speter                 const char *dst,
695251881Speter                 apr_pool_t *pool)
696251881Speter
697251881Speter{
698251881Speter#ifdef HAVE_READLINK
699251881Speter  svn_string_t *link_dest;
700251881Speter  const char *dst_tmp;
701251881Speter
702251881Speter  /* Notice what the link is pointing at... */
703251881Speter  SVN_ERR(svn_io_read_link(&link_dest, src, pool));
704251881Speter
705251881Speter  /* Make a tmp-link pointing at the same thing. */
706251881Speter  SVN_ERR(svn_io_create_unique_link(&dst_tmp, dst, link_dest->data,
707251881Speter                                    ".tmp", pool));
708251881Speter
709251881Speter  /* Move the tmp-link to link. */
710251881Speter  return svn_io_file_rename(dst_tmp, dst, pool);
711251881Speter
712251881Speter#else
713251881Speter  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
714251881Speter                          _("Symbolic links are not supported on this "
715251881Speter                            "platform"));
716251881Speter#endif
717251881Speter}
718251881Speter
719251881Speter/* Temporary directory name cache for svn_io_temp_dir() */
720251881Speterstatic volatile svn_atomic_t temp_dir_init_state = 0;
721251881Speterstatic const char *temp_dir;
722251881Speter
723251881Speter/* Helper function to initialize temp dir. Passed to svn_atomic__init_once */
724251881Speterstatic svn_error_t *
725251881Speterinit_temp_dir(void *baton, apr_pool_t *scratch_pool)
726251881Speter{
727251881Speter  /* Global pool for the temp path */
728251881Speter  apr_pool_t *global_pool = svn_pool_create(NULL);
729251881Speter  const char *dir;
730251881Speter
731251881Speter  apr_status_t apr_err = apr_temp_dir_get(&dir, scratch_pool);
732251881Speter
733251881Speter  if (apr_err)
734251881Speter    return svn_error_wrap_apr(apr_err, _("Can't find a temporary directory"));
735251881Speter
736251881Speter  SVN_ERR(cstring_to_utf8(&dir, dir, scratch_pool));
737251881Speter
738251881Speter  dir = svn_dirent_internal_style(dir, scratch_pool);
739251881Speter
740251881Speter  SVN_ERR(svn_dirent_get_absolute(&temp_dir, dir, global_pool));
741251881Speter
742251881Speter  return SVN_NO_ERROR;
743251881Speter}
744251881Speter
745251881Speter
746251881Spetersvn_error_t *
747251881Spetersvn_io_temp_dir(const char **dir,
748251881Speter                apr_pool_t *pool)
749251881Speter{
750251881Speter  SVN_ERR(svn_atomic__init_once(&temp_dir_init_state,
751251881Speter                                init_temp_dir, NULL, pool));
752251881Speter
753251881Speter  *dir = apr_pstrdup(pool, temp_dir);
754251881Speter
755251881Speter  return SVN_NO_ERROR;
756251881Speter}
757251881Speter
758251881Speter
759251881Speter
760251881Speter
761251881Speter/*** Creating, copying and appending files. ***/
762251881Speter
763251881Speter/* Transfer the contents of FROM_FILE to TO_FILE, using POOL for temporary
764251881Speter * allocations.
765251881Speter *
766251881Speter * NOTE: We don't use apr_copy_file() for this, since it takes filenames
767251881Speter * as parameters.  Since we want to copy to a temporary file
768251881Speter * and rename for atomicity (see below), this would require an extra
769251881Speter * close/open pair, which can be expensive, especially on
770251881Speter * remote file systems.
771251881Speter */
772251881Speterstatic apr_status_t
773251881Spetercopy_contents(apr_file_t *from_file,
774251881Speter              apr_file_t *to_file,
775251881Speter              apr_pool_t *pool)
776251881Speter{
777251881Speter  /* Copy bytes till the cows come home. */
778251881Speter  while (1)
779251881Speter    {
780251881Speter      char buf[SVN__STREAM_CHUNK_SIZE];
781251881Speter      apr_size_t bytes_this_time = sizeof(buf);
782251881Speter      apr_status_t read_err;
783251881Speter      apr_status_t write_err;
784251881Speter
785251881Speter      /* Read 'em. */
786251881Speter      read_err = apr_file_read(from_file, buf, &bytes_this_time);
787251881Speter      if (read_err && !APR_STATUS_IS_EOF(read_err))
788251881Speter        {
789251881Speter          return read_err;
790251881Speter        }
791251881Speter
792251881Speter      /* Write 'em. */
793251881Speter      write_err = apr_file_write_full(to_file, buf, bytes_this_time, NULL);
794251881Speter      if (write_err)
795251881Speter        {
796251881Speter          return write_err;
797251881Speter        }
798251881Speter
799251881Speter      if (read_err && APR_STATUS_IS_EOF(read_err))
800251881Speter        {
801251881Speter          /* Return the results of this close: an error, or success. */
802251881Speter          return APR_SUCCESS;
803251881Speter        }
804251881Speter    }
805251881Speter  /* NOTREACHED */
806251881Speter}
807251881Speter
808251881Speter
809251881Spetersvn_error_t *
810251881Spetersvn_io_copy_file(const char *src,
811251881Speter                 const char *dst,
812251881Speter                 svn_boolean_t copy_perms,
813251881Speter                 apr_pool_t *pool)
814251881Speter{
815251881Speter  apr_file_t *from_file, *to_file;
816251881Speter  apr_status_t apr_err;
817251881Speter  const char *dst_tmp;
818251881Speter  svn_error_t *err;
819251881Speter
820251881Speter  /* ### NOTE: sometimes src == dst. In this case, because we copy to a
821251881Speter     ###   temporary file, and then rename over the top of the destination,
822251881Speter     ###   the net result is resetting the permissions on src/dst.
823251881Speter     ###
824251881Speter     ### Note: specifically, this can happen during a switch when the desired
825251881Speter     ###   permissions for a file change from one branch to another. See
826251881Speter     ###   switch_tests 17.
827251881Speter     ###
828251881Speter     ### ... yes, we should avoid copying to the same file, and we should
829251881Speter     ###     make the "reset perms" explicit. The switch *happens* to work
830251881Speter     ###     because of this copy-to-temp-then-rename implementation. If it
831251881Speter     ###     weren't for that, the switch would break.
832251881Speter  */
833251881Speter#ifdef CHECK_FOR_SAME_FILE
834251881Speter  if (strcmp(src, dst) == 0)
835251881Speter    return SVN_NO_ERROR;
836251881Speter#endif
837251881Speter
838251881Speter  SVN_ERR(svn_io_file_open(&from_file, src, APR_READ,
839251881Speter                           APR_OS_DEFAULT, pool));
840251881Speter
841251881Speter  /* For atomicity, we copy to a tmp file and then rename the tmp
842251881Speter     file over the real destination. */
843251881Speter
844251881Speter  SVN_ERR(svn_io_open_unique_file3(&to_file, &dst_tmp,
845251881Speter                                   svn_dirent_dirname(dst, pool),
846251881Speter                                   svn_io_file_del_none, pool, pool));
847251881Speter
848251881Speter  apr_err = copy_contents(from_file, to_file, pool);
849251881Speter
850251881Speter  if (apr_err)
851251881Speter    {
852251881Speter      err = svn_error_wrap_apr(apr_err, _("Can't copy '%s' to '%s'"),
853251881Speter                               svn_dirent_local_style(src, pool),
854251881Speter                               svn_dirent_local_style(dst_tmp, pool));
855251881Speter    }
856251881Speter   else
857251881Speter     err = NULL;
858251881Speter
859251881Speter  err = svn_error_compose_create(err,
860251881Speter                                 svn_io_file_close(from_file, pool));
861251881Speter
862251881Speter  err = svn_error_compose_create(err,
863251881Speter                                 svn_io_file_close(to_file, pool));
864251881Speter
865251881Speter  if (err)
866251881Speter    {
867251881Speter      return svn_error_compose_create(
868251881Speter                                 err,
869251881Speter                                 svn_io_remove_file2(dst_tmp, TRUE, pool));
870251881Speter    }
871251881Speter
872251881Speter  /* If copying perms, set the perms on dst_tmp now, so they will be
873251881Speter     atomically inherited in the upcoming rename.  But note that we
874251881Speter     had to wait until now to set perms, because if they say
875251881Speter     read-only, then we'd have failed filling dst_tmp's contents. */
876251881Speter  if (copy_perms)
877251881Speter    SVN_ERR(svn_io_copy_perms(src, dst_tmp, pool));
878251881Speter
879251881Speter  return svn_error_trace(svn_io_file_rename(dst_tmp, dst, pool));
880251881Speter}
881251881Speter
882251881Speter#if !defined(WIN32) && !defined(__OS2__)
883251881Speter/* Wrapper for apr_file_perms_set(), taking a UTF8-encoded filename. */
884251881Speterstatic svn_error_t *
885251881Speterfile_perms_set(const char *fname, apr_fileperms_t perms,
886251881Speter               apr_pool_t *pool)
887251881Speter{
888251881Speter  const char *fname_apr;
889251881Speter  apr_status_t status;
890251881Speter
891251881Speter  SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
892251881Speter
893251881Speter  status = apr_file_perms_set(fname_apr, perms);
894251881Speter  if (status)
895251881Speter    return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
896251881Speter                              fname);
897251881Speter  else
898251881Speter    return SVN_NO_ERROR;
899251881Speter}
900251881Speter
901251881Speter/* Set permissions PERMS on the FILE. This is a cheaper variant of the
902251881Speter * file_perms_set wrapper() function because no locale-dependent string
903251881Speter * conversion is required. POOL will be used for allocations.
904251881Speter */
905251881Speterstatic svn_error_t *
906251881Speterfile_perms_set2(apr_file_t* file, apr_fileperms_t perms, apr_pool_t *pool)
907251881Speter{
908251881Speter  const char *fname_apr;
909251881Speter  apr_status_t status;
910251881Speter
911251881Speter  status = apr_file_name_get(&fname_apr, file);
912251881Speter  if (status)
913251881Speter    return svn_error_wrap_apr(status, _("Can't get file name"));
914251881Speter
915251881Speter  status = apr_file_perms_set(fname_apr, perms);
916251881Speter  if (status)
917251881Speter    return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
918251881Speter                              try_utf8_from_internal_style(fname_apr, pool));
919251881Speter  else
920251881Speter    return SVN_NO_ERROR;
921251881Speter}
922251881Speter
923251881Speter#endif /* !WIN32 && !__OS2__ */
924251881Speter
925251881Spetersvn_error_t *
926251881Spetersvn_io_copy_perms(const char *src,
927251881Speter                  const char *dst,
928251881Speter                  apr_pool_t *pool)
929251881Speter{
930251881Speter  /* ### On Windows or OS/2, apr_file_perms_set always returns APR_ENOTIMPL,
931251881Speter         and the path passed to apr_file_perms_set must be encoded
932251881Speter         in the platform-specific path encoding; not necessary UTF-8.
933251881Speter         We need a platform-specific implementation to get the
934251881Speter         permissions right. */
935251881Speter
936251881Speter#if !defined(WIN32) && !defined(__OS2__)
937251881Speter  {
938251881Speter    apr_finfo_t finfo;
939251881Speter    svn_node_kind_t kind;
940251881Speter    svn_boolean_t is_special;
941251881Speter    svn_error_t *err;
942251881Speter
943251881Speter    /* If DST is a symlink, don't bother copying permissions. */
944251881Speter    SVN_ERR(svn_io_check_special_path(dst, &kind, &is_special, pool));
945251881Speter    if (is_special)
946251881Speter      return SVN_NO_ERROR;
947251881Speter
948251881Speter    SVN_ERR(svn_io_stat(&finfo, src, APR_FINFO_PROT, pool));
949251881Speter    err = file_perms_set(dst, finfo.protection, pool);
950251881Speter    if (err)
951251881Speter      {
952251881Speter        /* We shouldn't be able to get APR_INCOMPLETE or APR_ENOTIMPL
953251881Speter           here under normal circumstances, because the perms themselves
954251881Speter           came from a call to apr_file_info_get(), and we already know
955251881Speter           this is the non-Win32 case.  But if it does happen, it's not
956251881Speter           an error. */
957251881Speter        if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
958251881Speter            APR_STATUS_IS_ENOTIMPL(err->apr_err))
959251881Speter          svn_error_clear(err);
960251881Speter        else
961251881Speter          {
962251881Speter            const char *message;
963251881Speter            message = apr_psprintf(pool, _("Can't set permissions on '%s'"),
964251881Speter                                   svn_dirent_local_style(dst, pool));
965251881Speter            return svn_error_quick_wrap(err, message);
966251881Speter          }
967251881Speter      }
968251881Speter  }
969251881Speter#endif /* !WIN32 && !__OS2__ */
970251881Speter
971251881Speter  return SVN_NO_ERROR;
972251881Speter}
973251881Speter
974251881Speter
975251881Spetersvn_error_t *
976251881Spetersvn_io_append_file(const char *src, const char *dst, apr_pool_t *pool)
977251881Speter{
978251881Speter  apr_status_t apr_err;
979251881Speter  const char *src_apr, *dst_apr;
980251881Speter
981251881Speter  SVN_ERR(cstring_from_utf8(&src_apr, src, pool));
982251881Speter  SVN_ERR(cstring_from_utf8(&dst_apr, dst, pool));
983251881Speter
984251881Speter  apr_err = apr_file_append(src_apr, dst_apr, APR_OS_DEFAULT, pool);
985251881Speter
986251881Speter  if (apr_err)
987251881Speter    return svn_error_wrap_apr(apr_err, _("Can't append '%s' to '%s'"),
988251881Speter                              svn_dirent_local_style(src, pool),
989251881Speter                              svn_dirent_local_style(dst, pool));
990251881Speter
991251881Speter  return SVN_NO_ERROR;
992251881Speter}
993251881Speter
994251881Speter
995251881Spetersvn_error_t *svn_io_copy_dir_recursively(const char *src,
996251881Speter                                         const char *dst_parent,
997251881Speter                                         const char *dst_basename,
998251881Speter                                         svn_boolean_t copy_perms,
999251881Speter                                         svn_cancel_func_t cancel_func,
1000251881Speter                                         void *cancel_baton,
1001251881Speter                                         apr_pool_t *pool)
1002251881Speter{
1003251881Speter  svn_node_kind_t kind;
1004251881Speter  apr_status_t status;
1005251881Speter  const char *dst_path;
1006251881Speter  apr_dir_t *this_dir;
1007251881Speter  apr_finfo_t this_entry;
1008251881Speter  apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
1009251881Speter
1010251881Speter  /* Make a subpool for recursion */
1011251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
1012251881Speter
1013251881Speter  /* The 'dst_path' is simply dst_parent/dst_basename */
1014251881Speter  dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
1015251881Speter
1016251881Speter  /* Sanity checks:  SRC and DST_PARENT are directories, and
1017251881Speter     DST_BASENAME doesn't already exist in DST_PARENT. */
1018251881Speter  SVN_ERR(svn_io_check_path(src, &kind, subpool));
1019251881Speter  if (kind != svn_node_dir)
1020251881Speter    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1021251881Speter                             _("Source '%s' is not a directory"),
1022251881Speter                             svn_dirent_local_style(src, pool));
1023251881Speter
1024251881Speter  SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool));
1025251881Speter  if (kind != svn_node_dir)
1026251881Speter    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1027251881Speter                             _("Destination '%s' is not a directory"),
1028251881Speter                             svn_dirent_local_style(dst_parent, pool));
1029251881Speter
1030251881Speter  SVN_ERR(svn_io_check_path(dst_path, &kind, subpool));
1031251881Speter  if (kind != svn_node_none)
1032251881Speter    return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
1033251881Speter                             _("Destination '%s' already exists"),
1034251881Speter                             svn_dirent_local_style(dst_path, pool));
1035251881Speter
1036251881Speter  /* Create the new directory. */
1037251881Speter  /* ### TODO: copy permissions (needs apr_file_attrs_get()) */
1038251881Speter  SVN_ERR(svn_io_dir_make(dst_path, APR_OS_DEFAULT, pool));
1039251881Speter
1040251881Speter  /* Loop over the dirents in SRC.  ('.' and '..' are auto-excluded) */
1041251881Speter  SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
1042251881Speter
1043251881Speter  for (status = apr_dir_read(&this_entry, flags, this_dir);
1044251881Speter       status == APR_SUCCESS;
1045251881Speter       status = apr_dir_read(&this_entry, flags, this_dir))
1046251881Speter    {
1047251881Speter      if ((this_entry.name[0] == '.')
1048251881Speter          && ((this_entry.name[1] == '\0')
1049251881Speter              || ((this_entry.name[1] == '.')
1050251881Speter                  && (this_entry.name[2] == '\0'))))
1051251881Speter        {
1052251881Speter          continue;
1053251881Speter        }
1054251881Speter      else
1055251881Speter        {
1056251881Speter          const char *src_target, *entryname_utf8;
1057251881Speter
1058251881Speter          if (cancel_func)
1059251881Speter            SVN_ERR(cancel_func(cancel_baton));
1060251881Speter
1061251881Speter          SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
1062251881Speter                                     src, subpool));
1063251881Speter          src_target = svn_dirent_join(src, entryname_utf8, subpool);
1064251881Speter
1065251881Speter          if (this_entry.filetype == APR_REG) /* regular file */
1066251881Speter            {
1067251881Speter              const char *dst_target = svn_dirent_join(dst_path,
1068251881Speter                                                       entryname_utf8,
1069251881Speter                                                       subpool);
1070251881Speter              SVN_ERR(svn_io_copy_file(src_target, dst_target,
1071251881Speter                                       copy_perms, subpool));
1072251881Speter            }
1073251881Speter          else if (this_entry.filetype == APR_LNK) /* symlink */
1074251881Speter            {
1075251881Speter              const char *dst_target = svn_dirent_join(dst_path,
1076251881Speter                                                       entryname_utf8,
1077251881Speter                                                       subpool);
1078251881Speter              SVN_ERR(svn_io_copy_link(src_target, dst_target,
1079251881Speter                                       subpool));
1080251881Speter            }
1081251881Speter          else if (this_entry.filetype == APR_DIR) /* recurse */
1082251881Speter            {
1083251881Speter              /* Prevent infinite recursion by filtering off our
1084251881Speter                 newly created destination path. */
1085251881Speter              if (strcmp(src, dst_parent) == 0
1086251881Speter                  && strcmp(entryname_utf8, dst_basename) == 0)
1087251881Speter                continue;
1088251881Speter
1089251881Speter              SVN_ERR(svn_io_copy_dir_recursively
1090251881Speter                      (src_target,
1091251881Speter                       dst_path,
1092251881Speter                       entryname_utf8,
1093251881Speter                       copy_perms,
1094251881Speter                       cancel_func,
1095251881Speter                       cancel_baton,
1096251881Speter                       subpool));
1097251881Speter            }
1098251881Speter          /* ### support other APR node types someday?? */
1099251881Speter
1100251881Speter        }
1101251881Speter    }
1102251881Speter
1103251881Speter  if (! (APR_STATUS_IS_ENOENT(status)))
1104251881Speter    return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
1105251881Speter                              svn_dirent_local_style(src, pool));
1106251881Speter
1107251881Speter  status = apr_dir_close(this_dir);
1108251881Speter  if (status)
1109251881Speter    return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
1110251881Speter                              svn_dirent_local_style(src, pool));
1111251881Speter
1112251881Speter  /* Free any memory used by recursion */
1113251881Speter  svn_pool_destroy(subpool);
1114251881Speter
1115251881Speter  return SVN_NO_ERROR;
1116251881Speter}
1117251881Speter
1118251881Speter
1119251881Spetersvn_error_t *
1120251881Spetersvn_io_make_dir_recursively(const char *path, apr_pool_t *pool)
1121251881Speter{
1122251881Speter  const char *path_apr;
1123251881Speter  apr_status_t apr_err;
1124251881Speter
1125251881Speter  if (svn_path_is_empty(path))
1126251881Speter    /* Empty path (current dir) is assumed to always exist,
1127251881Speter       so we do nothing, per docs. */
1128251881Speter    return SVN_NO_ERROR;
1129251881Speter
1130251881Speter  SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1131251881Speter
1132251881Speter  apr_err = apr_dir_make_recursive(path_apr, APR_OS_DEFAULT, pool);
1133251881Speter  WIN32_RETRY_LOOP(apr_err, apr_dir_make_recursive(path_apr,
1134251881Speter                                                   APR_OS_DEFAULT, pool));
1135251881Speter
1136251881Speter  if (apr_err)
1137251881Speter    return svn_error_wrap_apr(apr_err, _("Can't make directory '%s'"),
1138251881Speter                              svn_dirent_local_style(path, pool));
1139251881Speter
1140251881Speter  return SVN_NO_ERROR;
1141251881Speter}
1142251881Speter
1143251881Spetersvn_error_t *svn_io_file_create(const char *file,
1144251881Speter                                const char *contents,
1145251881Speter                                apr_pool_t *pool)
1146251881Speter{
1147251881Speter  apr_file_t *f;
1148251881Speter  apr_size_t written;
1149251881Speter  svn_error_t *err = SVN_NO_ERROR;
1150251881Speter
1151251881Speter  SVN_ERR(svn_io_file_open(&f, file,
1152251881Speter                           (APR_WRITE | APR_CREATE | APR_EXCL),
1153251881Speter                           APR_OS_DEFAULT,
1154251881Speter                           pool));
1155251881Speter  if (contents && *contents)
1156251881Speter    err = svn_io_file_write_full(f, contents, strlen(contents),
1157251881Speter                                 &written, pool);
1158251881Speter
1159251881Speter
1160251881Speter  return svn_error_trace(
1161251881Speter                        svn_error_compose_create(err,
1162251881Speter                                                 svn_io_file_close(f, pool)));
1163251881Speter}
1164251881Speter
1165251881Spetersvn_error_t *svn_io_dir_file_copy(const char *src_path,
1166251881Speter                                  const char *dest_path,
1167251881Speter                                  const char *file,
1168251881Speter                                  apr_pool_t *pool)
1169251881Speter{
1170251881Speter  const char *file_dest_path = svn_dirent_join(dest_path, file, pool);
1171251881Speter  const char *file_src_path = svn_dirent_join(src_path, file, pool);
1172251881Speter
1173251881Speter  return svn_io_copy_file(file_src_path, file_dest_path, TRUE, pool);
1174251881Speter}
1175251881Speter
1176251881Speter
1177251881Speter/*** Modtime checking. ***/
1178251881Speter
1179251881Spetersvn_error_t *
1180251881Spetersvn_io_file_affected_time(apr_time_t *apr_time,
1181251881Speter                          const char *path,
1182251881Speter                          apr_pool_t *pool)
1183251881Speter{
1184251881Speter  apr_finfo_t finfo;
1185251881Speter
1186251881Speter  SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_MIN | APR_FINFO_LINK, pool));
1187251881Speter
1188251881Speter  *apr_time = finfo.mtime;
1189251881Speter
1190251881Speter  return SVN_NO_ERROR;
1191251881Speter}
1192251881Speter
1193251881Speter
1194251881Spetersvn_error_t *
1195251881Spetersvn_io_set_file_affected_time(apr_time_t apr_time,
1196251881Speter                              const char *path,
1197251881Speter                              apr_pool_t *pool)
1198251881Speter{
1199251881Speter  apr_status_t status;
1200251881Speter  const char *native_path;
1201251881Speter
1202251881Speter  SVN_ERR(cstring_from_utf8(&native_path, path, pool));
1203251881Speter  status = apr_file_mtime_set(native_path, apr_time, pool);
1204251881Speter
1205251881Speter  if (status)
1206251881Speter    return svn_error_wrap_apr(status, _("Can't set access time of '%s'"),
1207251881Speter                              svn_dirent_local_style(path, pool));
1208251881Speter
1209251881Speter  return SVN_NO_ERROR;
1210251881Speter}
1211251881Speter
1212251881Speter
1213251881Spetervoid
1214251881Spetersvn_io_sleep_for_timestamps(const char *path, apr_pool_t *pool)
1215251881Speter{
1216251881Speter  apr_time_t now, then;
1217251881Speter  svn_error_t *err;
1218251881Speter  char *sleep_env_var;
1219251881Speter
1220251881Speter  sleep_env_var = getenv(SVN_SLEEP_ENV_VAR);
1221251881Speter
1222251881Speter  if (sleep_env_var && apr_strnatcasecmp(sleep_env_var, "yes") == 0)
1223251881Speter    return; /* Allow skipping for testing */
1224251881Speter
1225251881Speter  now = apr_time_now();
1226251881Speter
1227251881Speter  /* Calculate 0.02 seconds after the next second wallclock tick. */
1228251881Speter  then = apr_time_make(apr_time_sec(now) + 1, APR_USEC_PER_SEC / 50);
1229251881Speter
1230251881Speter  /* Worst case is waiting one second, so we can use that time to determine
1231251881Speter     if we can sleep shorter than that */
1232251881Speter  if (path)
1233251881Speter    {
1234251881Speter      apr_finfo_t finfo;
1235251881Speter
1236251881Speter      err = svn_io_stat(&finfo, path, APR_FINFO_MTIME | APR_FINFO_LINK, pool);
1237251881Speter
1238251881Speter      if (err)
1239251881Speter        {
1240251881Speter          svn_error_clear(err); /* Fall back on original behavior */
1241251881Speter        }
1242251881Speter      else if (finfo.mtime % APR_USEC_PER_SEC)
1243251881Speter        {
1244251881Speter          /* Very simplistic but safe approach:
1245251881Speter              If the filesystem has < sec mtime we can be reasonably sure
1246251881Speter              that the filesystem has <= millisecond precision.
1247251881Speter
1248251881Speter             ## Perhaps find a better algorithm here. This will fail once
1249251881Speter                in every 1000 cases on a millisecond precision filesystem.
1250251881Speter
1251251881Speter                But better to fail once in every thousand cases than every
1252251881Speter                time, like we did before.
1253251881Speter                (All tested filesystems I know have at least microsecond precision.)
1254251881Speter
1255251881Speter             Note for further research on algorithm:
1256251881Speter               FAT32 has < 1 sec precision on ctime, but 2 sec on mtime */
1257251881Speter
1258251881Speter          /* Sleep for at least 1 millisecond.
1259251881Speter             (t < 1000 will be round to 0 in apr) */
1260251881Speter          apr_sleep(1000);
1261251881Speter
1262251881Speter          return;
1263251881Speter        }
1264251881Speter
1265251881Speter      now = apr_time_now(); /* Extract the time used for the path stat */
1266251881Speter
1267251881Speter      if (now >= then)
1268251881Speter        return; /* Passing negative values may suspend indefinitely (Windows) */
1269251881Speter    }
1270251881Speter
1271251881Speter  apr_sleep(then - now);
1272251881Speter}
1273251881Speter
1274251881Speter
1275251881Spetersvn_error_t *
1276251881Spetersvn_io_filesizes_different_p(svn_boolean_t *different_p,
1277251881Speter                             const char *file1,
1278251881Speter                             const char *file2,
1279251881Speter                             apr_pool_t *pool)
1280251881Speter{
1281251881Speter  apr_finfo_t finfo1;
1282251881Speter  apr_finfo_t finfo2;
1283251881Speter  apr_status_t status;
1284251881Speter  const char *file1_apr, *file2_apr;
1285251881Speter
1286251881Speter  /* Not using svn_io_stat() because don't want to generate
1287251881Speter     svn_error_t objects for non-error conditions. */
1288251881Speter
1289251881Speter  SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool));
1290251881Speter  SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool));
1291251881Speter
1292251881Speter  /* Stat both files */
1293251881Speter  status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool);
1294251881Speter  if (status)
1295251881Speter    {
1296251881Speter      /* If we got an error stat'ing a file, it could be because the
1297251881Speter         file was removed... or who knows.  Whatever the case, we
1298251881Speter         don't know if the filesizes are definitely different, so
1299251881Speter         assume that they're not. */
1300251881Speter      *different_p = FALSE;
1301251881Speter      return SVN_NO_ERROR;
1302251881Speter    }
1303251881Speter
1304251881Speter  status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool);
1305251881Speter  if (status)
1306251881Speter    {
1307251881Speter      /* See previous comment. */
1308251881Speter      *different_p = FALSE;
1309251881Speter      return SVN_NO_ERROR;
1310251881Speter    }
1311251881Speter
1312251881Speter  /* Examine file sizes */
1313251881Speter  if (finfo1.size == finfo2.size)
1314251881Speter    *different_p = FALSE;
1315251881Speter  else
1316251881Speter    *different_p = TRUE;
1317251881Speter
1318251881Speter  return SVN_NO_ERROR;
1319251881Speter}
1320251881Speter
1321251881Speter
1322251881Spetersvn_error_t *
1323251881Spetersvn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
1324251881Speter                                   svn_boolean_t *different_p23,
1325251881Speter                                   svn_boolean_t *different_p13,
1326251881Speter                                   const char *file1,
1327251881Speter                                   const char *file2,
1328251881Speter                                   const char *file3,
1329251881Speter                                   apr_pool_t *scratch_pool)
1330251881Speter{
1331251881Speter  apr_finfo_t finfo1, finfo2, finfo3;
1332251881Speter  apr_status_t status1, status2, status3;
1333251881Speter  const char *file1_apr, *file2_apr, *file3_apr;
1334251881Speter
1335251881Speter  /* Not using svn_io_stat() because don't want to generate
1336251881Speter     svn_error_t objects for non-error conditions. */
1337251881Speter
1338251881Speter  SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool));
1339251881Speter  SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool));
1340251881Speter  SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool));
1341251881Speter
1342251881Speter  /* Stat all three files */
1343251881Speter  status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool);
1344251881Speter  status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool);
1345251881Speter  status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool);
1346251881Speter
1347251881Speter  /* If we got an error stat'ing a file, it could be because the
1348251881Speter     file was removed... or who knows.  Whatever the case, we
1349251881Speter     don't know if the filesizes are definitely different, so
1350251881Speter     assume that they're not. */
1351251881Speter  *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size;
1352251881Speter  *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size;
1353251881Speter  *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size;
1354251881Speter
1355251881Speter  return SVN_NO_ERROR;
1356251881Speter}
1357251881Speter
1358251881Speter
1359251881Spetersvn_error_t *
1360251881Spetersvn_io_file_checksum2(svn_checksum_t **checksum,
1361251881Speter                      const char *file,
1362251881Speter                      svn_checksum_kind_t kind,
1363251881Speter                      apr_pool_t *pool)
1364251881Speter{
1365251881Speter  svn_stream_t *file_stream;
1366251881Speter  svn_stream_t *checksum_stream;
1367251881Speter  apr_file_t* f;
1368251881Speter
1369251881Speter  SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool));
1370251881Speter  file_stream = svn_stream_from_aprfile2(f, FALSE, pool);
1371251881Speter  checksum_stream = svn_stream_checksummed2(file_stream, checksum, NULL, kind,
1372251881Speter                                            TRUE, pool);
1373251881Speter
1374251881Speter  /* Because the checksummed stream will force the reading (and
1375251881Speter     checksumming) of all the file's bytes, we can just close the stream
1376251881Speter     and let its magic work. */
1377251881Speter  return svn_stream_close(checksum_stream);
1378251881Speter}
1379251881Speter
1380251881Speter
1381251881Spetersvn_error_t *
1382251881Spetersvn_io_file_checksum(unsigned char digest[],
1383251881Speter                     const char *file,
1384251881Speter                     apr_pool_t *pool)
1385251881Speter{
1386251881Speter  svn_checksum_t *checksum;
1387251881Speter
1388251881Speter  SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool));
1389251881Speter  memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE);
1390251881Speter
1391251881Speter  return SVN_NO_ERROR;
1392251881Speter}
1393251881Speter
1394251881Speter
1395251881Speter
1396251881Speter/*** Permissions and modes. ***/
1397251881Speter
1398251881Speter#if !defined(WIN32) && !defined(__OS2__)
1399251881Speter/* Given the file specified by PATH, attempt to create an
1400251881Speter   identical version of it owned by the current user.  This is done by
1401251881Speter   moving it to a temporary location, copying the file back to its old
1402251881Speter   path, then deleting the temporarily moved version.  All temporary
1403251881Speter   allocations are done in POOL. */
1404251881Speterstatic svn_error_t *
1405251881Speterreown_file(const char *path,
1406251881Speter           apr_pool_t *pool)
1407251881Speter{
1408251881Speter  const char *unique_name;
1409251881Speter
1410251881Speter  SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name,
1411251881Speter                                   svn_dirent_dirname(path, pool),
1412251881Speter                                   svn_io_file_del_none, pool, pool));
1413251881Speter  SVN_ERR(svn_io_file_rename(path, unique_name, pool));
1414251881Speter  SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool));
1415251881Speter  return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool));
1416251881Speter}
1417251881Speter
1418251881Speter/* Determine what the PERMS for a new file should be by looking at the
1419251881Speter   permissions of a temporary file that we create.
1420251881Speter   Unfortunately, umask() as defined in POSIX provides no thread-safe way
1421251881Speter   to get at the current value of the umask, so what we're doing here is
1422251881Speter   the only way we have to determine which combination of write bits
1423251881Speter   (User/Group/World) should be set by default.
1424251881Speter   Make temporary allocations in SCRATCH_POOL.  */
1425251881Speterstatic svn_error_t *
1426251881Speterget_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool)
1427251881Speter{
1428251881Speter  /* the default permissions as read from the temp folder */
1429251881Speter  static apr_fileperms_t default_perms = 0;
1430251881Speter
1431251881Speter  /* Technically, this "racy": Multiple threads may use enter here and
1432251881Speter     try to figure out the default permission concurrently. That's fine
1433251881Speter     since they will end up with the same results. Even more technical,
1434251881Speter     apr_fileperms_t is an atomic type on 32+ bit machines.
1435251881Speter   */
1436251881Speter  if (default_perms == 0)
1437251881Speter    {
1438251881Speter      apr_finfo_t finfo;
1439251881Speter      apr_file_t *fd;
1440251881Speter      const char *fname_base, *fname;
1441251881Speter      apr_uint32_t randomish;
1442251881Speter      svn_error_t *err;
1443251881Speter
1444251881Speter      /* Get the perms for a newly created file to find out what bits
1445251881Speter        should be set.
1446251881Speter
1447251881Speter        Explictly delete the file because we want this file to be as
1448251881Speter        short-lived as possible since its presence means other
1449251881Speter        processes may have to try multiple names.
1450251881Speter
1451251881Speter        Using svn_io_open_uniquely_named() here because other tempfile
1452251881Speter        creation functions tweak the permission bits of files they create.
1453251881Speter      */
1454251881Speter      randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool
1455251881Speter                   + (apr_uint32_t)apr_time_now());
1456251881Speter      fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish);
1457251881Speter
1458251881Speter      SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, NULL, fname_base,
1459251881Speter                                         NULL, svn_io_file_del_none,
1460251881Speter                                         scratch_pool, scratch_pool));
1461251881Speter      err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool);
1462251881Speter      err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool));
1463251881Speter      err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE,
1464251881Speter                                                              scratch_pool));
1465251881Speter      SVN_ERR(err);
1466251881Speter      *perms = finfo.protection;
1467251881Speter      default_perms = finfo.protection;
1468251881Speter    }
1469251881Speter  else
1470251881Speter    *perms = default_perms;
1471251881Speter
1472251881Speter  return SVN_NO_ERROR;
1473251881Speter}
1474251881Speter
1475251881Speter/* OR together permission bits of the file FD and the default permissions
1476251881Speter   of a file as determined by get_default_file_perms(). Do temporary
1477251881Speter   allocations in SCRATCH_POOL. */
1478251881Speterstatic svn_error_t *
1479251881Spetermerge_default_file_perms(apr_file_t *fd, apr_fileperms_t *perms,
1480251881Speter                         apr_pool_t *scratch_pool)
1481251881Speter{
1482251881Speter  apr_finfo_t finfo;
1483251881Speter  apr_fileperms_t default_perms;
1484251881Speter
1485251881Speter  SVN_ERR(get_default_file_perms(&default_perms, scratch_pool));
1486251881Speter  SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool));
1487251881Speter
1488251881Speter  /* Glom the perms together. */
1489251881Speter  *perms = default_perms | finfo.protection;
1490251881Speter  return SVN_NO_ERROR;
1491251881Speter}
1492251881Speter
1493251881Speter/* This is a helper function for the svn_io_set_file_read* functions
1494251881Speter   that attempts to honor the users umask when dealing with
1495251881Speter   permission changes.  It is a no-op when invoked on a symlink. */
1496251881Speterstatic svn_error_t *
1497251881Speterio_set_file_perms(const char *path,
1498251881Speter                  svn_boolean_t change_readwrite,
1499251881Speter                  svn_boolean_t enable_write,
1500251881Speter                  svn_boolean_t change_executable,
1501251881Speter                  svn_boolean_t executable,
1502251881Speter                  svn_boolean_t ignore_enoent,
1503251881Speter                  apr_pool_t *pool)
1504251881Speter{
1505251881Speter  apr_status_t status;
1506251881Speter  const char *path_apr;
1507251881Speter  apr_finfo_t finfo;
1508251881Speter  apr_fileperms_t perms_to_set;
1509251881Speter
1510251881Speter  SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1511251881Speter
1512251881Speter  /* Try to change only a minimal amount of the perms first
1513251881Speter     by getting the current perms and adding bits
1514251881Speter     only on where read perms are granted.  If this fails
1515251881Speter     fall through to just setting file attributes. */
1516251881Speter  status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool);
1517251881Speter  if (status)
1518251881Speter    {
1519251881Speter      if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1520251881Speter        return SVN_NO_ERROR;
1521251881Speter      else if (status != APR_ENOTIMPL)
1522251881Speter        return svn_error_wrap_apr(status,
1523251881Speter                                  _("Can't change perms of file '%s'"),
1524251881Speter                                  svn_dirent_local_style(path, pool));
1525251881Speter      return SVN_NO_ERROR;
1526251881Speter    }
1527251881Speter
1528251881Speter  if (finfo.filetype == APR_LNK)
1529251881Speter    return SVN_NO_ERROR;
1530251881Speter
1531251881Speter  perms_to_set = finfo.protection;
1532251881Speter  if (change_readwrite)
1533251881Speter    {
1534251881Speter      if (enable_write) /* Make read-write. */
1535251881Speter        {
1536251881Speter          apr_file_t *fd;
1537251881Speter
1538251881Speter          /* Get the perms for the original file so we'll have any other bits
1539251881Speter           * that were already set (like the execute bits, for example). */
1540251881Speter          SVN_ERR(svn_io_file_open(&fd, path, APR_READ,
1541251881Speter                                   APR_OS_DEFAULT, pool));
1542251881Speter          SVN_ERR(merge_default_file_perms(fd, &perms_to_set, pool));
1543251881Speter          SVN_ERR(svn_io_file_close(fd, pool));
1544251881Speter        }
1545251881Speter      else
1546251881Speter        {
1547251881Speter          if (finfo.protection & APR_UREAD)
1548251881Speter            perms_to_set &= ~APR_UWRITE;
1549251881Speter          if (finfo.protection & APR_GREAD)
1550251881Speter            perms_to_set &= ~APR_GWRITE;
1551251881Speter          if (finfo.protection & APR_WREAD)
1552251881Speter            perms_to_set &= ~APR_WWRITE;
1553251881Speter        }
1554251881Speter    }
1555251881Speter
1556251881Speter  if (change_executable)
1557251881Speter    {
1558251881Speter      if (executable)
1559251881Speter        {
1560251881Speter          if (finfo.protection & APR_UREAD)
1561251881Speter            perms_to_set |= APR_UEXECUTE;
1562251881Speter          if (finfo.protection & APR_GREAD)
1563251881Speter            perms_to_set |= APR_GEXECUTE;
1564251881Speter          if (finfo.protection & APR_WREAD)
1565251881Speter            perms_to_set |= APR_WEXECUTE;
1566251881Speter        }
1567251881Speter      else
1568251881Speter        {
1569251881Speter          if (finfo.protection & APR_UREAD)
1570251881Speter            perms_to_set &= ~APR_UEXECUTE;
1571251881Speter          if (finfo.protection & APR_GREAD)
1572251881Speter            perms_to_set &= ~APR_GEXECUTE;
1573251881Speter          if (finfo.protection & APR_WREAD)
1574251881Speter            perms_to_set &= ~APR_WEXECUTE;
1575251881Speter        }
1576251881Speter    }
1577251881Speter
1578251881Speter  /* If we aren't changing anything then just return, this saves
1579251881Speter     some system calls and helps with shared working copies */
1580251881Speter  if (perms_to_set == finfo.protection)
1581251881Speter    return SVN_NO_ERROR;
1582251881Speter
1583251881Speter  status = apr_file_perms_set(path_apr, perms_to_set);
1584251881Speter  if (!status)
1585251881Speter    return SVN_NO_ERROR;
1586251881Speter
1587251881Speter  if (APR_STATUS_IS_EPERM(status))
1588251881Speter    {
1589251881Speter      /* We don't have permissions to change the
1590251881Speter         permissions!  Try a move, copy, and delete
1591251881Speter         workaround to see if we can get the file owned by
1592251881Speter         us.  If these succeed, try the permissions set
1593251881Speter         again.
1594251881Speter
1595251881Speter         Note that we only attempt this in the
1596251881Speter         stat-available path.  This assumes that the
1597251881Speter         move-copy workaround will only be helpful on
1598251881Speter         platforms that implement apr_stat. */
1599251881Speter      SVN_ERR(reown_file(path, pool));
1600251881Speter      status = apr_file_perms_set(path_apr, perms_to_set);
1601251881Speter    }
1602251881Speter
1603251881Speter  if (!status)
1604251881Speter    return SVN_NO_ERROR;
1605251881Speter
1606251881Speter  if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1607251881Speter    return SVN_NO_ERROR;
1608251881Speter  else if (status == APR_ENOTIMPL)
1609251881Speter    {
1610251881Speter      /* At least try to set the attributes. */
1611251881Speter      apr_fileattrs_t attrs = 0;
1612251881Speter      apr_fileattrs_t attrs_values = 0;
1613251881Speter
1614251881Speter      if (change_readwrite)
1615251881Speter        {
1616251881Speter          attrs = APR_FILE_ATTR_READONLY;
1617251881Speter          if (!enable_write)
1618251881Speter            attrs_values = APR_FILE_ATTR_READONLY;
1619251881Speter        }
1620251881Speter      if (change_executable)
1621251881Speter        {
1622251881Speter          attrs = APR_FILE_ATTR_EXECUTABLE;
1623251881Speter          if (executable)
1624251881Speter            attrs_values = APR_FILE_ATTR_EXECUTABLE;
1625251881Speter        }
1626251881Speter      status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
1627251881Speter    }
1628251881Speter
1629251881Speter  return svn_error_wrap_apr(status,
1630251881Speter                            _("Can't change perms of file '%s'"),
1631251881Speter                            svn_dirent_local_style(path, pool));
1632251881Speter}
1633251881Speter#endif /* !WIN32 && !__OS2__ */
1634251881Speter
1635251881Speter#ifdef WIN32
1636251881Speter#if APR_HAS_UNICODE_FS
1637251881Speter/* copy of the apr function utf8_to_unicode_path since apr doesn't export this one */
1638251881Speterstatic apr_status_t io_utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen,
1639251881Speter                                            const char* srcstr)
1640251881Speter{
1641251881Speter    /* TODO: The computations could preconvert the string to determine
1642251881Speter     * the true size of the retstr, but that's a memory over speed
1643251881Speter     * tradeoff that isn't appropriate this early in development.
1644251881Speter     *
1645251881Speter     * Allocate the maximum string length based on leading 4
1646251881Speter     * characters of \\?\ (allowing nearly unlimited path lengths)
1647251881Speter     * plus the trailing null, then transform /'s into \\'s since
1648251881Speter     * the \\?\ form doesn't allow '/' path separators.
1649251881Speter     *
1650251881Speter     * Note that the \\?\ form only works for local drive paths, and
1651251881Speter     * \\?\UNC\ is needed UNC paths.
1652251881Speter     */
1653251881Speter    apr_size_t srcremains = strlen(srcstr) + 1;
1654251881Speter    apr_wchar_t *t = retstr;
1655251881Speter    apr_status_t rv;
1656251881Speter
1657251881Speter    /* This is correct, we don't twist the filename if it will
1658251881Speter     * definitely be shorter than 248 characters.  It merits some
1659251881Speter     * performance testing to see if this has any effect, but there
1660251881Speter     * seem to be applications that get confused by the resulting
1661251881Speter     * Unicode \\?\ style file names, especially if they use argv[0]
1662251881Speter     * or call the Win32 API functions such as GetModuleName, etc.
1663251881Speter     * Not every application is prepared to handle such names.
1664251881Speter     *
1665251881Speter     * Note also this is shorter than MAX_PATH, as directory paths
1666251881Speter     * are actually limited to 248 characters.
1667251881Speter     *
1668251881Speter     * Note that a utf-8 name can never result in more wide chars
1669251881Speter     * than the original number of utf-8 narrow chars.
1670251881Speter     */
1671251881Speter    if (srcremains > 248) {
1672251881Speter        if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
1673251881Speter            wcscpy (retstr, L"\\\\?\\");
1674251881Speter            retlen -= 4;
1675251881Speter            t += 4;
1676251881Speter        }
1677251881Speter        else if ((srcstr[0] == '/' || srcstr[0] == '\\')
1678251881Speter              && (srcstr[1] == '/' || srcstr[1] == '\\')
1679251881Speter              && (srcstr[2] != '?')) {
1680251881Speter            /* Skip the slashes */
1681251881Speter            srcstr += 2;
1682251881Speter            srcremains -= 2;
1683251881Speter            wcscpy (retstr, L"\\\\?\\UNC\\");
1684251881Speter            retlen -= 8;
1685251881Speter            t += 8;
1686251881Speter        }
1687251881Speter    }
1688251881Speter
1689251881Speter    if (rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen)) {
1690251881Speter        return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
1691251881Speter    }
1692251881Speter    if (srcremains) {
1693251881Speter        return APR_ENAMETOOLONG;
1694251881Speter    }
1695251881Speter    for (; *t; ++t)
1696251881Speter        if (*t == L'/')
1697251881Speter            *t = L'\\';
1698251881Speter    return APR_SUCCESS;
1699251881Speter}
1700251881Speter#endif
1701251881Speter
1702251881Speterstatic apr_status_t io_win_file_attrs_set(const char *fname,
1703251881Speter                                          DWORD attributes,
1704251881Speter                                          DWORD attr_mask,
1705251881Speter                                          apr_pool_t *pool)
1706251881Speter{
1707251881Speter    /* this is an implementation of apr_file_attrs_set() but one
1708251881Speter       that uses the proper Windows attributes instead of the apr
1709251881Speter       attributes. This way, we can apply any Windows file and
1710251881Speter       folder attributes even if apr doesn't implement them */
1711251881Speter    DWORD flags;
1712251881Speter    apr_status_t rv;
1713251881Speter#if APR_HAS_UNICODE_FS
1714251881Speter    apr_wchar_t wfname[APR_PATH_MAX];
1715251881Speter#endif
1716251881Speter
1717251881Speter#if APR_HAS_UNICODE_FS
1718251881Speter    IF_WIN_OS_IS_UNICODE
1719251881Speter    {
1720251881Speter        if (rv = io_utf8_to_unicode_path(wfname,
1721251881Speter                                         sizeof(wfname) / sizeof(wfname[0]),
1722251881Speter                                         fname))
1723251881Speter            return rv;
1724251881Speter        flags = GetFileAttributesW(wfname);
1725251881Speter    }
1726251881Speter#endif
1727251881Speter#if APR_HAS_ANSI_FS
1728251881Speter    ELSE_WIN_OS_IS_ANSI
1729251881Speter    {
1730251881Speter        flags = GetFileAttributesA(fname);
1731251881Speter    }
1732251881Speter#endif
1733251881Speter
1734251881Speter    if (flags == 0xFFFFFFFF)
1735251881Speter        return apr_get_os_error();
1736251881Speter
1737251881Speter    flags &= ~attr_mask;
1738251881Speter    flags |= (attributes & attr_mask);
1739251881Speter
1740251881Speter#if APR_HAS_UNICODE_FS
1741251881Speter    IF_WIN_OS_IS_UNICODE
1742251881Speter    {
1743251881Speter        rv = SetFileAttributesW(wfname, flags);
1744251881Speter    }
1745251881Speter#endif
1746251881Speter#if APR_HAS_ANSI_FS
1747251881Speter    ELSE_WIN_OS_IS_ANSI
1748251881Speter    {
1749251881Speter        rv = SetFileAttributesA(fname, flags);
1750251881Speter    }
1751251881Speter#endif
1752251881Speter
1753251881Speter    if (rv == 0)
1754251881Speter        return apr_get_os_error();
1755251881Speter
1756251881Speter    return APR_SUCCESS;
1757251881Speter}
1758251881Speter
1759251881Speter#endif
1760251881Speter
1761251881Spetersvn_error_t *
1762251881Spetersvn_io_set_file_read_write_carefully(const char *path,
1763251881Speter                                     svn_boolean_t enable_write,
1764251881Speter                                     svn_boolean_t ignore_enoent,
1765251881Speter                                     apr_pool_t *pool)
1766251881Speter{
1767251881Speter  if (enable_write)
1768251881Speter    return svn_io_set_file_read_write(path, ignore_enoent, pool);
1769251881Speter  return svn_io_set_file_read_only(path, ignore_enoent, pool);
1770251881Speter}
1771251881Speter
1772251881Spetersvn_error_t *
1773251881Spetersvn_io_set_file_read_only(const char *path,
1774251881Speter                          svn_boolean_t ignore_enoent,
1775251881Speter                          apr_pool_t *pool)
1776251881Speter{
1777251881Speter  /* On Windows and OS/2, just set the file attributes -- on unix call
1778251881Speter     our internal function which attempts to honor the umask. */
1779251881Speter#if !defined(WIN32) && !defined(__OS2__)
1780251881Speter  return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE,
1781251881Speter                           ignore_enoent, pool);
1782251881Speter#else
1783251881Speter  apr_status_t status;
1784251881Speter  const char *path_apr;
1785251881Speter
1786251881Speter  SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1787251881Speter
1788251881Speter  status = apr_file_attrs_set(path_apr,
1789251881Speter                              APR_FILE_ATTR_READONLY,
1790251881Speter                              APR_FILE_ATTR_READONLY,
1791251881Speter                              pool);
1792251881Speter
1793251881Speter  if (status && status != APR_ENOTIMPL)
1794251881Speter    if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
1795251881Speter      return svn_error_wrap_apr(status,
1796251881Speter                                _("Can't set file '%s' read-only"),
1797251881Speter                                svn_dirent_local_style(path, pool));
1798251881Speter
1799251881Speter  return SVN_NO_ERROR;
1800251881Speter#endif
1801251881Speter}
1802251881Speter
1803251881Speter
1804251881Spetersvn_error_t *
1805251881Spetersvn_io_set_file_read_write(const char *path,
1806251881Speter                           svn_boolean_t ignore_enoent,
1807251881Speter                           apr_pool_t *pool)
1808251881Speter{
1809251881Speter  /* On Windows and OS/2, just set the file attributes -- on unix call
1810251881Speter     our internal function which attempts to honor the umask. */
1811251881Speter#if !defined(WIN32) && !defined(__OS2__)
1812251881Speter  return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE,
1813251881Speter                           ignore_enoent, pool);
1814251881Speter#else
1815251881Speter  apr_status_t status;
1816251881Speter  const char *path_apr;
1817251881Speter
1818251881Speter  SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1819251881Speter
1820251881Speter  status = apr_file_attrs_set(path_apr,
1821251881Speter                              0,
1822251881Speter                              APR_FILE_ATTR_READONLY,
1823251881Speter                              pool);
1824251881Speter
1825251881Speter  if (status && status != APR_ENOTIMPL)
1826251881Speter    if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
1827251881Speter      return svn_error_wrap_apr(status,
1828251881Speter                                _("Can't set file '%s' read-write"),
1829251881Speter                                svn_dirent_local_style(path, pool));
1830251881Speter
1831251881Speter  return SVN_NO_ERROR;
1832251881Speter#endif
1833251881Speter}
1834251881Speter
1835251881Spetersvn_error_t *
1836251881Spetersvn_io_set_file_executable(const char *path,
1837251881Speter                           svn_boolean_t executable,
1838251881Speter                           svn_boolean_t ignore_enoent,
1839251881Speter                           apr_pool_t *pool)
1840251881Speter{
1841251881Speter  /* On Windows and OS/2, just exit -- on unix call our internal function
1842251881Speter  which attempts to honor the umask. */
1843251881Speter#if (!defined(WIN32) && !defined(__OS2__))
1844251881Speter  return io_set_file_perms(path, FALSE, FALSE, TRUE, executable,
1845251881Speter                           ignore_enoent, pool);
1846251881Speter#else
1847251881Speter  return SVN_NO_ERROR;
1848251881Speter#endif
1849251881Speter}
1850251881Speter
1851251881Speter
1852251881Spetersvn_error_t *
1853251881Spetersvn_io__is_finfo_read_only(svn_boolean_t *read_only,
1854251881Speter                           apr_finfo_t *file_info,
1855251881Speter                           apr_pool_t *pool)
1856251881Speter{
1857251881Speter#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1858251881Speter  apr_status_t apr_err;
1859251881Speter  apr_uid_t uid;
1860251881Speter  apr_gid_t gid;
1861251881Speter
1862251881Speter  *read_only = FALSE;
1863251881Speter
1864251881Speter  apr_err = apr_uid_current(&uid, &gid, pool);
1865251881Speter
1866251881Speter  if (apr_err)
1867251881Speter    return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
1868251881Speter
1869251881Speter  /* Check write bit for current user. */
1870251881Speter  if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
1871251881Speter    *read_only = !(file_info->protection & APR_UWRITE);
1872251881Speter
1873251881Speter  else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1874251881Speter    *read_only = !(file_info->protection & APR_GWRITE);
1875251881Speter
1876251881Speter  else
1877251881Speter    *read_only = !(file_info->protection & APR_WWRITE);
1878251881Speter
1879251881Speter#else  /* WIN32 || __OS2__ || !APR_HAS_USER */
1880251881Speter  *read_only = (file_info->protection & APR_FREADONLY);
1881251881Speter#endif
1882251881Speter
1883251881Speter  return SVN_NO_ERROR;
1884251881Speter}
1885251881Speter
1886251881Spetersvn_error_t *
1887251881Spetersvn_io__is_finfo_executable(svn_boolean_t *executable,
1888251881Speter                            apr_finfo_t *file_info,
1889251881Speter                            apr_pool_t *pool)
1890251881Speter{
1891251881Speter#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1892251881Speter  apr_status_t apr_err;
1893251881Speter  apr_uid_t uid;
1894251881Speter  apr_gid_t gid;
1895251881Speter
1896251881Speter  *executable = FALSE;
1897251881Speter
1898251881Speter  apr_err = apr_uid_current(&uid, &gid, pool);
1899251881Speter
1900251881Speter  if (apr_err)
1901251881Speter    return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
1902251881Speter
1903251881Speter  /* Check executable bit for current user. */
1904251881Speter  if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
1905251881Speter    *executable = (file_info->protection & APR_UEXECUTE);
1906251881Speter
1907251881Speter  else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
1908251881Speter    *executable = (file_info->protection & APR_GEXECUTE);
1909251881Speter
1910251881Speter  else
1911251881Speter    *executable = (file_info->protection & APR_WEXECUTE);
1912251881Speter
1913251881Speter#else  /* WIN32 || __OS2__ || !APR_HAS_USER */
1914251881Speter  *executable = FALSE;
1915251881Speter#endif
1916251881Speter
1917251881Speter  return SVN_NO_ERROR;
1918251881Speter}
1919251881Speter
1920251881Spetersvn_error_t *
1921251881Spetersvn_io_is_file_executable(svn_boolean_t *executable,
1922251881Speter                          const char *path,
1923251881Speter                          apr_pool_t *pool)
1924251881Speter{
1925251881Speter#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
1926251881Speter  apr_finfo_t file_info;
1927251881Speter
1928251881Speter  SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
1929251881Speter                      pool));
1930251881Speter  SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
1931251881Speter
1932251881Speter#else  /* WIN32 || __OS2__ || !APR_HAS_USER */
1933251881Speter  *executable = FALSE;
1934251881Speter#endif
1935251881Speter
1936251881Speter  return SVN_NO_ERROR;
1937251881Speter}
1938251881Speter
1939251881Speter
1940251881Speter/*** File locking. ***/
1941251881Speter#if !defined(WIN32) && !defined(__OS2__)
1942251881Speter/* Clear all outstanding locks on ARG, an open apr_file_t *. */
1943251881Speterstatic apr_status_t
1944251881Speterfile_clear_locks(void *arg)
1945251881Speter{
1946251881Speter  apr_status_t apr_err;
1947251881Speter  apr_file_t *f = arg;
1948251881Speter
1949251881Speter  /* Remove locks. */
1950251881Speter  apr_err = apr_file_unlock(f);
1951251881Speter  if (apr_err)
1952251881Speter    return apr_err;
1953251881Speter
1954251881Speter  return 0;
1955251881Speter}
1956251881Speter#endif
1957251881Speter
1958251881Spetersvn_error_t *
1959251881Spetersvn_io_lock_open_file(apr_file_t *lockfile_handle,
1960251881Speter                      svn_boolean_t exclusive,
1961251881Speter                      svn_boolean_t nonblocking,
1962251881Speter                      apr_pool_t *pool)
1963251881Speter{
1964251881Speter  int locktype = APR_FLOCK_SHARED;
1965251881Speter  apr_status_t apr_err;
1966251881Speter  const char *fname;
1967251881Speter
1968251881Speter  if (exclusive)
1969251881Speter    locktype = APR_FLOCK_EXCLUSIVE;
1970251881Speter  if (nonblocking)
1971251881Speter    locktype |= APR_FLOCK_NONBLOCK;
1972251881Speter
1973251881Speter  /* We need this only in case of an error but this is cheap to get -
1974251881Speter   * so we do it here for clarity. */
1975251881Speter  apr_err = apr_file_name_get(&fname, lockfile_handle);
1976251881Speter  if (apr_err)
1977251881Speter    return svn_error_wrap_apr(apr_err, _("Can't get file name"));
1978251881Speter
1979251881Speter  /* Get lock on the filehandle. */
1980251881Speter  apr_err = apr_file_lock(lockfile_handle, locktype);
1981251881Speter
1982251881Speter  /* In deployments with two or more multithreaded servers running on
1983251881Speter     the same system serving two or more fsfs repositories it is
1984251881Speter     possible for a deadlock to occur when getting a write lock on
1985251881Speter     db/txn-current-lock:
1986251881Speter
1987251881Speter     Process 1                         Process 2
1988251881Speter     ---------                         ---------
1989251881Speter     thread 1: get lock in repos A
1990251881Speter                                       thread 1: get lock in repos B
1991251881Speter                                       thread 2: block getting lock in repos A
1992251881Speter     thread 2: try to get lock in B *** deadlock ***
1993251881Speter
1994251881Speter     Retry for a while for the deadlock to clear. */
1995251881Speter  FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
1996251881Speter
1997251881Speter  if (apr_err)
1998251881Speter    {
1999251881Speter      switch (locktype & APR_FLOCK_TYPEMASK)
2000251881Speter        {
2001251881Speter        case APR_FLOCK_SHARED:
2002251881Speter          return svn_error_wrap_apr(apr_err,
2003251881Speter                                    _("Can't get shared lock on file '%s'"),
2004251881Speter                                    try_utf8_from_internal_style(fname, pool));
2005251881Speter        case APR_FLOCK_EXCLUSIVE:
2006251881Speter          return svn_error_wrap_apr(apr_err,
2007251881Speter                                    _("Can't get exclusive lock on file '%s'"),
2008251881Speter                                    try_utf8_from_internal_style(fname, pool));
2009251881Speter        default:
2010251881Speter          SVN_ERR_MALFUNCTION();
2011251881Speter        }
2012251881Speter    }
2013251881Speter
2014251881Speter/* On Windows and OS/2 file locks are automatically released when
2015251881Speter   the file handle closes */
2016251881Speter#if !defined(WIN32) && !defined(__OS2__)
2017251881Speter  apr_pool_cleanup_register(pool, lockfile_handle,
2018251881Speter                            file_clear_locks,
2019251881Speter                            apr_pool_cleanup_null);
2020251881Speter#endif
2021251881Speter
2022251881Speter  return SVN_NO_ERROR;
2023251881Speter}
2024251881Speter
2025251881Spetersvn_error_t *
2026251881Spetersvn_io_unlock_open_file(apr_file_t *lockfile_handle,
2027251881Speter                        apr_pool_t *pool)
2028251881Speter{
2029251881Speter  const char *fname;
2030251881Speter  apr_status_t apr_err;
2031251881Speter
2032251881Speter  /* We need this only in case of an error but this is cheap to get -
2033251881Speter   * so we do it here for clarity. */
2034251881Speter  apr_err = apr_file_name_get(&fname, lockfile_handle);
2035251881Speter  if (apr_err)
2036251881Speter    return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2037251881Speter
2038251881Speter  /* The actual unlock attempt. */
2039251881Speter  apr_err = apr_file_unlock(lockfile_handle);
2040251881Speter  if (apr_err)
2041251881Speter    return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
2042251881Speter                              try_utf8_from_internal_style(fname, pool));
2043251881Speter
2044251881Speter/* On Windows and OS/2 file locks are automatically released when
2045251881Speter   the file handle closes */
2046251881Speter#if !defined(WIN32) && !defined(__OS2__)
2047251881Speter  apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks);
2048251881Speter#endif
2049251881Speter
2050251881Speter  return SVN_NO_ERROR;
2051251881Speter}
2052251881Speter
2053251881Spetersvn_error_t *
2054251881Spetersvn_io_file_lock2(const char *lock_file,
2055251881Speter                  svn_boolean_t exclusive,
2056251881Speter                  svn_boolean_t nonblocking,
2057251881Speter                  apr_pool_t *pool)
2058251881Speter{
2059251881Speter  int locktype = APR_FLOCK_SHARED;
2060251881Speter  apr_file_t *lockfile_handle;
2061251881Speter  apr_int32_t flags;
2062251881Speter
2063251881Speter  if (exclusive)
2064251881Speter    locktype = APR_FLOCK_EXCLUSIVE;
2065251881Speter
2066251881Speter  flags = APR_READ;
2067251881Speter  if (locktype == APR_FLOCK_EXCLUSIVE)
2068251881Speter    flags |= APR_WRITE;
2069251881Speter
2070251881Speter  /* locktype is never read after this block, so we don't need to bother
2071251881Speter     setting it.  If that were to ever change, uncomment the following
2072251881Speter     block.
2073251881Speter  if (nonblocking)
2074251881Speter    locktype |= APR_FLOCK_NONBLOCK;
2075251881Speter  */
2076251881Speter
2077251881Speter  SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
2078251881Speter                           APR_OS_DEFAULT,
2079251881Speter                           pool));
2080251881Speter
2081251881Speter  /* Get lock on the filehandle. */
2082251881Speter  return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
2083251881Speter}
2084251881Speter
2085251881Speter
2086251881Speter
2087251881Speter/* Data consistency/coherency operations. */
2088251881Speter
2089251881Spetersvn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
2090251881Speter                                       apr_pool_t *pool)
2091251881Speter{
2092251881Speter  apr_os_file_t filehand;
2093251881Speter
2094251881Speter  /* First make sure that any user-space buffered data is flushed. */
2095251881Speter  SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
2096251881Speter                                     N_("Can't flush file '%s'"),
2097251881Speter                                     N_("Can't flush stream"),
2098251881Speter                                     pool));
2099251881Speter
2100251881Speter  apr_os_file_get(&filehand, file);
2101251881Speter
2102251881Speter  /* Call the operating system specific function to actually force the
2103251881Speter     data to disk. */
2104251881Speter  {
2105251881Speter#ifdef WIN32
2106251881Speter
2107251881Speter    if (! FlushFileBuffers(filehand))
2108251881Speter        return svn_error_wrap_apr(apr_get_os_error(),
2109251881Speter                                  _("Can't flush file to disk"));
2110251881Speter
2111251881Speter#else
2112251881Speter      int rv;
2113251881Speter
2114251881Speter      do {
2115251881Speter        rv = fsync(filehand);
2116251881Speter      } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
2117251881Speter
2118251881Speter      /* If the file is in a memory filesystem, fsync() may return
2119251881Speter         EINVAL.  Presumably the user knows the risks, and we can just
2120251881Speter         ignore the error. */
2121251881Speter      if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
2122251881Speter        return SVN_NO_ERROR;
2123251881Speter
2124251881Speter      if (rv == -1)
2125251881Speter        return svn_error_wrap_apr(apr_get_os_error(),
2126251881Speter                                  _("Can't flush file to disk"));
2127251881Speter
2128251881Speter#endif
2129251881Speter  }
2130251881Speter  return SVN_NO_ERROR;
2131251881Speter}
2132251881Speter
2133251881Speter
2134251881Speter
2135251881Speter/* TODO write test for these two functions, then refactor. */
2136251881Speter
2137251881Speter/* Set RESULT to an svn_stringbuf_t containing the contents of FILE.
2138251881Speter   FILENAME is the FILE's on-disk APR-safe name, or NULL if that name
2139251881Speter   isn't known.  If CHECK_SIZE is TRUE, the function will attempt to
2140251881Speter   first stat() the file to determine it's size before sucking its
2141251881Speter   contents into the stringbuf.  (Doing so can prevent unnecessary
2142251881Speter   memory usage, an unwanted side effect of the stringbuf growth and
2143251881Speter   reallocation mechanism.)  */
2144251881Speterstatic svn_error_t *
2145251881Speterstringbuf_from_aprfile(svn_stringbuf_t **result,
2146251881Speter                       const char *filename,
2147251881Speter                       apr_file_t *file,
2148251881Speter                       svn_boolean_t check_size,
2149251881Speter                       apr_pool_t *pool)
2150251881Speter{
2151251881Speter  apr_size_t len;
2152251881Speter  svn_error_t *err;
2153251881Speter  svn_stringbuf_t *res = NULL;
2154251881Speter  apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE;
2155251881Speter  char *buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
2156251881Speter
2157251881Speter  /* If our caller wants us to check the size of the file for
2158251881Speter     efficient memory handling, we'll try to do so. */
2159251881Speter  if (check_size)
2160251881Speter    {
2161251881Speter      apr_status_t status;
2162251881Speter
2163251881Speter      /* If our caller didn't tell us the file's name, we'll ask APR
2164251881Speter         if it knows the name.  No problem if we can't figure it out.  */
2165251881Speter      if (! filename)
2166251881Speter        {
2167251881Speter          const char *filename_apr;
2168251881Speter          if (! (status = apr_file_name_get(&filename_apr, file)))
2169251881Speter            filename = filename_apr;
2170251881Speter        }
2171251881Speter
2172251881Speter      /* If we now know the filename, try to stat().  If we succeed,
2173251881Speter         we know how to allocate our stringbuf.  */
2174251881Speter      if (filename)
2175251881Speter        {
2176251881Speter          apr_finfo_t finfo;
2177251881Speter          if (! (status = apr_stat(&finfo, filename, APR_FINFO_MIN, pool)))
2178251881Speter            res_initial_len = (apr_size_t)finfo.size;
2179251881Speter        }
2180251881Speter    }
2181251881Speter
2182251881Speter
2183251881Speter  /* XXX: We should check the incoming data for being of type binary. */
2184251881Speter
2185251881Speter  res = svn_stringbuf_create_ensure(res_initial_len, pool);
2186251881Speter
2187251881Speter  /* apr_file_read will not return data and eof in the same call. So this loop
2188251881Speter   * is safe from missing read data.  */
2189251881Speter  len = SVN__STREAM_CHUNK_SIZE;
2190251881Speter  err = svn_io_file_read(file, buf, &len, pool);
2191251881Speter  while (! err)
2192251881Speter    {
2193251881Speter      svn_stringbuf_appendbytes(res, buf, len);
2194251881Speter      len = SVN__STREAM_CHUNK_SIZE;
2195251881Speter      err = svn_io_file_read(file, buf, &len, pool);
2196251881Speter    }
2197251881Speter
2198251881Speter  /* Having read all the data we *expect* EOF */
2199251881Speter  if (err && !APR_STATUS_IS_EOF(err->apr_err))
2200251881Speter    return err;
2201251881Speter  svn_error_clear(err);
2202251881Speter
2203251881Speter  *result = res;
2204251881Speter  return SVN_NO_ERROR;
2205251881Speter}
2206251881Speter
2207251881Spetersvn_error_t *
2208251881Spetersvn_stringbuf_from_file2(svn_stringbuf_t **result,
2209251881Speter                         const char *filename,
2210251881Speter                         apr_pool_t *pool)
2211251881Speter{
2212251881Speter  apr_file_t *f;
2213251881Speter
2214251881Speter  if (filename[0] == '-' && filename[1] == '\0')
2215251881Speter    {
2216251881Speter      apr_status_t apr_err;
2217251881Speter      if ((apr_err = apr_file_open_stdin(&f, pool)))
2218251881Speter        return svn_error_wrap_apr(apr_err, _("Can't open stdin"));
2219251881Speter      SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool));
2220251881Speter    }
2221251881Speter  else
2222251881Speter    {
2223251881Speter      SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool));
2224251881Speter      SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool));
2225251881Speter    }
2226251881Speter  return svn_io_file_close(f, pool);
2227251881Speter}
2228251881Speter
2229251881Speter
2230251881Spetersvn_error_t *
2231251881Spetersvn_stringbuf_from_file(svn_stringbuf_t **result,
2232251881Speter                        const char *filename,
2233251881Speter                        apr_pool_t *pool)
2234251881Speter{
2235251881Speter  if (filename[0] == '-' && filename[1] == '\0')
2236251881Speter    return svn_error_create
2237251881Speter        (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2238251881Speter         _("Reading from stdin is disallowed"));
2239251881Speter  return svn_stringbuf_from_file2(result, filename, pool);
2240251881Speter}
2241251881Speter
2242251881Spetersvn_error_t *
2243251881Spetersvn_stringbuf_from_aprfile(svn_stringbuf_t **result,
2244251881Speter                           apr_file_t *file,
2245251881Speter                           apr_pool_t *pool)
2246251881Speter{
2247251881Speter  return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
2248251881Speter}
2249251881Speter
2250251881Speter
2251251881Speter
2252251881Speter/* Deletion. */
2253251881Speter
2254251881Spetersvn_error_t *
2255251881Spetersvn_io_remove_file2(const char *path,
2256251881Speter                    svn_boolean_t ignore_enoent,
2257251881Speter                    apr_pool_t *scratch_pool)
2258251881Speter{
2259251881Speter  apr_status_t apr_err;
2260251881Speter  const char *path_apr;
2261251881Speter
2262251881Speter  SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
2263251881Speter
2264251881Speter  apr_err = apr_file_remove(path_apr, scratch_pool);
2265251881Speter  if (!apr_err
2266251881Speter      || (ignore_enoent
2267251881Speter          && (APR_STATUS_IS_ENOENT(apr_err)
2268251881Speter              || SVN__APR_STATUS_IS_ENOTDIR(apr_err))))
2269251881Speter    return SVN_NO_ERROR;
2270251881Speter
2271251881Speter#ifdef WIN32
2272251881Speter  /* If the target is read only NTFS reports EACCESS and FAT/FAT32
2273251881Speter     reports EEXIST */
2274251881Speter  if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
2275251881Speter    {
2276251881Speter      /* Set the destination file writable because Windows will not
2277251881Speter         allow us to delete when path is read-only */
2278251881Speter      SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool));
2279251881Speter      apr_err = apr_file_remove(path_apr, scratch_pool);
2280251881Speter
2281251881Speter      if (!apr_err)
2282251881Speter        return SVN_NO_ERROR;
2283251881Speter    }
2284251881Speter
2285251881Speter    {
2286251881Speter      apr_status_t os_err = APR_TO_OS_ERROR(apr_err);
2287251881Speter      /* Check to make sure we aren't trying to delete a directory */
2288251881Speter      if (os_err == ERROR_ACCESS_DENIED || os_err == ERROR_SHARING_VIOLATION)
2289251881Speter        {
2290251881Speter          apr_finfo_t finfo;
2291251881Speter
2292251881Speter          if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
2293251881Speter              && finfo.filetype == APR_REG)
2294251881Speter            {
2295251881Speter              WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr,
2296251881Speter                                                        scratch_pool));
2297251881Speter            }
2298251881Speter        }
2299251881Speter
2300251881Speter      /* Just return the delete error */
2301251881Speter    }
2302251881Speter#endif
2303251881Speter
2304251881Speter  if (apr_err)
2305251881Speter    return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
2306251881Speter                              svn_dirent_local_style(path, scratch_pool));
2307251881Speter
2308251881Speter  return SVN_NO_ERROR;
2309251881Speter}
2310251881Speter
2311251881Speter
2312251881Spetersvn_error_t *
2313251881Spetersvn_io_remove_dir(const char *path, apr_pool_t *pool)
2314251881Speter{
2315251881Speter  return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
2316251881Speter}
2317251881Speter
2318251881Speter/*
2319251881Speter Mac OS X has a bug where if you're reading the contents of a
2320251881Speter directory via readdir in a loop, and you remove one of the entries in
2321251881Speter the directory and the directory has 338 or more files in it you will
2322251881Speter skip over some of the entries in the directory.  Needless to say,
2323251881Speter this causes problems if you are using this kind of loop inside a
2324251881Speter function that is recursively deleting a directory, because when you
2325251881Speter get around to removing the directory it will still have something in
2326251881Speter it. A similar problem has been observed in other BSDs. This bug has
2327251881Speter since been fixed. See http://www.vnode.ch/fixing_seekdir for details.
2328251881Speter
2329251881Speter The workaround is to delete the files only _after_ the initial
2330251881Speter directory scan.  A previous workaround involving rewinddir is
2331251881Speter problematic on Win32 and some NFS clients, notably NetBSD.
2332251881Speter
2333251881Speter See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
2334251881Speter http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
2335251881Speter*/
2336251881Speter
2337251881Speter/* Neither windows nor unix allows us to delete a non-empty
2338251881Speter   directory.
2339251881Speter
2340251881Speter   This is a function to perform the equivalent of 'rm -rf'. */
2341251881Spetersvn_error_t *
2342251881Spetersvn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
2343251881Speter                   svn_cancel_func_t cancel_func, void *cancel_baton,
2344251881Speter                   apr_pool_t *pool)
2345251881Speter{
2346251881Speter  svn_error_t *err;
2347251881Speter  apr_pool_t *subpool;
2348251881Speter  apr_hash_t *dirents;
2349251881Speter  apr_hash_index_t *hi;
2350251881Speter
2351251881Speter  /* Check for pending cancellation request.
2352251881Speter     If we need to bail out, do so early. */
2353251881Speter
2354251881Speter  if (cancel_func)
2355251881Speter    SVN_ERR((*cancel_func)(cancel_baton));
2356251881Speter
2357251881Speter  subpool = svn_pool_create(pool);
2358251881Speter
2359251881Speter  err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
2360251881Speter  if (err)
2361251881Speter    {
2362251881Speter      /* if the directory doesn't exist, our mission is accomplished */
2363251881Speter      if (ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err))
2364251881Speter        {
2365251881Speter          svn_error_clear(err);
2366251881Speter          return SVN_NO_ERROR;
2367251881Speter        }
2368251881Speter      return svn_error_trace(err);
2369251881Speter    }
2370251881Speter
2371251881Speter  for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
2372251881Speter    {
2373251881Speter      const char *name = svn__apr_hash_index_key(hi);
2374251881Speter      const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
2375251881Speter      const char *fullpath;
2376251881Speter
2377251881Speter      fullpath = svn_dirent_join(path, name, subpool);
2378251881Speter      if (dirent->kind == svn_node_dir)
2379251881Speter        {
2380251881Speter          /* Don't check for cancellation, the callee will immediately do so */
2381251881Speter          SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func,
2382251881Speter                                     cancel_baton, subpool));
2383251881Speter        }
2384251881Speter      else
2385251881Speter        {
2386251881Speter          if (cancel_func)
2387251881Speter            SVN_ERR((*cancel_func)(cancel_baton));
2388251881Speter
2389251881Speter          err = svn_io_remove_file2(fullpath, FALSE, subpool);
2390251881Speter          if (err)
2391251881Speter            return svn_error_createf
2392251881Speter              (err->apr_err, err, _("Can't remove '%s'"),
2393251881Speter               svn_dirent_local_style(fullpath, subpool));
2394251881Speter        }
2395251881Speter    }
2396251881Speter
2397251881Speter  svn_pool_destroy(subpool);
2398251881Speter
2399251881Speter  return svn_io_dir_remove_nonrecursive(path, pool);
2400251881Speter}
2401251881Speter
2402251881Spetersvn_error_t *
2403251881Spetersvn_io_get_dir_filenames(apr_hash_t **dirents,
2404251881Speter                         const char *path,
2405251881Speter                         apr_pool_t *pool)
2406251881Speter{
2407251881Speter  return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
2408251881Speter                                             pool, pool));
2409251881Speter}
2410251881Speter
2411251881Spetersvn_io_dirent2_t *
2412251881Spetersvn_io_dirent2_create(apr_pool_t *result_pool)
2413251881Speter{
2414251881Speter  svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
2415251881Speter
2416251881Speter  /*dirent->kind = svn_node_none;
2417251881Speter  dirent->special = FALSE;*/
2418251881Speter  dirent->filesize = SVN_INVALID_FILESIZE;
2419251881Speter  /*dirent->mtime = 0;*/
2420251881Speter
2421251881Speter  return dirent;
2422251881Speter}
2423251881Speter
2424251881Spetersvn_io_dirent2_t *
2425251881Spetersvn_io_dirent2_dup(const svn_io_dirent2_t *item,
2426251881Speter                   apr_pool_t *result_pool)
2427251881Speter{
2428251881Speter  return apr_pmemdup(result_pool,
2429251881Speter                     item,
2430251881Speter                     sizeof(*item));
2431251881Speter}
2432251881Speter
2433251881Spetersvn_error_t *
2434251881Spetersvn_io_get_dirents3(apr_hash_t **dirents,
2435251881Speter                    const char *path,
2436251881Speter                    svn_boolean_t only_check_type,
2437251881Speter                    apr_pool_t *result_pool,
2438251881Speter                    apr_pool_t *scratch_pool)
2439251881Speter{
2440251881Speter  apr_status_t status;
2441251881Speter  apr_dir_t *this_dir;
2442251881Speter  apr_finfo_t this_entry;
2443251881Speter  apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
2444251881Speter
2445251881Speter  if (!only_check_type)
2446251881Speter    flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
2447251881Speter
2448251881Speter  *dirents = apr_hash_make(result_pool);
2449251881Speter
2450251881Speter  SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
2451251881Speter
2452251881Speter  for (status = apr_dir_read(&this_entry, flags, this_dir);
2453251881Speter       status == APR_SUCCESS;
2454251881Speter       status = apr_dir_read(&this_entry, flags, this_dir))
2455251881Speter    {
2456251881Speter      if ((this_entry.name[0] == '.')
2457251881Speter          && ((this_entry.name[1] == '\0')
2458251881Speter              || ((this_entry.name[1] == '.')
2459251881Speter                  && (this_entry.name[2] == '\0'))))
2460251881Speter        {
2461251881Speter          continue;
2462251881Speter        }
2463251881Speter      else
2464251881Speter        {
2465251881Speter          const char *name;
2466251881Speter          svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
2467251881Speter
2468251881Speter          SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
2469251881Speter
2470251881Speter          map_apr_finfo_to_node_kind(&(dirent->kind),
2471251881Speter                                     &(dirent->special),
2472251881Speter                                     &this_entry);
2473251881Speter
2474251881Speter          if (!only_check_type)
2475251881Speter            {
2476251881Speter              dirent->filesize = this_entry.size;
2477251881Speter              dirent->mtime = this_entry.mtime;
2478251881Speter            }
2479251881Speter
2480251881Speter          svn_hash_sets(*dirents, name, dirent);
2481251881Speter        }
2482251881Speter    }
2483251881Speter
2484251881Speter  if (! (APR_STATUS_IS_ENOENT(status)))
2485251881Speter    return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
2486251881Speter                              svn_dirent_local_style(path, scratch_pool));
2487251881Speter
2488251881Speter  status = apr_dir_close(this_dir);
2489251881Speter  if (status)
2490251881Speter    return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
2491251881Speter                              svn_dirent_local_style(path, scratch_pool));
2492251881Speter
2493251881Speter  return SVN_NO_ERROR;
2494251881Speter}
2495251881Speter
2496251881Spetersvn_error_t *
2497251881Spetersvn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
2498251881Speter                    const char *path,
2499251881Speter                    svn_boolean_t verify_truename,
2500251881Speter                    svn_boolean_t ignore_enoent,
2501251881Speter                    apr_pool_t *result_pool,
2502251881Speter                    apr_pool_t *scratch_pool)
2503251881Speter{
2504251881Speter  apr_finfo_t finfo;
2505251881Speter  svn_io_dirent2_t *dirent;
2506251881Speter  svn_error_t *err;
2507251881Speter  apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
2508251881Speter                       | APR_FINFO_SIZE | APR_FINFO_MTIME;
2509251881Speter
2510251881Speter#if defined(WIN32) || defined(__OS2__)
2511251881Speter  if (verify_truename)
2512251881Speter    wanted |= APR_FINFO_NAME;
2513251881Speter#endif
2514251881Speter
2515251881Speter  err = svn_io_stat(&finfo, path, wanted, scratch_pool);
2516251881Speter
2517251881Speter  if (err && ignore_enoent &&
2518251881Speter      (APR_STATUS_IS_ENOENT(err->apr_err)
2519251881Speter       || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2520251881Speter    {
2521251881Speter      svn_error_clear(err);
2522251881Speter      dirent = svn_io_dirent2_create(result_pool);
2523251881Speter      SVN_ERR_ASSERT(dirent->kind == svn_node_none);
2524251881Speter
2525251881Speter      *dirent_p = dirent;
2526251881Speter      return SVN_NO_ERROR;
2527251881Speter    }
2528251881Speter  SVN_ERR(err);
2529251881Speter
2530251881Speter#if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
2531251881Speter  if (verify_truename)
2532251881Speter    {
2533251881Speter      const char *requested_name = svn_dirent_basename(path, NULL);
2534251881Speter
2535251881Speter      if (requested_name[0] == '\0')
2536251881Speter        {
2537251881Speter          /* No parent directory. No need to stat/verify */
2538251881Speter        }
2539251881Speter#if defined(WIN32) || defined(__OS2__)
2540251881Speter      else if (finfo.name)
2541251881Speter        {
2542251881Speter          const char *name_on_disk;
2543251881Speter          SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
2544251881Speter                                     scratch_pool));
2545251881Speter
2546251881Speter          if (strcmp(name_on_disk, requested_name) /* != 0 */)
2547251881Speter            {
2548251881Speter              if (ignore_enoent)
2549251881Speter                {
2550251881Speter                  *dirent_p = svn_io_dirent2_create(result_pool);
2551251881Speter                  return SVN_NO_ERROR;
2552251881Speter                }
2553251881Speter              else
2554251881Speter                return svn_error_createf(APR_ENOENT, NULL,
2555251881Speter                          _("Path '%s' not found, case obstructed by '%s'"),
2556251881Speter                          svn_dirent_local_style(path, scratch_pool),
2557251881Speter                          name_on_disk);
2558251881Speter            }
2559251881Speter        }
2560251881Speter#elif defined(DARWIN)
2561251881Speter      /* Currently apr doesn't set finfo.name on DARWIN, returning
2562251881Speter                   APR_INCOMPLETE.
2563251881Speter         ### Can we optimize this in another way? */
2564251881Speter      else
2565251881Speter        {
2566251881Speter          apr_hash_t *dirents;
2567251881Speter
2568251881Speter          err = svn_io_get_dirents3(&dirents,
2569251881Speter                                    svn_dirent_dirname(path, scratch_pool),
2570251881Speter                                    TRUE /* only_check_type */,
2571251881Speter                                    scratch_pool, scratch_pool);
2572251881Speter
2573251881Speter          if (err && ignore_enoent
2574251881Speter              && (APR_STATUS_IS_ENOENT(err->apr_err)
2575251881Speter                  || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2576251881Speter            {
2577251881Speter              svn_error_clear(err);
2578251881Speter
2579251881Speter              *dirent_p = svn_io_dirent2_create(result_pool);
2580251881Speter              return SVN_NO_ERROR;
2581251881Speter            }
2582251881Speter          else
2583251881Speter            SVN_ERR(err);
2584251881Speter
2585251881Speter          if (! svn_hash_gets(dirents, requested_name))
2586251881Speter            {
2587251881Speter              if (ignore_enoent)
2588251881Speter                {
2589251881Speter                  *dirent_p = svn_io_dirent2_create(result_pool);
2590251881Speter                  return SVN_NO_ERROR;
2591251881Speter                }
2592251881Speter              else
2593251881Speter                return svn_error_createf(APR_ENOENT, NULL,
2594251881Speter                          _("Path '%s' not found"),
2595251881Speter                          svn_dirent_local_style(path, scratch_pool));
2596251881Speter            }
2597251881Speter        }
2598251881Speter#endif
2599251881Speter    }
2600251881Speter#endif
2601251881Speter
2602251881Speter  dirent = svn_io_dirent2_create(result_pool);
2603251881Speter  map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
2604251881Speter
2605251881Speter  dirent->filesize = finfo.size;
2606251881Speter  dirent->mtime = finfo.mtime;
2607251881Speter
2608251881Speter  *dirent_p = dirent;
2609251881Speter
2610251881Speter  return SVN_NO_ERROR;
2611251881Speter}
2612251881Speter
2613251881Speter/* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2614251881Speter#define ERRFILE_KEY "svn-io-start-cmd-errfile"
2615251881Speter
2616251881Speter/* Handle an error from the child process (before command execution) by
2617251881Speter   printing DESC and the error string corresponding to STATUS to stderr. */
2618251881Speterstatic void
2619251881Speterhandle_child_process_error(apr_pool_t *pool, apr_status_t status,
2620251881Speter                           const char *desc)
2621251881Speter{
2622251881Speter  char errbuf[256];
2623251881Speter  apr_file_t *errfile;
2624251881Speter  void *p;
2625251881Speter
2626251881Speter  /* We can't do anything if we get an error here, so just return. */
2627251881Speter  if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool))
2628251881Speter    return;
2629251881Speter  errfile = p;
2630251881Speter
2631251881Speter  if (errfile)
2632251881Speter    /* What we get from APR is in native encoding. */
2633251881Speter    apr_file_printf(errfile, "%s: %s",
2634251881Speter                    desc, apr_strerror(status, errbuf,
2635251881Speter                                       sizeof(errbuf)));
2636251881Speter}
2637251881Speter
2638251881Speter
2639251881Spetersvn_error_t *
2640251881Spetersvn_io_start_cmd3(apr_proc_t *cmd_proc,
2641251881Speter                  const char *path,
2642251881Speter                  const char *cmd,
2643251881Speter                  const char *const *args,
2644251881Speter                  const char *const *env,
2645251881Speter                  svn_boolean_t inherit,
2646251881Speter                  svn_boolean_t infile_pipe,
2647251881Speter                  apr_file_t *infile,
2648251881Speter                  svn_boolean_t outfile_pipe,
2649251881Speter                  apr_file_t *outfile,
2650251881Speter                  svn_boolean_t errfile_pipe,
2651251881Speter                  apr_file_t *errfile,
2652251881Speter                  apr_pool_t *pool)
2653251881Speter{
2654251881Speter  apr_status_t apr_err;
2655251881Speter  apr_procattr_t *cmdproc_attr;
2656251881Speter  int num_args;
2657251881Speter  const char **args_native;
2658251881Speter  const char *cmd_apr;
2659251881Speter
2660251881Speter  SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
2661251881Speter  SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
2662251881Speter  SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
2663251881Speter
2664251881Speter  /* Create the process attributes. */
2665251881Speter  apr_err = apr_procattr_create(&cmdproc_attr, pool);
2666251881Speter  if (apr_err)
2667251881Speter    return svn_error_wrap_apr(apr_err,
2668251881Speter                              _("Can't create process '%s' attributes"),
2669251881Speter                              cmd);
2670251881Speter
2671251881Speter  /* Make sure we invoke cmd directly, not through a shell. */
2672251881Speter  apr_err = apr_procattr_cmdtype_set(cmdproc_attr,
2673251881Speter                                     inherit ? APR_PROGRAM_PATH : APR_PROGRAM);
2674251881Speter  if (apr_err)
2675251881Speter    return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
2676251881Speter                              cmd);
2677251881Speter
2678251881Speter  /* Set the process's working directory. */
2679251881Speter  if (path)
2680251881Speter    {
2681251881Speter      const char *path_apr;
2682251881Speter
2683251881Speter      SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2684251881Speter      apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
2685251881Speter      if (apr_err)
2686251881Speter        return svn_error_wrap_apr(apr_err,
2687251881Speter                                  _("Can't set process '%s' directory"),
2688251881Speter                                  cmd);
2689251881Speter    }
2690251881Speter
2691251881Speter  /* Use requested inputs and outputs.
2692251881Speter
2693251881Speter     ### Unfortunately each of these apr functions creates a pipe and then
2694251881Speter     overwrites the pipe file descriptor with the descriptor we pass
2695251881Speter     in. The pipes can then never be closed. This is an APR bug. */
2696251881Speter  if (infile)
2697251881Speter    {
2698251881Speter      apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
2699251881Speter      if (apr_err)
2700251881Speter        return svn_error_wrap_apr(apr_err,
2701251881Speter                                  _("Can't set process '%s' child input"),
2702251881Speter                                  cmd);
2703251881Speter    }
2704251881Speter  if (outfile)
2705251881Speter    {
2706251881Speter      apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
2707251881Speter      if (apr_err)
2708251881Speter        return svn_error_wrap_apr(apr_err,
2709251881Speter                                  _("Can't set process '%s' child outfile"),
2710251881Speter                                  cmd);
2711251881Speter    }
2712251881Speter  if (errfile)
2713251881Speter    {
2714251881Speter      apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
2715251881Speter      if (apr_err)
2716251881Speter        return svn_error_wrap_apr(apr_err,
2717251881Speter                                  _("Can't set process '%s' child errfile"),
2718251881Speter                                  cmd);
2719251881Speter    }
2720251881Speter
2721251881Speter  /* Forward request for pipes to APR. */
2722251881Speter  if (infile_pipe || outfile_pipe || errfile_pipe)
2723251881Speter    {
2724251881Speter      apr_err = apr_procattr_io_set(cmdproc_attr,
2725251881Speter                                    infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
2726251881Speter                                    outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
2727251881Speter                                    errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE);
2728251881Speter
2729251881Speter      if (apr_err)
2730251881Speter        return svn_error_wrap_apr(apr_err,
2731251881Speter                                  _("Can't set process '%s' stdio pipes"),
2732251881Speter                                  cmd);
2733251881Speter    }
2734251881Speter
2735251881Speter  /* Have the child print any problems executing its program to errfile. */
2736251881Speter  apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
2737251881Speter  if (apr_err)
2738251881Speter    return svn_error_wrap_apr(apr_err,
2739251881Speter                              _("Can't set process '%s' child errfile for "
2740251881Speter                                "error handler"),
2741251881Speter                              cmd);
2742251881Speter  apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
2743251881Speter                                         handle_child_process_error);
2744251881Speter  if (apr_err)
2745251881Speter    return svn_error_wrap_apr(apr_err,
2746251881Speter                              _("Can't set process '%s' error handler"),
2747251881Speter                              cmd);
2748251881Speter
2749251881Speter  /* Convert cmd and args from UTF-8 */
2750251881Speter  SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool));
2751251881Speter  for (num_args = 0; args[num_args]; num_args++)
2752251881Speter    ;
2753251881Speter  args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
2754251881Speter  args_native[num_args] = NULL;
2755251881Speter  while (num_args--)
2756251881Speter    {
2757251881Speter      /* ### Well, it turns out that on APR on Windows expects all
2758251881Speter             program args to be in UTF-8. Callers of svn_io_run_cmd
2759251881Speter             should be aware of that. */
2760251881Speter      SVN_ERR(cstring_from_utf8(&args_native[num_args],
2761251881Speter                                args[num_args], pool));
2762251881Speter    }
2763251881Speter
2764251881Speter
2765251881Speter  /* Start the cmd command. */
2766251881Speter  apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
2767251881Speter                            inherit ? NULL : env, cmdproc_attr, pool);
2768251881Speter  if (apr_err)
2769251881Speter    return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
2770251881Speter
2771251881Speter  return SVN_NO_ERROR;
2772251881Speter}
2773251881Speter
2774251881Speter#undef ERRFILE_KEY
2775251881Speter
2776251881Spetersvn_error_t *
2777251881Spetersvn_io_wait_for_cmd(apr_proc_t *cmd_proc,
2778251881Speter                    const char *cmd,
2779251881Speter                    int *exitcode,
2780251881Speter                    apr_exit_why_e *exitwhy,
2781251881Speter                    apr_pool_t *pool)
2782251881Speter{
2783251881Speter  apr_status_t apr_err;
2784251881Speter  apr_exit_why_e exitwhy_val;
2785251881Speter  int exitcode_val;
2786251881Speter
2787251881Speter  /* The Win32 apr_proc_wait doesn't set this... */
2788251881Speter  exitwhy_val = APR_PROC_EXIT;
2789251881Speter
2790251881Speter  /* Wait for the cmd command to finish. */
2791251881Speter  apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT);
2792251881Speter  if (!APR_STATUS_IS_CHILD_DONE(apr_err))
2793251881Speter    return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"),
2794251881Speter                              cmd);
2795251881Speter
2796251881Speter  if (exitwhy)
2797251881Speter    *exitwhy = exitwhy_val;
2798251881Speter  else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)
2799251881Speter           && APR_PROC_CHECK_CORE_DUMP(exitwhy_val))
2800251881Speter    return svn_error_createf
2801251881Speter      (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2802251881Speter       _("Process '%s' failed (signal %d, core dumped)"),
2803251881Speter       cmd, exitcode_val);
2804251881Speter  else if (APR_PROC_CHECK_SIGNALED(exitwhy_val))
2805251881Speter    return svn_error_createf
2806251881Speter      (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2807251881Speter       _("Process '%s' failed (signal %d)"),
2808251881Speter       cmd, exitcode_val);
2809251881Speter  else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
2810251881Speter    /* Don't really know what happened here. */
2811251881Speter    return svn_error_createf
2812251881Speter      (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2813251881Speter       _("Process '%s' failed (exitwhy %d, exitcode %d)"),
2814251881Speter       cmd, exitwhy_val, exitcode_val);
2815251881Speter
2816251881Speter  if (exitcode)
2817251881Speter    *exitcode = exitcode_val;
2818251881Speter  else if (exitcode_val != 0)
2819251881Speter    return svn_error_createf
2820251881Speter      (SVN_ERR_EXTERNAL_PROGRAM, NULL,
2821251881Speter       _("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
2822251881Speter
2823251881Speter  return SVN_NO_ERROR;
2824251881Speter}
2825251881Speter
2826251881Speter
2827251881Spetersvn_error_t *
2828251881Spetersvn_io_run_cmd(const char *path,
2829251881Speter               const char *cmd,
2830251881Speter               const char *const *args,
2831251881Speter               int *exitcode,
2832251881Speter               apr_exit_why_e *exitwhy,
2833251881Speter               svn_boolean_t inherit,
2834251881Speter               apr_file_t *infile,
2835251881Speter               apr_file_t *outfile,
2836251881Speter               apr_file_t *errfile,
2837251881Speter               apr_pool_t *pool)
2838251881Speter{
2839251881Speter  apr_proc_t cmd_proc;
2840251881Speter
2841251881Speter  SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
2842251881Speter                            FALSE, infile, FALSE, outfile, FALSE, errfile,
2843251881Speter                            pool));
2844251881Speter
2845251881Speter  return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
2846251881Speter}
2847251881Speter
2848251881Speter
2849251881Spetersvn_error_t *
2850251881Spetersvn_io_run_diff2(const char *dir,
2851251881Speter                 const char *const *user_args,
2852251881Speter                 int num_user_args,
2853251881Speter                 const char *label1,
2854251881Speter                 const char *label2,
2855251881Speter                 const char *from,
2856251881Speter                 const char *to,
2857251881Speter                 int *pexitcode,
2858251881Speter                 apr_file_t *outfile,
2859251881Speter                 apr_file_t *errfile,
2860251881Speter                 const char *diff_cmd,
2861251881Speter                 apr_pool_t *pool)
2862251881Speter{
2863251881Speter  const char **args;
2864251881Speter  int i;
2865251881Speter  int exitcode;
2866251881Speter  int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
2867251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
2868251881Speter
2869251881Speter  if (pexitcode == NULL)
2870251881Speter    pexitcode = &exitcode;
2871251881Speter
2872251881Speter  if (user_args != NULL)
2873251881Speter    nargs += num_user_args;
2874251881Speter  else
2875251881Speter    nargs += 1; /* -u */
2876251881Speter
2877251881Speter  if (label1 != NULL)
2878251881Speter    nargs += 2; /* the -L and the label itself */
2879251881Speter  if (label2 != NULL)
2880251881Speter    nargs += 2; /* the -L and the label itself */
2881251881Speter
2882251881Speter  args = apr_palloc(subpool, nargs * sizeof(char *));
2883251881Speter
2884251881Speter  i = 0;
2885251881Speter  args[i++] = diff_cmd;
2886251881Speter
2887251881Speter  if (user_args != NULL)
2888251881Speter    {
2889251881Speter      int j;
2890251881Speter      for (j = 0; j < num_user_args; ++j)
2891251881Speter        args[i++] = user_args[j];
2892251881Speter    }
2893251881Speter  else
2894251881Speter    args[i++] = "-u"; /* assume -u if the user didn't give us any args */
2895251881Speter
2896251881Speter  if (label1 != NULL)
2897251881Speter    {
2898251881Speter      args[i++] = "-L";
2899251881Speter      args[i++] = label1;
2900251881Speter    }
2901251881Speter  if (label2 != NULL)
2902251881Speter    {
2903251881Speter      args[i++] = "-L";
2904251881Speter      args[i++] = label2;
2905251881Speter    }
2906251881Speter
2907251881Speter  args[i++] = svn_dirent_local_style(from, subpool);
2908251881Speter  args[i++] = svn_dirent_local_style(to, subpool);
2909251881Speter  args[i++] = NULL;
2910251881Speter
2911251881Speter  SVN_ERR_ASSERT(i == nargs);
2912251881Speter
2913251881Speter  SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
2914251881Speter                         NULL, outfile, errfile, subpool));
2915251881Speter
2916251881Speter  /* The man page for (GNU) diff describes the return value as:
2917251881Speter
2918251881Speter       "An exit status of 0 means no differences were found, 1 means
2919251881Speter        some differences were found, and 2 means trouble."
2920251881Speter
2921251881Speter     A return value of 2 typically occurs when diff cannot read its input
2922251881Speter     or write to its output, but in any case we probably ought to return an
2923251881Speter     error for anything other than 0 or 1 as the output is likely to be
2924251881Speter     corrupt.
2925251881Speter   */
2926251881Speter  if (*pexitcode != 0 && *pexitcode != 1)
2927251881Speter    return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
2928251881Speter                             _("'%s' returned %d"),
2929251881Speter                             svn_dirent_local_style(diff_cmd, pool),
2930251881Speter                             *pexitcode);
2931251881Speter
2932251881Speter  svn_pool_destroy(subpool);
2933251881Speter
2934251881Speter  return SVN_NO_ERROR;
2935251881Speter}
2936251881Speter
2937251881Speter
2938251881Spetersvn_error_t *
2939251881Spetersvn_io_run_diff3_3(int *exitcode,
2940251881Speter                   const char *dir,
2941251881Speter                   const char *mine,
2942251881Speter                   const char *older,
2943251881Speter                   const char *yours,
2944251881Speter                   const char *mine_label,
2945251881Speter                   const char *older_label,
2946251881Speter                   const char *yours_label,
2947251881Speter                   apr_file_t *merged,
2948251881Speter                   const char *diff3_cmd,
2949251881Speter                   const apr_array_header_t *user_args,
2950251881Speter                   apr_pool_t *pool)
2951251881Speter{
2952251881Speter  const char **args = apr_palloc(pool,
2953251881Speter                                 sizeof(char*) * (13
2954251881Speter                                                  + (user_args
2955251881Speter                                                     ? user_args->nelts
2956251881Speter                                                     : 1)));
2957251881Speter#ifndef NDEBUG
2958251881Speter  int nargs = 12;
2959251881Speter#endif
2960251881Speter  int i = 0;
2961251881Speter
2962251881Speter  /* Labels fall back to sensible defaults if not specified. */
2963251881Speter  if (mine_label == NULL)
2964251881Speter    mine_label = ".working";
2965251881Speter  if (older_label == NULL)
2966251881Speter    older_label = ".old";
2967251881Speter  if (yours_label == NULL)
2968251881Speter    yours_label = ".new";
2969251881Speter
2970251881Speter  /* Set up diff3 command line. */
2971251881Speter  args[i++] = diff3_cmd;
2972251881Speter  if (user_args)
2973251881Speter    {
2974251881Speter      int j;
2975251881Speter      for (j = 0; j < user_args->nelts; ++j)
2976251881Speter        args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
2977251881Speter#ifndef NDEBUG
2978251881Speter      nargs += user_args->nelts;
2979251881Speter#endif
2980251881Speter    }
2981251881Speter  else
2982251881Speter    {
2983251881Speter      args[i++] = "-E";             /* We tried "-A" here, but that caused
2984251881Speter                                       overlapping identical changes to
2985251881Speter                                       conflict.  See issue #682. */
2986251881Speter#ifndef NDEBUG
2987251881Speter      ++nargs;
2988251881Speter#endif
2989251881Speter    }
2990251881Speter  args[i++] = "-m";
2991251881Speter  args[i++] = "-L";
2992251881Speter  args[i++] = mine_label;
2993251881Speter  args[i++] = "-L";
2994251881Speter  args[i++] = older_label;      /* note:  this label is ignored if
2995251881Speter                                   using 2-part markers, which is the
2996251881Speter                                   case with "-E". */
2997251881Speter  args[i++] = "-L";
2998251881Speter  args[i++] = yours_label;
2999251881Speter#ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
3000251881Speter  {
3001251881Speter    svn_boolean_t has_arg;
3002251881Speter
3003251881Speter    /* ### FIXME: we really shouldn't be reading the config here;
3004251881Speter       instead, the necessary bits should be passed in by the caller.
3005251881Speter       But should we add another parameter to this function, when the
3006251881Speter       whole external diff3 thing might eventually go away?  */
3007251881Speter    apr_hash_t *config;
3008251881Speter    svn_config_t *cfg;
3009251881Speter
3010251881Speter    SVN_ERR(svn_config_get_config(&config, pool));
3011251881Speter    cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
3012251881Speter    SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS,
3013251881Speter                                SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG,
3014251881Speter                                TRUE));
3015251881Speter    if (has_arg)
3016251881Speter      {
3017251881Speter        const char *diff_cmd, *diff_utf8;
3018251881Speter        svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
3019251881Speter                       SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF);
3020251881Speter        SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool));
3021251881Speter        args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8, NULL);
3022251881Speter#ifndef NDEBUG
3023251881Speter        ++nargs;
3024251881Speter#endif
3025251881Speter      }
3026251881Speter  }
3027251881Speter#endif
3028251881Speter  args[i++] = svn_dirent_local_style(mine, pool);
3029251881Speter  args[i++] = svn_dirent_local_style(older, pool);
3030251881Speter  args[i++] = svn_dirent_local_style(yours, pool);
3031251881Speter  args[i++] = NULL;
3032251881Speter#ifndef NDEBUG
3033251881Speter  SVN_ERR_ASSERT(i == nargs);
3034251881Speter#endif
3035251881Speter
3036251881Speter  /* Run diff3, output the merged text into the scratch file. */
3037251881Speter  SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
3038251881Speter                         exitcode, NULL,
3039251881Speter                         TRUE, /* keep environment */
3040251881Speter                         NULL, merged, NULL,
3041251881Speter                         pool));
3042251881Speter
3043251881Speter  /* According to the diff3 docs, a '0' means the merge was clean, and
3044251881Speter     '1' means conflict markers were found.  Anything else is real
3045251881Speter     error. */
3046251881Speter  if ((*exitcode != 0) && (*exitcode != 1))
3047251881Speter    return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3048251881Speter                             _("Error running '%s':  exitcode was %d, "
3049251881Speter                               "args were:"
3050251881Speter                               "\nin directory '%s', basenames:\n%s\n%s\n%s"),
3051251881Speter                             svn_dirent_local_style(diff3_cmd, pool),
3052251881Speter                             *exitcode,
3053251881Speter                             svn_dirent_local_style(dir, pool),
3054251881Speter                             /* Don't call svn_path_local_style() on
3055251881Speter                                the basenames.  We don't want them to
3056251881Speter                                be absolute, and we don't need the
3057251881Speter                                separator conversion. */
3058251881Speter                             mine, older, yours);
3059251881Speter
3060251881Speter  return SVN_NO_ERROR;
3061251881Speter}
3062251881Speter
3063251881Speter
3064251881Speter/* Canonicalize a string for hashing.  Modifies KEY in place. */
3065251881Speterstatic APR_INLINE char *
3066251881Speterfileext_tolower(char *key)
3067251881Speter{
3068251881Speter  register char *p;
3069251881Speter  for (p = key; *p != 0; ++p)
3070251881Speter    *p = (char)apr_tolower(*p);
3071251881Speter  return key;
3072251881Speter}
3073251881Speter
3074251881Speter
3075251881Spetersvn_error_t *
3076251881Spetersvn_io_parse_mimetypes_file(apr_hash_t **type_map,
3077251881Speter                            const char *mimetypes_file,
3078251881Speter                            apr_pool_t *pool)
3079251881Speter{
3080251881Speter  svn_error_t *err = SVN_NO_ERROR;
3081251881Speter  apr_hash_t *types = apr_hash_make(pool);
3082251881Speter  svn_boolean_t eof = FALSE;
3083251881Speter  svn_stringbuf_t *buf;
3084251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
3085251881Speter  apr_file_t *types_file;
3086251881Speter  svn_stream_t *mimetypes_stream;
3087251881Speter
3088251881Speter  SVN_ERR(svn_io_file_open(&types_file, mimetypes_file,
3089251881Speter                           APR_READ, APR_OS_DEFAULT, pool));
3090251881Speter  mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool);
3091251881Speter
3092251881Speter  while (1)
3093251881Speter    {
3094251881Speter      apr_array_header_t *tokens;
3095251881Speter      const char *type;
3096251881Speter
3097251881Speter      svn_pool_clear(subpool);
3098251881Speter
3099251881Speter      /* Read a line. */
3100251881Speter      if ((err = svn_stream_readline(mimetypes_stream, &buf,
3101251881Speter                                     APR_EOL_STR, &eof, subpool)))
3102251881Speter        break;
3103251881Speter
3104251881Speter      /* Only pay attention to non-empty, non-comment lines. */
3105251881Speter      if (buf->len)
3106251881Speter        {
3107251881Speter          int i;
3108251881Speter
3109251881Speter          if (buf->data[0] == '#')
3110251881Speter            continue;
3111251881Speter
3112251881Speter          /* Tokenize (into our return pool). */
3113251881Speter          tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
3114251881Speter          if (tokens->nelts < 2)
3115251881Speter            continue;
3116251881Speter
3117251881Speter          /* The first token in a multi-token line is the media type.
3118251881Speter             Subsequent tokens are filename extensions associated with
3119251881Speter             that media type. */
3120251881Speter          type = APR_ARRAY_IDX(tokens, 0, const char *);
3121251881Speter          for (i = 1; i < tokens->nelts; i++)
3122251881Speter            {
3123251881Speter              /* We can safely address 'ext' as a non-const string because
3124251881Speter               * we know svn_cstring_split() allocated it in 'pool' for us. */
3125251881Speter              char *ext = APR_ARRAY_IDX(tokens, i, char *);
3126251881Speter              fileext_tolower(ext);
3127251881Speter              svn_hash_sets(types, ext, type);
3128251881Speter            }
3129251881Speter        }
3130251881Speter      if (eof)
3131251881Speter        break;
3132251881Speter    }
3133251881Speter  svn_pool_destroy(subpool);
3134251881Speter
3135251881Speter  /* If there was an error above, close the file (ignoring any error
3136251881Speter     from *that*) and return the originally error. */
3137251881Speter  if (err)
3138251881Speter    {
3139251881Speter      svn_error_clear(svn_stream_close(mimetypes_stream));
3140251881Speter      return err;
3141251881Speter    }
3142251881Speter
3143251881Speter  /* Close the stream (which closes the underlying file, too). */
3144251881Speter  SVN_ERR(svn_stream_close(mimetypes_stream));
3145251881Speter
3146251881Speter  *type_map = types;
3147251881Speter  return SVN_NO_ERROR;
3148251881Speter}
3149251881Speter
3150251881Speter
3151251881Spetersvn_error_t *
3152251881Spetersvn_io_detect_mimetype2(const char **mimetype,
3153251881Speter                        const char *file,
3154251881Speter                        apr_hash_t *mimetype_map,
3155251881Speter                        apr_pool_t *pool)
3156251881Speter{
3157251881Speter  static const char * const generic_binary = "application/octet-stream";
3158251881Speter
3159251881Speter  svn_node_kind_t kind;
3160251881Speter  apr_file_t *fh;
3161251881Speter  svn_error_t *err;
3162251881Speter  unsigned char block[1024];
3163251881Speter  apr_size_t amt_read = sizeof(block);
3164251881Speter
3165251881Speter  /* Default return value is NULL. */
3166251881Speter  *mimetype = NULL;
3167251881Speter
3168251881Speter  /* If there is a mimetype_map provided, we'll first try to look up
3169251881Speter     our file's extension in the map.  Failing that, we'll run the
3170251881Speter     heuristic. */
3171251881Speter  if (mimetype_map)
3172251881Speter    {
3173251881Speter      const char *type_from_map;
3174251881Speter      char *path_ext; /* Can point to physical const memory but only when
3175251881Speter                         svn_path_splitext sets it to "". */
3176251881Speter      svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
3177251881Speter      fileext_tolower(path_ext);
3178251881Speter      if ((type_from_map = svn_hash_gets(mimetype_map, path_ext)))
3179251881Speter        {
3180251881Speter          *mimetype = type_from_map;
3181251881Speter          return SVN_NO_ERROR;
3182251881Speter        }
3183251881Speter    }
3184251881Speter
3185251881Speter  /* See if this file even exists, and make sure it really is a file. */
3186251881Speter  SVN_ERR(svn_io_check_path(file, &kind, pool));
3187251881Speter  if (kind != svn_node_file)
3188251881Speter    return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
3189251881Speter                             _("Can't detect MIME type of non-file '%s'"),
3190251881Speter                             svn_dirent_local_style(file, pool));
3191251881Speter
3192251881Speter  SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
3193251881Speter
3194251881Speter  /* Read a block of data from FILE. */
3195251881Speter  err = svn_io_file_read(fh, block, &amt_read, pool);
3196251881Speter  if (err && ! APR_STATUS_IS_EOF(err->apr_err))
3197251881Speter    return err;
3198251881Speter  svn_error_clear(err);
3199251881Speter
3200251881Speter  /* Now close the file.  No use keeping it open any more.  */
3201251881Speter  SVN_ERR(svn_io_file_close(fh, pool));
3202251881Speter
3203251881Speter  if (svn_io_is_binary_data(block, amt_read))
3204251881Speter    *mimetype = generic_binary;
3205251881Speter
3206251881Speter  return SVN_NO_ERROR;
3207251881Speter}
3208251881Speter
3209251881Speter
3210251881Spetersvn_boolean_t
3211251881Spetersvn_io_is_binary_data(const void *data, apr_size_t len)
3212251881Speter{
3213251881Speter  const unsigned char *buf = data;
3214251881Speter
3215251881Speter  if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
3216251881Speter    {
3217251881Speter      /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
3218251881Speter       * Treat it as plain text. */
3219251881Speter      return FALSE;
3220251881Speter    }
3221251881Speter
3222251881Speter  /* Right now, this function is going to be really stupid.  It's
3223251881Speter     going to examine the block of data, and make sure that 15%
3224251881Speter     of the bytes are such that their value is in the ranges 0x07-0x0D
3225251881Speter     or 0x20-0x7F, and that none of those bytes is 0x00.  If those
3226251881Speter     criteria are not met, we're calling it binary.
3227251881Speter
3228251881Speter     NOTE:  Originally, I intended to target 85% of the bytes being in
3229251881Speter     the specified ranges, but I flubbed the condition.  At any rate,
3230251881Speter     folks aren't complaining, so I'm not sure that it's worth
3231251881Speter     adjusting this retroactively now.  --cmpilato  */
3232251881Speter  if (len > 0)
3233251881Speter    {
3234251881Speter      apr_size_t i;
3235251881Speter      apr_size_t binary_count = 0;
3236251881Speter
3237251881Speter      /* Run through the data we've read, counting the 'binary-ish'
3238251881Speter         bytes.  HINT: If we see a 0x00 byte, we'll set our count to its
3239251881Speter         max and stop reading the file. */
3240251881Speter      for (i = 0; i < len; i++)
3241251881Speter        {
3242251881Speter          if (buf[i] == 0)
3243251881Speter            {
3244251881Speter              binary_count = len;
3245251881Speter              break;
3246251881Speter            }
3247251881Speter          if ((buf[i] < 0x07)
3248251881Speter              || ((buf[i] > 0x0D) && (buf[i] < 0x20))
3249251881Speter              || (buf[i] > 0x7F))
3250251881Speter            {
3251251881Speter              binary_count++;
3252251881Speter            }
3253251881Speter        }
3254251881Speter
3255251881Speter      return (((binary_count * 1000) / len) > 850);
3256251881Speter    }
3257251881Speter
3258251881Speter  return FALSE;
3259251881Speter}
3260251881Speter
3261251881Speter
3262251881Spetersvn_error_t *
3263251881Spetersvn_io_detect_mimetype(const char **mimetype,
3264251881Speter                       const char *file,
3265251881Speter                       apr_pool_t *pool)
3266251881Speter{
3267251881Speter  return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
3268251881Speter}
3269251881Speter
3270251881Speter
3271251881Spetersvn_error_t *
3272251881Spetersvn_io_file_open(apr_file_t **new_file, const char *fname,
3273251881Speter                 apr_int32_t flag, apr_fileperms_t perm,
3274251881Speter                 apr_pool_t *pool)
3275251881Speter{
3276251881Speter  const char *fname_apr;
3277251881Speter  apr_status_t status;
3278251881Speter
3279251881Speter  SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3280251881Speter  status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
3281251881Speter                     pool);
3282251881Speter
3283251881Speter  if (status)
3284251881Speter    return svn_error_wrap_apr(status, _("Can't open file '%s'"),
3285251881Speter                              svn_dirent_local_style(fname, pool));
3286251881Speter  else
3287251881Speter    return SVN_NO_ERROR;
3288251881Speter}
3289251881Speter
3290251881Speter
3291251881Speterstatic APR_INLINE svn_error_t *
3292251881Speterdo_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
3293251881Speter                           const char *msg, const char *msg_no_name,
3294251881Speter                           apr_pool_t *pool)
3295251881Speter{
3296251881Speter  const char *name;
3297251881Speter  svn_error_t *err;
3298251881Speter
3299251881Speter  if (! status)
3300251881Speter    return SVN_NO_ERROR;
3301251881Speter
3302251881Speter  err = svn_io_file_name_get(&name, file, pool);
3303251881Speter  if (err)
3304251881Speter    name = NULL;
3305251881Speter  svn_error_clear(err);
3306251881Speter
3307251881Speter  /* ### Issue #3014: Return a specific error for broken pipes,
3308251881Speter   * ### with a single element in the error chain. */
3309251881Speter  if (APR_STATUS_IS_EPIPE(status))
3310251881Speter    return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
3311251881Speter
3312251881Speter  if (name)
3313251881Speter    return svn_error_wrap_apr(status, _(msg),
3314251881Speter                              try_utf8_from_internal_style(name, pool));
3315251881Speter  else
3316251881Speter    return svn_error_wrap_apr(status, "%s", _(msg_no_name));
3317251881Speter}
3318251881Speter
3319251881Speter
3320251881Spetersvn_error_t *
3321251881Spetersvn_io_file_close(apr_file_t *file, apr_pool_t *pool)
3322251881Speter{
3323251881Speter  return do_io_file_wrapper_cleanup(file, apr_file_close(file),
3324251881Speter                                    N_("Can't close file '%s'"),
3325251881Speter                                    N_("Can't close stream"),
3326251881Speter                                    pool);
3327251881Speter}
3328251881Speter
3329251881Speter
3330251881Spetersvn_error_t *
3331251881Spetersvn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
3332251881Speter{
3333251881Speter  return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file),
3334251881Speter                                    N_("Can't read file '%s'"),
3335251881Speter                                    N_("Can't read stream"),
3336251881Speter                                    pool);
3337251881Speter}
3338251881Speter
3339251881Speter
3340251881Spetersvn_error_t *
3341251881Spetersvn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
3342251881Speter{
3343251881Speter  return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file),
3344251881Speter                                    N_("Can't write file '%s'"),
3345251881Speter                                    N_("Can't write stream"),
3346251881Speter                                    pool);
3347251881Speter}
3348251881Speter
3349251881Speter
3350251881Spetersvn_error_t *
3351251881Spetersvn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
3352251881Speter                     apr_file_t *file, apr_pool_t *pool)
3353251881Speter{
3354251881Speter  /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3355251881Speter  wanted &= ~SVN__APR_FINFO_MASK_OUT;
3356251881Speter
3357251881Speter  return do_io_file_wrapper_cleanup(
3358251881Speter             file, apr_file_info_get(finfo, wanted, file),
3359251881Speter             N_("Can't get attribute information from file '%s'"),
3360251881Speter             N_("Can't get attribute information from stream"),
3361251881Speter             pool);
3362251881Speter}
3363251881Speter
3364251881Speter
3365251881Spetersvn_error_t *
3366251881Spetersvn_io_file_read(apr_file_t *file, void *buf,
3367251881Speter                 apr_size_t *nbytes, apr_pool_t *pool)
3368251881Speter{
3369251881Speter  return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes),
3370251881Speter                                    N_("Can't read file '%s'"),
3371251881Speter                                    N_("Can't read stream"),
3372251881Speter                                    pool);
3373251881Speter}
3374251881Speter
3375251881Speter
3376251881Spetersvn_error_t *
3377251881Spetersvn_io_file_read_full2(apr_file_t *file, void *buf,
3378251881Speter                       apr_size_t nbytes, apr_size_t *bytes_read,
3379251881Speter                       svn_boolean_t *hit_eof,
3380251881Speter                       apr_pool_t *pool)
3381251881Speter{
3382251881Speter  apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
3383251881Speter  if (hit_eof)
3384251881Speter    {
3385251881Speter      if (APR_STATUS_IS_EOF(status))
3386251881Speter        {
3387251881Speter          *hit_eof = TRUE;
3388251881Speter          return SVN_NO_ERROR;
3389251881Speter        }
3390251881Speter      else
3391251881Speter        *hit_eof = FALSE;
3392251881Speter    }
3393251881Speter
3394251881Speter  return do_io_file_wrapper_cleanup(file, status,
3395251881Speter                                    N_("Can't read file '%s'"),
3396251881Speter                                    N_("Can't read stream"),
3397251881Speter                                    pool);
3398251881Speter}
3399251881Speter
3400251881Speter
3401251881Spetersvn_error_t *
3402251881Spetersvn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
3403251881Speter                 apr_off_t *offset, apr_pool_t *pool)
3404251881Speter{
3405251881Speter  return do_io_file_wrapper_cleanup(
3406251881Speter             file, apr_file_seek(file, where, offset),
3407251881Speter             N_("Can't set position pointer in file '%s'"),
3408251881Speter             N_("Can't set position pointer in stream"),
3409251881Speter             pool);
3410251881Speter}
3411251881Speter
3412251881Speter
3413251881Spetersvn_error_t *
3414251881Spetersvn_io_file_write(apr_file_t *file, const void *buf,
3415251881Speter                  apr_size_t *nbytes, apr_pool_t *pool)
3416251881Speter{
3417251881Speter  return svn_error_trace(do_io_file_wrapper_cleanup(
3418251881Speter     file, apr_file_write(file, buf, nbytes),
3419251881Speter     N_("Can't write to file '%s'"),
3420251881Speter     N_("Can't write to stream"),
3421251881Speter     pool));
3422251881Speter}
3423251881Speter
3424251881Speter
3425251881Spetersvn_error_t *
3426251881Spetersvn_io_file_write_full(apr_file_t *file, const void *buf,
3427251881Speter                       apr_size_t nbytes, apr_size_t *bytes_written,
3428251881Speter                       apr_pool_t *pool)
3429251881Speter{
3430251881Speter  /* We cannot simply call apr_file_write_full on Win32 as it may fail
3431251881Speter     for larger values of NBYTES. In that case, we have to emulate the
3432251881Speter     "_full" part here. Thus, always call apr_file_write directly on
3433251881Speter     Win32 as this minimizes overhead for small data buffers. */
3434251881Speter#ifdef WIN32
3435251881Speter#define MAXBUFSIZE 30*1024
3436251881Speter  apr_size_t bw = nbytes;
3437251881Speter  apr_size_t to_write = nbytes;
3438251881Speter
3439251881Speter  /* try a simple "write everything at once" first */
3440251881Speter  apr_status_t rv = apr_file_write(file, buf, &bw);
3441251881Speter  buf = (char *)buf + bw;
3442251881Speter  to_write -= bw;
3443251881Speter
3444251881Speter  /* if the OS cannot handle that, use smaller chunks */
3445251881Speter  if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
3446251881Speter      && nbytes > MAXBUFSIZE)
3447251881Speter    {
3448251881Speter      do {
3449251881Speter        bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
3450251881Speter        rv = apr_file_write(file, buf, &bw);
3451251881Speter        buf = (char *)buf + bw;
3452251881Speter        to_write -= bw;
3453251881Speter      } while (rv == APR_SUCCESS && to_write > 0);
3454251881Speter    }
3455251881Speter
3456251881Speter  /* bytes_written may actually be NULL */
3457251881Speter  if (bytes_written)
3458251881Speter    *bytes_written = nbytes - to_write;
3459251881Speter#undef MAXBUFSIZE
3460251881Speter#else
3461251881Speter  apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
3462251881Speter#endif
3463251881Speter
3464251881Speter  return svn_error_trace(do_io_file_wrapper_cleanup(
3465251881Speter     file, rv,
3466251881Speter     N_("Can't write to file '%s'"),
3467251881Speter     N_("Can't write to stream"),
3468251881Speter     pool));
3469251881Speter}
3470251881Speter
3471251881Speter
3472251881Spetersvn_error_t *
3473251881Spetersvn_io_write_unique(const char **tmp_path,
3474251881Speter                    const char *dirpath,
3475251881Speter                    const void *buf,
3476251881Speter                    apr_size_t nbytes,
3477251881Speter                    svn_io_file_del_t delete_when,
3478251881Speter                    apr_pool_t *pool)
3479251881Speter{
3480251881Speter  apr_file_t *new_file;
3481251881Speter  svn_error_t *err;
3482251881Speter
3483251881Speter  SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
3484251881Speter                                   delete_when, pool, pool));
3485251881Speter
3486251881Speter  err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
3487251881Speter
3488251881Speter  if (!err)
3489251881Speter    err = svn_io_file_flush_to_disk(new_file, pool);
3490251881Speter
3491251881Speter  return svn_error_trace(
3492251881Speter                  svn_error_compose_create(err,
3493251881Speter                                           svn_io_file_close(new_file, pool)));
3494251881Speter}
3495251881Speter
3496251881Speter
3497251881Spetersvn_error_t *
3498251881Spetersvn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
3499251881Speter{
3500251881Speter  /* This is a work-around. APR would flush the write buffer
3501251881Speter     _after_ truncating the file causing now invalid buffered
3502251881Speter     data to be written behind OFFSET. */
3503251881Speter  SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
3504251881Speter                                     N_("Can't flush file '%s'"),
3505251881Speter                                     N_("Can't flush stream"),
3506251881Speter                                     pool));
3507251881Speter
3508251881Speter  return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset),
3509251881Speter                                    N_("Can't truncate file '%s'"),
3510251881Speter                                    N_("Can't truncate stream"),
3511251881Speter                                    pool);
3512251881Speter}
3513251881Speter
3514251881Speter
3515251881Spetersvn_error_t *
3516251881Spetersvn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
3517251881Speter                        apr_pool_t *pool)
3518251881Speter{
3519251881Speter  /* variables */
3520251881Speter  apr_size_t total_read = 0;
3521251881Speter  svn_boolean_t eof = FALSE;
3522251881Speter  const char *name;
3523251881Speter  svn_error_t *err;
3524251881Speter  apr_size_t buf_size = *limit;
3525251881Speter
3526251881Speter  while (buf_size > 0)
3527251881Speter    {
3528251881Speter      /* read a fair chunk of data at once. But don't get too ambitious
3529251881Speter       * as that would result in too much waste. Also make sure we can
3530251881Speter       * put a NUL after the last byte read.
3531251881Speter       */
3532251881Speter      apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
3533251881Speter      apr_size_t bytes_read = 0;
3534251881Speter      char *eol;
3535251881Speter
3536253734Speter      if (to_read == 0)
3537253734Speter        break;
3538253734Speter
3539251881Speter      /* read data block (or just a part of it) */
3540251881Speter      SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
3541251881Speter                                     &bytes_read, &eof, pool));
3542251881Speter
3543251881Speter      /* look or a newline char */
3544251881Speter      buf[bytes_read] = 0;
3545251881Speter      eol = strchr(buf, '\n');
3546251881Speter      if (eol)
3547251881Speter        {
3548251881Speter          apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
3549251881Speter
3550251881Speter          *eol = 0;
3551251881Speter          *limit = total_read + (eol - buf);
3552251881Speter
3553251881Speter          /* correct the file pointer:
3554251881Speter           * appear as though we just had read the newline char
3555251881Speter           */
3556251881Speter          SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
3557251881Speter
3558251881Speter          return SVN_NO_ERROR;
3559251881Speter        }
3560251881Speter      else if (eof)
3561251881Speter        {
3562251881Speter          /* no EOL found but we hit the end of the file.
3563251881Speter           * Generate a nice EOF error object and return it.
3564251881Speter           */
3565251881Speter          char dummy;
3566251881Speter          SVN_ERR(svn_io_file_getc(&dummy, file, pool));
3567251881Speter        }
3568251881Speter
3569251881Speter      /* next data chunk */
3570251881Speter      buf_size -= bytes_read;
3571251881Speter      buf += bytes_read;
3572251881Speter      total_read += bytes_read;
3573251881Speter    }
3574251881Speter
3575251881Speter  /* buffer limit has been exceeded without finding the EOL */
3576251881Speter  err = svn_io_file_name_get(&name, file, pool);
3577251881Speter  if (err)
3578251881Speter    name = NULL;
3579251881Speter  svn_error_clear(err);
3580251881Speter
3581251881Speter  if (name)
3582251881Speter    return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
3583251881Speter                             _("Can't read length line in file '%s'"),
3584251881Speter                             svn_dirent_local_style(name, pool));
3585251881Speter  else
3586251881Speter    return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
3587251881Speter                            _("Can't read length line in stream"));
3588251881Speter}
3589251881Speter
3590251881Speter
3591251881Spetersvn_error_t *
3592251881Spetersvn_io_stat(apr_finfo_t *finfo, const char *fname,
3593251881Speter            apr_int32_t wanted, apr_pool_t *pool)
3594251881Speter{
3595251881Speter  apr_status_t status;
3596251881Speter  const char *fname_apr;
3597251881Speter
3598251881Speter  /* APR doesn't like "" directories */
3599251881Speter  if (fname[0] == '\0')
3600251881Speter    fname = ".";
3601251881Speter
3602251881Speter  SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3603251881Speter
3604251881Speter  /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3605251881Speter  wanted &= ~SVN__APR_FINFO_MASK_OUT;
3606251881Speter
3607251881Speter  status = apr_stat(finfo, fname_apr, wanted, pool);
3608251881Speter  if (status)
3609251881Speter    return svn_error_wrap_apr(status, _("Can't stat '%s'"),
3610251881Speter                              svn_dirent_local_style(fname, pool));
3611251881Speter
3612251881Speter  return SVN_NO_ERROR;
3613251881Speter}
3614251881Speter
3615251881Speter
3616251881Spetersvn_error_t *
3617251881Spetersvn_io_file_rename(const char *from_path, const char *to_path,
3618251881Speter                   apr_pool_t *pool)
3619251881Speter{
3620251881Speter  apr_status_t status = APR_SUCCESS;
3621251881Speter  const char *from_path_apr, *to_path_apr;
3622251881Speter
3623251881Speter  SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
3624251881Speter  SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
3625251881Speter
3626251881Speter  status = apr_file_rename(from_path_apr, to_path_apr, pool);
3627251881Speter
3628251881Speter#if defined(WIN32) || defined(__OS2__)
3629251881Speter  /* If the target file is read only NTFS reports EACCESS and
3630251881Speter     FAT/FAT32 reports EEXIST */
3631251881Speter  if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
3632251881Speter    {
3633251881Speter      /* Set the destination file writable because Windows will not
3634251881Speter         allow us to rename when to_path is read-only, but will
3635251881Speter         allow renaming when from_path is read only. */
3636251881Speter      SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
3637251881Speter
3638251881Speter      status = apr_file_rename(from_path_apr, to_path_apr, pool);
3639251881Speter    }
3640251881Speter  WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool));
3641251881Speter#endif /* WIN32 || __OS2__ */
3642251881Speter
3643251881Speter  if (status)
3644251881Speter    return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
3645251881Speter                              svn_dirent_local_style(from_path, pool),
3646251881Speter                              svn_dirent_local_style(to_path, pool));
3647251881Speter
3648251881Speter  return SVN_NO_ERROR;
3649251881Speter}
3650251881Speter
3651251881Speter
3652251881Spetersvn_error_t *
3653251881Spetersvn_io_file_move(const char *from_path, const char *to_path,
3654251881Speter                 apr_pool_t *pool)
3655251881Speter{
3656251881Speter  svn_error_t *err = svn_io_file_rename(from_path, to_path, pool);
3657251881Speter
3658251881Speter  if (err && APR_STATUS_IS_EXDEV(err->apr_err))
3659251881Speter    {
3660251881Speter      const char *tmp_to_path;
3661251881Speter
3662251881Speter      svn_error_clear(err);
3663251881Speter
3664251881Speter      SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_to_path,
3665251881Speter                                       svn_dirent_dirname(to_path, pool),
3666251881Speter                                       svn_io_file_del_none,
3667251881Speter                                       pool, pool));
3668251881Speter
3669251881Speter      err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool);
3670251881Speter      if (err)
3671251881Speter        goto failed_tmp;
3672251881Speter
3673251881Speter      err = svn_io_file_rename(tmp_to_path, to_path, pool);
3674251881Speter      if (err)
3675251881Speter        goto failed_tmp;
3676251881Speter
3677251881Speter      err = svn_io_remove_file2(from_path, FALSE, pool);
3678251881Speter      if (! err)
3679251881Speter        return SVN_NO_ERROR;
3680251881Speter
3681251881Speter      svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool));
3682251881Speter
3683251881Speter      return err;
3684251881Speter
3685251881Speter    failed_tmp:
3686251881Speter      svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool));
3687251881Speter    }
3688251881Speter
3689251881Speter  return err;
3690251881Speter}
3691251881Speter
3692251881Speter/* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
3693251881Speter   HIDDEN determines if the hidden attribute
3694251881Speter   should be set on the newly created directory. */
3695251881Speterstatic svn_error_t *
3696251881Speterdir_make(const char *path, apr_fileperms_t perm,
3697251881Speter         svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
3698251881Speter{
3699251881Speter  apr_status_t status;
3700251881Speter  const char *path_apr;
3701251881Speter
3702251881Speter  SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
3703251881Speter
3704251881Speter  /* APR doesn't like "" directories */
3705251881Speter  if (path_apr[0] == '\0')
3706251881Speter    path_apr = ".";
3707251881Speter
3708251881Speter#if (APR_OS_DEFAULT & APR_WSTICKY)
3709251881Speter  /* The APR shipped with httpd 2.0.50 contains a bug where
3710251881Speter     APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
3711251881Speter     There is a special case for file creation, but not directory
3712251881Speter     creation, so directories wind up getting created with the sticky
3713251881Speter     bit set.  (There is no such thing as a setuid directory, and the
3714251881Speter     setgid bit is apparently ignored at mkdir() time.)  If we detect
3715251881Speter     this problem, work around it by unsetting those bits if we are
3716251881Speter     passed APR_OS_DEFAULT. */
3717251881Speter  if (perm == APR_OS_DEFAULT)
3718251881Speter    perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY);
3719251881Speter#endif
3720251881Speter
3721251881Speter  status = apr_dir_make(path_apr, perm, pool);
3722251881Speter  WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
3723251881Speter
3724251881Speter  if (status)
3725251881Speter    return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
3726251881Speter                              svn_dirent_local_style(path, pool));
3727251881Speter
3728251881Speter#ifdef APR_FILE_ATTR_HIDDEN
3729251881Speter  if (hidden)
3730251881Speter    {
3731251881Speter#ifndef WIN32
3732251881Speter      status = apr_file_attrs_set(path_apr,
3733251881Speter                                  APR_FILE_ATTR_HIDDEN,
3734251881Speter                                  APR_FILE_ATTR_HIDDEN,
3735251881Speter                                  pool);
3736251881Speter#else
3737251881Speter    /* on Windows, use our wrapper so we can also set the
3738251881Speter       FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */
3739251881Speter    status = io_win_file_attrs_set(path_apr,
3740251881Speter                                   FILE_ATTRIBUTE_HIDDEN |
3741251881Speter                                   FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
3742251881Speter                                   FILE_ATTRIBUTE_HIDDEN |
3743251881Speter                                   FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
3744251881Speter                                   pool);
3745251881Speter
3746251881Speter#endif
3747251881Speter      if (status)
3748251881Speter        return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
3749251881Speter                                  svn_dirent_local_style(path, pool));
3750251881Speter    }
3751251881Speter#endif
3752251881Speter
3753251881Speter/* Windows does not implement sgid. Skip here because retrieving
3754251881Speter   the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented
3755251881Speter   to be 'incredibly expensive'. */
3756251881Speter#ifndef WIN32
3757251881Speter  if (sgid)
3758251881Speter    {
3759251881Speter      apr_finfo_t finfo;
3760251881Speter
3761251881Speter      /* Per our contract, don't do error-checking.  Some filesystems
3762251881Speter       * don't support the sgid bit, and that's okay. */
3763251881Speter      status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool);
3764251881Speter
3765251881Speter      if (!status)
3766251881Speter        apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
3767251881Speter    }
3768251881Speter#endif
3769251881Speter
3770251881Speter  return SVN_NO_ERROR;
3771251881Speter}
3772251881Speter
3773251881Spetersvn_error_t *
3774251881Spetersvn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
3775251881Speter{
3776251881Speter  return dir_make(path, perm, FALSE, FALSE, pool);
3777251881Speter}
3778251881Speter
3779251881Spetersvn_error_t *
3780251881Spetersvn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
3781251881Speter                       apr_pool_t *pool)
3782251881Speter{
3783251881Speter  return dir_make(path, perm, TRUE, FALSE, pool);
3784251881Speter}
3785251881Speter
3786251881Spetersvn_error_t *
3787251881Spetersvn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
3788251881Speter                     apr_pool_t *pool)
3789251881Speter{
3790251881Speter  return dir_make(path, perm, FALSE, TRUE, pool);
3791251881Speter}
3792251881Speter
3793251881Speter
3794251881Spetersvn_error_t *
3795251881Spetersvn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
3796251881Speter{
3797251881Speter  apr_status_t status;
3798251881Speter  const char *dirname_apr;
3799251881Speter
3800251881Speter  /* APR doesn't like "" directories */
3801251881Speter  if (dirname[0] == '\0')
3802251881Speter    dirname = ".";
3803251881Speter
3804251881Speter  SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3805251881Speter
3806251881Speter  status = apr_dir_open(new_dir, dirname_apr, pool);
3807251881Speter  if (status)
3808251881Speter    return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
3809251881Speter                              svn_dirent_local_style(dirname, pool));
3810251881Speter
3811251881Speter  return SVN_NO_ERROR;
3812251881Speter}
3813251881Speter
3814251881Spetersvn_error_t *
3815251881Spetersvn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
3816251881Speter{
3817251881Speter  apr_status_t status;
3818251881Speter  const char *dirname_apr;
3819251881Speter
3820251881Speter  SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3821251881Speter
3822251881Speter  status = apr_dir_remove(dirname_apr, pool);
3823251881Speter
3824251881Speter#ifdef WIN32
3825251881Speter  {
3826251881Speter    svn_boolean_t retry = TRUE;
3827251881Speter
3828251881Speter    if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY)
3829251881Speter      {
3830251881Speter        apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
3831251881Speter
3832251881Speter        if (APR_STATUS_IS_ENOTEMPTY(empty_status))
3833251881Speter          retry = FALSE;
3834251881Speter      }
3835251881Speter
3836251881Speter    if (retry)
3837251881Speter      {
3838251881Speter        WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
3839251881Speter      }
3840251881Speter  }
3841251881Speter#endif
3842251881Speter  if (status)
3843251881Speter    return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
3844251881Speter                              svn_dirent_local_style(dirname, pool));
3845251881Speter
3846251881Speter  return SVN_NO_ERROR;
3847251881Speter}
3848251881Speter
3849251881Speter
3850251881Spetersvn_error_t *
3851251881Spetersvn_io_dir_read(apr_finfo_t *finfo,
3852251881Speter                apr_int32_t wanted,
3853251881Speter                apr_dir_t *thedir,
3854251881Speter                apr_pool_t *pool)
3855251881Speter{
3856251881Speter  apr_status_t status;
3857251881Speter
3858251881Speter  status = apr_dir_read(finfo, wanted, thedir);
3859251881Speter
3860251881Speter  if (status)
3861251881Speter    return svn_error_wrap_apr(status, _("Can't read directory"));
3862251881Speter
3863251881Speter  /* It would be nice to use entry_name_to_utf8() below, but can we
3864251881Speter     get the dir's path out of an apr_dir_t?  I don't see a reliable
3865251881Speter     way to do it. */
3866251881Speter
3867251881Speter  if (finfo->fname)
3868251881Speter    SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
3869251881Speter
3870251881Speter  if (finfo->name)
3871251881Speter    SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
3872251881Speter
3873251881Speter  return SVN_NO_ERROR;
3874251881Speter}
3875251881Speter
3876251881Spetersvn_error_t *
3877251881Spetersvn_io_dir_close(apr_dir_t *thedir)
3878251881Speter{
3879251881Speter  apr_status_t apr_err = apr_dir_close(thedir);
3880251881Speter  if (apr_err)
3881251881Speter    return svn_error_wrap_apr(apr_err, _("Error closing directory"));
3882251881Speter
3883251881Speter  return SVN_NO_ERROR;
3884251881Speter}
3885251881Speter
3886251881Spetersvn_error_t *
3887251881Spetersvn_io_dir_walk2(const char *dirname,
3888251881Speter                 apr_int32_t wanted,
3889251881Speter                 svn_io_walk_func_t walk_func,
3890251881Speter                 void *walk_baton,
3891251881Speter                 apr_pool_t *pool)
3892251881Speter{
3893251881Speter  apr_status_t apr_err;
3894251881Speter  apr_dir_t *handle;
3895251881Speter  apr_pool_t *subpool;
3896251881Speter  const char *dirname_apr;
3897251881Speter  apr_finfo_t finfo;
3898251881Speter
3899251881Speter  wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
3900251881Speter
3901251881Speter  /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3902251881Speter  wanted &= ~SVN__APR_FINFO_MASK_OUT;
3903251881Speter
3904251881Speter  /* The documentation for apr_dir_read used to state that "." and ".."
3905251881Speter     will be returned as the first two files, but it doesn't
3906251881Speter     work that way in practice, in particular ext3 on Linux-2.6 doesn't
3907251881Speter     follow the rules.  For details see
3908251881Speter     http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
3909251881Speter
3910251881Speter     If APR ever does implement "dot-first" then it would be possible to
3911251881Speter     remove the svn_io_stat and walk_func calls and use the walk_func
3912251881Speter     inside the loop.
3913251881Speter
3914251881Speter     Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
3915251881Speter     documented to provide it, so we have to do a bit extra. */
3916251881Speter  SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool));
3917251881Speter  SVN_ERR(cstring_from_utf8(&finfo.name,
3918251881Speter                            svn_dirent_basename(dirname, pool),
3919251881Speter                            pool));
3920251881Speter  finfo.valid |= APR_FINFO_NAME;
3921251881Speter  SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
3922251881Speter
3923251881Speter  SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
3924251881Speter
3925251881Speter  /* APR doesn't like "" directories */
3926251881Speter  if (dirname_apr[0] == '\0')
3927251881Speter    dirname_apr = ".";
3928251881Speter
3929251881Speter  apr_err = apr_dir_open(&handle, dirname_apr, pool);
3930251881Speter  if (apr_err)
3931251881Speter    return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
3932251881Speter                              svn_dirent_local_style(dirname, pool));
3933251881Speter
3934251881Speter  /* iteration subpool */
3935251881Speter  subpool = svn_pool_create(pool);
3936251881Speter
3937251881Speter  while (1)
3938251881Speter    {
3939251881Speter      const char *name_utf8;
3940251881Speter      const char *full_path;
3941251881Speter
3942251881Speter      svn_pool_clear(subpool);
3943251881Speter
3944251881Speter      apr_err = apr_dir_read(&finfo, wanted, handle);
3945251881Speter      if (APR_STATUS_IS_ENOENT(apr_err))
3946251881Speter        break;
3947251881Speter      else if (apr_err)
3948251881Speter        {
3949251881Speter          return svn_error_wrap_apr(apr_err,
3950251881Speter                                    _("Can't read directory entry in '%s'"),
3951251881Speter                                    svn_dirent_local_style(dirname, pool));
3952251881Speter        }
3953251881Speter
3954251881Speter      if (finfo.filetype == APR_DIR)
3955251881Speter        {
3956251881Speter          if (finfo.name[0] == '.'
3957251881Speter              && (finfo.name[1] == '\0'
3958251881Speter                  || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
3959251881Speter            /* skip "." and ".." */
3960251881Speter            continue;
3961251881Speter
3962251881Speter          /* some other directory. recurse. it will be passed to the
3963251881Speter             callback inside the recursion. */
3964251881Speter          SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
3965251881Speter                                     subpool));
3966251881Speter          full_path = svn_dirent_join(dirname, name_utf8, subpool);
3967251881Speter          SVN_ERR(svn_io_dir_walk2(full_path,
3968251881Speter                                   wanted,
3969251881Speter                                   walk_func,
3970251881Speter                                   walk_baton,
3971251881Speter                                   subpool));
3972251881Speter        }
3973251881Speter      else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
3974251881Speter        {
3975251881Speter          /* some other directory. pass it to the callback. */
3976251881Speter          SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
3977251881Speter                                     subpool));
3978251881Speter          full_path = svn_dirent_join(dirname, name_utf8, subpool);
3979251881Speter          SVN_ERR((*walk_func)(walk_baton,
3980251881Speter                               full_path,
3981251881Speter                               &finfo,
3982251881Speter                               subpool));
3983251881Speter        }
3984251881Speter      /* else:
3985251881Speter         Some other type of file; skip it for now.  We've reserved the
3986251881Speter         right to expand our coverage here in the future, though,
3987251881Speter         without revving this API.
3988251881Speter      */
3989251881Speter    }
3990251881Speter
3991251881Speter  svn_pool_destroy(subpool);
3992251881Speter
3993251881Speter  apr_err = apr_dir_close(handle);
3994251881Speter  if (apr_err)
3995251881Speter    return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
3996251881Speter                              svn_dirent_local_style(dirname, pool));
3997251881Speter
3998251881Speter  return SVN_NO_ERROR;
3999251881Speter}
4000251881Speter
4001251881Speter
4002251881Speter
4003251881Speter/**
4004251881Speter * Determine if a directory is empty or not.
4005251881Speter * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
4006251881Speter * @param path The directory.
4007251881Speter * @param pool Used for temporary allocation.
4008251881Speter * @remark If path is not a directory, or some other error occurs,
4009251881Speter * then return the appropriate apr status code.
4010251881Speter *
4011251881Speter * (This function is written in APR style, in anticipation of
4012251881Speter * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
4013251881Speter */
4014251881Speterstatic apr_status_t
4015251881Speterdir_is_empty(const char *dir, apr_pool_t *pool)
4016251881Speter{
4017251881Speter  apr_status_t apr_err;
4018251881Speter  apr_dir_t *dir_handle;
4019251881Speter  apr_finfo_t finfo;
4020251881Speter  apr_status_t retval = APR_SUCCESS;
4021251881Speter
4022251881Speter  /* APR doesn't like "" directories */
4023251881Speter  if (dir[0] == '\0')
4024251881Speter    dir = ".";
4025251881Speter
4026251881Speter  apr_err = apr_dir_open(&dir_handle, dir, pool);
4027251881Speter  if (apr_err != APR_SUCCESS)
4028251881Speter    return apr_err;
4029251881Speter
4030251881Speter  for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle);
4031251881Speter       apr_err == APR_SUCCESS;
4032251881Speter       apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle))
4033251881Speter    {
4034251881Speter      /* Ignore entries for this dir and its parent, robustly.
4035251881Speter         (APR promises that they'll come first, so technically
4036251881Speter         this guard could be moved outside the loop.  But Ryan Bloom
4037251881Speter         says he doesn't believe it, and I believe him. */
4038251881Speter      if (! (finfo.name[0] == '.'
4039251881Speter             && (finfo.name[1] == '\0'
4040251881Speter                 || (finfo.name[1] == '.' && finfo.name[2] == '\0'))))
4041251881Speter        {
4042251881Speter          retval = APR_ENOTEMPTY;
4043251881Speter          break;
4044251881Speter        }
4045251881Speter    }
4046251881Speter
4047251881Speter  /* Make sure we broke out of the loop for the right reason. */
4048251881Speter  if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
4049251881Speter    return apr_err;
4050251881Speter
4051251881Speter  apr_err = apr_dir_close(dir_handle);
4052251881Speter  if (apr_err != APR_SUCCESS)
4053251881Speter    return apr_err;
4054251881Speter
4055251881Speter  return retval;
4056251881Speter}
4057251881Speter
4058251881Speter
4059251881Spetersvn_error_t *
4060251881Spetersvn_io_dir_empty(svn_boolean_t *is_empty_p,
4061251881Speter                 const char *path,
4062251881Speter                 apr_pool_t *pool)
4063251881Speter{
4064251881Speter  apr_status_t status;
4065251881Speter  const char *path_apr;
4066251881Speter
4067251881Speter  SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4068251881Speter
4069251881Speter  status = dir_is_empty(path_apr, pool);
4070251881Speter
4071251881Speter  if (!status)
4072251881Speter    *is_empty_p = TRUE;
4073251881Speter  else if (APR_STATUS_IS_ENOTEMPTY(status))
4074251881Speter    *is_empty_p = FALSE;
4075251881Speter  else
4076251881Speter    return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
4077251881Speter                              svn_dirent_local_style(path, pool));
4078251881Speter
4079251881Speter  return SVN_NO_ERROR;
4080251881Speter}
4081251881Speter
4082251881Speter
4083251881Speter
4084251881Speter/*** Version/format files ***/
4085251881Speter
4086251881Spetersvn_error_t *
4087251881Spetersvn_io_write_version_file(const char *path,
4088251881Speter                          int version,
4089251881Speter                          apr_pool_t *pool)
4090251881Speter{
4091251881Speter  const char *path_tmp;
4092251881Speter  const char *format_contents = apr_psprintf(pool, "%d\n", version);
4093251881Speter
4094251881Speter  SVN_ERR_ASSERT(version >= 0);
4095251881Speter
4096251881Speter  SVN_ERR(svn_io_write_unique(&path_tmp,
4097251881Speter                              svn_dirent_dirname(path, pool),
4098251881Speter                              format_contents, strlen(format_contents),
4099251881Speter                              svn_io_file_del_none, pool));
4100251881Speter
4101251881Speter#if defined(WIN32) || defined(__OS2__)
4102251881Speter  /* make the destination writable, but only on Windows, because
4103251881Speter     Windows does not let us replace read-only files. */
4104251881Speter  SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
4105251881Speter#endif /* WIN32 || __OS2__ */
4106251881Speter
4107251881Speter  /* rename the temp file as the real destination */
4108251881Speter  SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
4109251881Speter
4110251881Speter  /* And finally remove the perms to make it read only */
4111251881Speter  return svn_io_set_file_read_only(path, FALSE, pool);
4112251881Speter}
4113251881Speter
4114251881Speter
4115251881Spetersvn_error_t *
4116251881Spetersvn_io_read_version_file(int *version,
4117251881Speter                         const char *path,
4118251881Speter                         apr_pool_t *pool)
4119251881Speter{
4120251881Speter  apr_file_t *format_file;
4121251881Speter  char buf[80];
4122251881Speter  apr_size_t len;
4123251881Speter  svn_error_t *err;
4124251881Speter
4125251881Speter  /* Read a chunk of data from PATH */
4126251881Speter  SVN_ERR(svn_io_file_open(&format_file, path, APR_READ,
4127251881Speter                           APR_OS_DEFAULT, pool));
4128251881Speter  len = sizeof(buf);
4129251881Speter  err = svn_io_file_read(format_file, buf, &len, pool);
4130251881Speter
4131251881Speter  /* Close the file. */
4132251881Speter  SVN_ERR(svn_error_compose_create(err,
4133251881Speter                                   svn_io_file_close(format_file, pool)));
4134251881Speter
4135251881Speter  /* If there was no data in PATH, return an error. */
4136251881Speter  if (len == 0)
4137251881Speter    return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
4138251881Speter                             _("Reading '%s'"),
4139251881Speter                             svn_dirent_local_style(path, pool));
4140251881Speter
4141251881Speter  /* Check that the first line contains only digits. */
4142251881Speter  {
4143251881Speter    apr_size_t i;
4144251881Speter
4145251881Speter    for (i = 0; i < len; ++i)
4146251881Speter      {
4147251881Speter        char c = buf[i];
4148251881Speter
4149251881Speter        if (i > 0 && (c == '\r' || c == '\n'))
4150251881Speter          {
4151251881Speter            buf[i] = '\0';
4152251881Speter            break;
4153251881Speter          }
4154251881Speter        if (! svn_ctype_isdigit(c))
4155251881Speter          return svn_error_createf
4156251881Speter            (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
4157251881Speter             _("First line of '%s' contains non-digit"),
4158251881Speter             svn_dirent_local_style(path, pool));
4159251881Speter      }
4160251881Speter  }
4161251881Speter
4162251881Speter  /* Convert to integer. */
4163251881Speter  SVN_ERR(svn_cstring_atoi(version, buf));
4164251881Speter
4165251881Speter  return SVN_NO_ERROR;
4166251881Speter}
4167251881Speter
4168251881Speter
4169251881Speter
4170251881Speter/* Do a byte-for-byte comparison of FILE1 and FILE2. */
4171251881Speterstatic svn_error_t *
4172251881Spetercontents_identical_p(svn_boolean_t *identical_p,
4173251881Speter                     const char *file1,
4174251881Speter                     const char *file2,
4175251881Speter                     apr_pool_t *pool)
4176251881Speter{
4177251881Speter  svn_error_t *err;
4178251881Speter  apr_size_t bytes_read1, bytes_read2;
4179251881Speter  char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4180251881Speter  char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4181251881Speter  apr_file_t *file1_h;
4182251881Speter  apr_file_t *file2_h;
4183251881Speter  svn_boolean_t eof1 = FALSE;
4184251881Speter  svn_boolean_t eof2 = FALSE;
4185251881Speter
4186251881Speter  SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4187251881Speter                           pool));
4188251881Speter
4189251881Speter  err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4190251881Speter                         pool);
4191251881Speter
4192251881Speter  if (err)
4193251881Speter    return svn_error_trace(
4194251881Speter               svn_error_compose_create(err,
4195251881Speter                                        svn_io_file_close(file1_h, pool)));
4196251881Speter
4197251881Speter  *identical_p = TRUE;  /* assume TRUE, until disproved below */
4198251881Speter  while (!err && !eof1 && !eof2)
4199251881Speter    {
4200251881Speter      err = svn_io_file_read_full2(file1_h, buf1,
4201251881Speter                                   SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4202251881Speter                                   &eof1, pool);
4203251881Speter      if (err)
4204251881Speter          break;
4205251881Speter
4206251881Speter      err = svn_io_file_read_full2(file2_h, buf2,
4207251881Speter                                   SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4208251881Speter                                   &eof2, pool);
4209251881Speter      if (err)
4210251881Speter          break;
4211251881Speter
4212251881Speter      if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
4213251881Speter        {
4214251881Speter          *identical_p = FALSE;
4215251881Speter          break;
4216251881Speter        }
4217251881Speter    }
4218251881Speter
4219251881Speter  /* Special case: one file being a prefix of the other and the shorter
4220251881Speter   * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */
4221251881Speter  if (!err && (eof1 != eof2))
4222251881Speter    *identical_p = FALSE;
4223251881Speter
4224251881Speter  return svn_error_trace(
4225251881Speter           svn_error_compose_create(
4226251881Speter                err,
4227251881Speter                svn_error_compose_create(svn_io_file_close(file1_h, pool),
4228251881Speter                                         svn_io_file_close(file2_h, pool))));
4229251881Speter}
4230251881Speter
4231251881Speter
4232251881Speter
4233251881Speter/* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
4234251881Speterstatic svn_error_t *
4235251881Spetercontents_three_identical_p(svn_boolean_t *identical_p12,
4236251881Speter                           svn_boolean_t *identical_p23,
4237251881Speter                           svn_boolean_t *identical_p13,
4238251881Speter                           const char *file1,
4239251881Speter                           const char *file2,
4240251881Speter                           const char *file3,
4241251881Speter                           apr_pool_t *scratch_pool)
4242251881Speter{
4243251881Speter  svn_error_t *err;
4244251881Speter  apr_size_t bytes_read1, bytes_read2, bytes_read3;
4245251881Speter  char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4246251881Speter  char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4247251881Speter  char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4248251881Speter  apr_file_t *file1_h;
4249251881Speter  apr_file_t *file2_h;
4250251881Speter  apr_file_t *file3_h;
4251251881Speter  svn_boolean_t eof1 = FALSE;
4252251881Speter  svn_boolean_t eof2 = FALSE;
4253251881Speter  svn_boolean_t eof3 = FALSE;
4254251881Speter  svn_boolean_t read_1, read_2, read_3;
4255251881Speter
4256251881Speter  SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4257251881Speter                           scratch_pool));
4258251881Speter
4259251881Speter  err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4260251881Speter                         scratch_pool);
4261251881Speter
4262251881Speter  if (err)
4263251881Speter    return svn_error_trace(
4264251881Speter               svn_error_compose_create(err,
4265251881Speter                                        svn_io_file_close(file1_h, scratch_pool)));
4266251881Speter
4267251881Speter  err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
4268251881Speter                         scratch_pool);
4269251881Speter
4270251881Speter  if (err)
4271251881Speter      return svn_error_trace(
4272251881Speter               svn_error_compose_create(
4273251881Speter                    err,
4274251881Speter                    svn_error_compose_create(svn_io_file_close(file1_h,
4275251881Speter                                                          scratch_pool),
4276251881Speter                                             svn_io_file_close(file2_h,
4277251881Speter                                                          scratch_pool))));
4278251881Speter
4279251881Speter  /* assume TRUE, until disproved below */
4280251881Speter  *identical_p12 = *identical_p23 = *identical_p13 = TRUE;
4281251881Speter  /* We need to read as long as no error occurs, and as long as one of the
4282251881Speter   * flags could still change due to a read operation */
4283251881Speter  while (!err
4284251881Speter        && ((*identical_p12 && !eof1 && !eof2)
4285251881Speter            || (*identical_p23 && !eof2 && !eof3)
4286251881Speter            || (*identical_p13 && !eof1 && !eof3)))
4287251881Speter    {
4288251881Speter      read_1 = read_2 = read_3 = FALSE;
4289251881Speter
4290251881Speter      /* As long as a file is not at the end yet, and it is still
4291251881Speter       * potentially identical to another file, we read the next chunk.*/
4292251881Speter      if (!eof1 && (identical_p12 || identical_p13))
4293251881Speter        {
4294251881Speter          err = svn_io_file_read_full2(file1_h, buf1,
4295251881Speter                                   SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4296251881Speter                                   &eof1, scratch_pool);
4297251881Speter          if (err)
4298251881Speter              break;
4299251881Speter          read_1 = TRUE;
4300251881Speter        }
4301251881Speter
4302251881Speter      if (!eof2 && (identical_p12 || identical_p23))
4303251881Speter        {
4304251881Speter          err = svn_io_file_read_full2(file2_h, buf2,
4305251881Speter                                   SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4306251881Speter                                   &eof2, scratch_pool);
4307251881Speter          if (err)
4308251881Speter              break;
4309251881Speter          read_2 = TRUE;
4310251881Speter        }
4311251881Speter
4312251881Speter      if (!eof3 && (identical_p13 || identical_p23))
4313251881Speter        {
4314251881Speter          err = svn_io_file_read_full2(file3_h, buf3,
4315251881Speter                                   SVN__STREAM_CHUNK_SIZE, &bytes_read3,
4316251881Speter                                   &eof3, scratch_pool);
4317251881Speter          if (err)
4318251881Speter              break;
4319251881Speter          read_3 = TRUE;
4320251881Speter        }
4321251881Speter
4322251881Speter      /* If the files are still marked identical, and at least one of them
4323251881Speter       * is not at the end of file, we check whether they differ, and set
4324251881Speter       * their flag to false then. */
4325251881Speter      if (*identical_p12
4326251881Speter          && (read_1 || read_2)
4327251881Speter          && ((eof1 != eof2)
4328251881Speter              || (bytes_read1 != bytes_read2)
4329251881Speter              || memcmp(buf1, buf2, bytes_read1)))
4330251881Speter        {
4331251881Speter          *identical_p12 = FALSE;
4332251881Speter        }
4333251881Speter
4334251881Speter      if (*identical_p23
4335251881Speter          && (read_2 || read_3)
4336251881Speter          && ((eof2 != eof3)
4337251881Speter              || (bytes_read2 != bytes_read3)
4338251881Speter              || memcmp(buf2, buf3, bytes_read2)))
4339251881Speter        {
4340251881Speter          *identical_p23 = FALSE;
4341251881Speter        }
4342251881Speter
4343251881Speter      if (*identical_p13
4344251881Speter          && (read_1 || read_3)
4345251881Speter          && ((eof1 != eof3)
4346251881Speter              || (bytes_read1 != bytes_read3)
4347251881Speter              || memcmp(buf1, buf3, bytes_read3)))
4348251881Speter        {
4349251881Speter          *identical_p13 = FALSE;
4350251881Speter        }
4351251881Speter    }
4352251881Speter
4353251881Speter  return svn_error_trace(
4354251881Speter           svn_error_compose_create(
4355251881Speter                err,
4356251881Speter                svn_error_compose_create(
4357251881Speter                    svn_io_file_close(file1_h, scratch_pool),
4358251881Speter                    svn_error_compose_create(
4359251881Speter                        svn_io_file_close(file2_h, scratch_pool),
4360251881Speter                        svn_io_file_close(file3_h, scratch_pool)))));
4361251881Speter}
4362251881Speter
4363251881Speter
4364251881Speter
4365251881Spetersvn_error_t *
4366251881Spetersvn_io_files_contents_same_p(svn_boolean_t *same,
4367251881Speter                             const char *file1,
4368251881Speter                             const char *file2,
4369251881Speter                             apr_pool_t *pool)
4370251881Speter{
4371251881Speter  svn_boolean_t q;
4372251881Speter
4373251881Speter  SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
4374251881Speter
4375251881Speter  if (q)
4376251881Speter    {
4377251881Speter      *same = FALSE;
4378251881Speter      return SVN_NO_ERROR;
4379251881Speter    }
4380251881Speter
4381251881Speter  SVN_ERR(contents_identical_p(&q, file1, file2, pool));
4382251881Speter
4383251881Speter  if (q)
4384251881Speter    *same = TRUE;
4385251881Speter  else
4386251881Speter    *same = FALSE;
4387251881Speter
4388251881Speter  return SVN_NO_ERROR;
4389251881Speter}
4390251881Speter
4391251881Spetersvn_error_t *
4392251881Spetersvn_io_files_contents_three_same_p(svn_boolean_t *same12,
4393251881Speter                                   svn_boolean_t *same23,
4394251881Speter                                   svn_boolean_t *same13,
4395251881Speter                                   const char *file1,
4396251881Speter                                   const char *file2,
4397251881Speter                                   const char *file3,
4398251881Speter                                   apr_pool_t *scratch_pool)
4399251881Speter{
4400251881Speter  svn_boolean_t diff_size12, diff_size23, diff_size13;
4401251881Speter
4402251881Speter  SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
4403251881Speter                                             &diff_size23,
4404251881Speter                                             &diff_size13,
4405251881Speter                                             file1,
4406251881Speter                                             file2,
4407251881Speter                                             file3,
4408251881Speter                                             scratch_pool));
4409251881Speter
4410251881Speter  if (diff_size12 && diff_size23 && diff_size13)
4411251881Speter    {
4412251881Speter      *same12 = *same23 = *same13 = FALSE;
4413251881Speter    }
4414251881Speter  else if (diff_size12 && diff_size23)
4415251881Speter    {
4416251881Speter      *same12 = *same23 = FALSE;
4417251881Speter      SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
4418251881Speter    }
4419251881Speter  else if (diff_size23 && diff_size13)
4420251881Speter    {
4421251881Speter      *same23 = *same13 = FALSE;
4422251881Speter      SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
4423251881Speter    }
4424251881Speter  else if (diff_size12 && diff_size13)
4425251881Speter    {
4426251881Speter      *same12 = *same13 = FALSE;
4427251881Speter      SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
4428251881Speter    }
4429251881Speter  else
4430251881Speter    {
4431251881Speter      SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
4432251881Speter      SVN_ERR(contents_three_identical_p(same12, same23, same13,
4433251881Speter                                         file1, file2, file3,
4434251881Speter                                         scratch_pool));
4435251881Speter    }
4436251881Speter
4437251881Speter  return SVN_NO_ERROR;
4438251881Speter}
4439251881Speter
4440251881Speter#ifdef WIN32
4441251881Speter/* Counter value of file_mktemp request (used in a threadsafe way), to make
4442251881Speter   sure that a single process normally never generates the same tempname
4443251881Speter   twice */
4444251881Speterstatic volatile apr_uint32_t tempname_counter = 0;
4445251881Speter#endif
4446251881Speter
4447251881Speter/* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
4448251881Speter   Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name.
4449251881Speter   Perform temporary allocations in SCRATCH_POOL and the result in
4450251881Speter   RESULT_POOL. */
4451251881Speterstatic svn_error_t *
4452251881Spetertemp_file_create(apr_file_t **new_file,
4453251881Speter                 const char **new_file_name,
4454251881Speter                 const char *directory,
4455251881Speter                 apr_int32_t flags,
4456251881Speter                 apr_pool_t *result_pool,
4457251881Speter                 apr_pool_t *scratch_pool)
4458251881Speter{
4459251881Speter#ifndef WIN32
4460251881Speter  const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
4461251881Speter  const char *templ_apr;
4462251881Speter  apr_status_t status;
4463251881Speter
4464251881Speter  SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
4465251881Speter
4466251881Speter  /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
4467251881Speter         data available in POOL and we need a non-const pointer here,
4468251881Speter         as apr changes the template to return the new filename. */
4469251881Speter  status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool);
4470251881Speter
4471251881Speter  if (status)
4472251881Speter    return svn_error_wrap_apr(status, _("Can't create temporary file from "
4473251881Speter                              "template '%s'"), templ);
4474251881Speter
4475251881Speter  /* Translate the returned path back to utf-8 before returning it */
4476251881Speter  return svn_error_trace(svn_path_cstring_to_utf8(new_file_name,
4477251881Speter                                                  templ_apr,
4478251881Speter                                                  result_pool));
4479251881Speter#else
4480251881Speter  /* The Windows implementation of apr_file_mktemp doesn't handle access
4481251881Speter     denied errors correctly. Therefore we implement our own temp file
4482251881Speter     creation function here. */
4483251881Speter
4484251881Speter  /* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
4485251881Speter     ### the function we used before. But we try to guess a more unique
4486251881Speter     ### name before trying if it exists. */
4487251881Speter
4488251881Speter  /* Offset by some time value and a unique request nr to make the number
4489251881Speter     +- unique for both this process and on the computer */
4490251881Speter  int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter)
4491251881Speter               + GetCurrentProcessId();
4492251881Speter  int i;
4493251881Speter
4494251881Speter  /* ### Maybe use an iterpool? */
4495251881Speter  for (i = 0; i <= 99999; i++)
4496251881Speter    {
4497251881Speter      apr_uint32_t unique_nr;
4498251881Speter      const char *unique_name;
4499251881Speter      const char *unique_name_apr;
4500251881Speter      apr_file_t *try_file;
4501251881Speter      apr_status_t apr_err;
4502251881Speter
4503251881Speter      /* Generate a number that should be unique for this application and
4504251881Speter         usually for the entire computer to reduce the number of cycles
4505251881Speter         through this loop. (A bit of calculation is much cheaper then
4506251881Speter         disk io) */
4507251881Speter      unique_nr = baseNr + 3 * i;
4508251881Speter
4509251881Speter      unique_name = svn_dirent_join(directory,
4510251881Speter                                    apr_psprintf(scratch_pool, "svn-%X",
4511251881Speter                                                 unique_nr),
4512251881Speter                                    scratch_pool);
4513251881Speter
4514251881Speter      SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
4515251881Speter
4516251881Speter      apr_err = file_open(&try_file, unique_name_apr, flags,
4517251881Speter                          APR_OS_DEFAULT, FALSE, scratch_pool);
4518251881Speter
4519251881Speter      if (APR_STATUS_IS_EEXIST(apr_err))
4520251881Speter          continue;
4521251881Speter      else if (apr_err)
4522251881Speter        {
4523251881Speter          /* On Win32, CreateFile fails with an "Access Denied" error
4524251881Speter             code, rather than "File Already Exists", if the colliding
4525251881Speter             name belongs to a directory. */
4526251881Speter
4527251881Speter          if (APR_STATUS_IS_EACCES(apr_err))
4528251881Speter            {
4529251881Speter              apr_finfo_t finfo;
4530251881Speter              apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
4531251881Speter                                                APR_FINFO_TYPE, scratch_pool);
4532251881Speter
4533251881Speter              if (!apr_err_2 && finfo.filetype == APR_DIR)
4534251881Speter                continue;
4535251881Speter
4536251881Speter              apr_err_2 = APR_TO_OS_ERROR(apr_err);
4537251881Speter
4538251881Speter              if (apr_err_2 == ERROR_ACCESS_DENIED ||
4539251881Speter                  apr_err_2 == ERROR_SHARING_VIOLATION)
4540251881Speter                {
4541251881Speter                  /* The file is in use by another process or is hidden;
4542251881Speter                     create a new name, but don't do this 99999 times in
4543251881Speter                     case the folder is not writable */
4544251881Speter                  i += 797;
4545251881Speter                  continue;
4546251881Speter                }
4547251881Speter
4548251881Speter              /* Else fall through and return the original error. */
4549251881Speter            }
4550251881Speter
4551251881Speter          return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
4552251881Speter                                    svn_dirent_local_style(unique_name,
4553251881Speter                                                           scratch_pool));
4554251881Speter        }
4555251881Speter      else
4556251881Speter        {
4557251881Speter          /* Move file to the right pool */
4558251881Speter          apr_err = apr_file_setaside(new_file, try_file, result_pool);
4559251881Speter
4560251881Speter          if (apr_err)
4561251881Speter            return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
4562251881Speter                                      svn_dirent_local_style(unique_name,
4563251881Speter                                                             scratch_pool));
4564251881Speter
4565251881Speter          *new_file_name = apr_pstrdup(result_pool, unique_name);
4566251881Speter
4567251881Speter          return SVN_NO_ERROR;
4568251881Speter        }
4569251881Speter    }
4570251881Speter
4571251881Speter  return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
4572251881Speter                           NULL,
4573251881Speter                           _("Unable to make name in '%s'"),
4574251881Speter                           svn_dirent_local_style(directory, scratch_pool));
4575251881Speter#endif
4576251881Speter}
4577251881Speter
4578251881Speter/* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
4579251881Spetersvn_error_t *
4580251881Spetersvn_io_file_name_get(const char **filename,
4581251881Speter                     apr_file_t *file,
4582251881Speter                     apr_pool_t *pool)
4583251881Speter{
4584251881Speter  const char *fname_apr;
4585251881Speter  apr_status_t status;
4586251881Speter
4587251881Speter  status = apr_file_name_get(&fname_apr, file);
4588251881Speter  if (status)
4589251881Speter    return svn_error_wrap_apr(status, _("Can't get file name"));
4590251881Speter
4591251881Speter  if (fname_apr)
4592251881Speter    SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
4593251881Speter  else
4594251881Speter    *filename = NULL;
4595251881Speter
4596251881Speter  return SVN_NO_ERROR;
4597251881Speter}
4598251881Speter
4599251881Speter
4600251881Spetersvn_error_t *
4601251881Spetersvn_io_open_unique_file3(apr_file_t **file,
4602251881Speter                         const char **unique_path,
4603251881Speter                         const char *dirpath,
4604251881Speter                         svn_io_file_del_t delete_when,
4605251881Speter                         apr_pool_t *result_pool,
4606251881Speter                         apr_pool_t *scratch_pool)
4607251881Speter{
4608251881Speter  apr_file_t *tempfile;
4609251881Speter  const char *tempname;
4610251881Speter  struct temp_file_cleanup_s *baton = NULL;
4611251881Speter  apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
4612251881Speter                       APR_BUFFERED | APR_BINARY);
4613251881Speter#if !defined(WIN32) && !defined(__OS2__)
4614251881Speter  apr_fileperms_t perms;
4615251881Speter  svn_boolean_t using_system_temp_dir = FALSE;
4616251881Speter#endif
4617251881Speter
4618251881Speter  SVN_ERR_ASSERT(file || unique_path);
4619251881Speter  if (file)
4620251881Speter    *file = NULL;
4621251881Speter  if (unique_path)
4622251881Speter    *unique_path = NULL;
4623251881Speter
4624251881Speter  if (dirpath == NULL)
4625251881Speter    {
4626251881Speter#if !defined(WIN32) && !defined(__OS2__)
4627251881Speter      using_system_temp_dir = TRUE;
4628251881Speter#endif
4629251881Speter      SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
4630251881Speter    }
4631251881Speter
4632251881Speter  switch (delete_when)
4633251881Speter    {
4634251881Speter      case svn_io_file_del_on_pool_cleanup:
4635251881Speter        baton = apr_palloc(result_pool, sizeof(*baton));
4636251881Speter        baton->pool = result_pool;
4637251881Speter        baton->fname_apr = NULL;
4638251881Speter
4639251881Speter        /* Because cleanups are run LIFO, we need to make sure to register
4640251881Speter           our cleanup before the apr_file_close cleanup:
4641251881Speter
4642251881Speter           On Windows, you can't remove an open file.
4643251881Speter        */
4644251881Speter        apr_pool_cleanup_register(result_pool, baton,
4645251881Speter                                  temp_file_plain_cleanup_handler,
4646251881Speter                                  temp_file_child_cleanup_handler);
4647251881Speter
4648251881Speter        break;
4649251881Speter      case svn_io_file_del_on_close:
4650251881Speter        flags |= APR_DELONCLOSE;
4651251881Speter        break;
4652251881Speter      default:
4653251881Speter        break;
4654251881Speter    }
4655251881Speter
4656251881Speter  SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
4657251881Speter                           result_pool, scratch_pool));
4658251881Speter
4659251881Speter#if !defined(WIN32) && !defined(__OS2__)
4660251881Speter  /* apr_file_mktemp() creates files with mode 0600.
4661251881Speter   * This is appropriate if we're using a system temp dir since we don't
4662251881Speter   * want to leak sensitive data into temp files other users can read.
4663251881Speter   * If we're not using a system temp dir we're probably using the
4664251881Speter   * .svn/tmp area and it's likely that the tempfile will end up being
4665251881Speter   * copied or renamed into the working copy.
4666251881Speter   * This would cause working files having mode 0600 while users might
4667251881Speter   * expect to see 0644 or 0664. So we tweak perms of the tempfile in this
4668251881Speter   * case, but only if the umask allows it. */
4669251881Speter  if (!using_system_temp_dir)
4670251881Speter    {
4671251881Speter      SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
4672251881Speter      SVN_ERR(file_perms_set2(tempfile, perms, scratch_pool));
4673251881Speter    }
4674251881Speter#endif
4675251881Speter
4676251881Speter  if (file)
4677251881Speter    *file = tempfile;
4678251881Speter  else
4679251881Speter    SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
4680251881Speter
4681251881Speter  if (unique_path)
4682251881Speter    *unique_path = tempname; /* Was allocated in result_pool */
4683251881Speter
4684251881Speter  if (baton)
4685251881Speter    SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
4686251881Speter
4687251881Speter  return SVN_NO_ERROR;
4688251881Speter}
4689251881Speter
4690251881Spetersvn_error_t *
4691251881Spetersvn_io_file_readline(apr_file_t *file,
4692251881Speter                     svn_stringbuf_t **stringbuf,
4693251881Speter                     const char **eol,
4694251881Speter                     svn_boolean_t *eof,
4695251881Speter                     apr_size_t max_len,
4696251881Speter                     apr_pool_t *result_pool,
4697251881Speter                     apr_pool_t *scratch_pool)
4698251881Speter{
4699251881Speter  svn_stringbuf_t *str;
4700251881Speter  const char *eol_str;
4701251881Speter  apr_size_t numbytes;
4702251881Speter  char c;
4703251881Speter  apr_size_t len;
4704251881Speter  svn_boolean_t found_eof;
4705251881Speter
4706251881Speter  str = svn_stringbuf_create_ensure(80, result_pool);
4707251881Speter
4708251881Speter  /* Read bytes into STR up to and including, but not storing,
4709251881Speter   * the next EOL sequence. */
4710251881Speter  eol_str = NULL;
4711251881Speter  numbytes = 1;
4712251881Speter  len = 0;
4713251881Speter  found_eof = FALSE;
4714251881Speter  while (!found_eof)
4715251881Speter    {
4716251881Speter      if (len < max_len)
4717251881Speter        SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4718251881Speter                                       &found_eof, scratch_pool));
4719251881Speter      len++;
4720251881Speter      if (numbytes != 1 || len > max_len)
4721251881Speter        {
4722251881Speter          found_eof = TRUE;
4723251881Speter          break;
4724251881Speter        }
4725251881Speter
4726251881Speter      if (c == '\n')
4727251881Speter        {
4728251881Speter          eol_str = "\n";
4729251881Speter        }
4730251881Speter      else if (c == '\r')
4731251881Speter        {
4732251881Speter          eol_str = "\r";
4733251881Speter
4734251881Speter          if (!found_eof && len < max_len)
4735251881Speter            {
4736251881Speter              apr_off_t pos;
4737251881Speter
4738251881Speter              /* Check for "\r\n" by peeking at the next byte. */
4739251881Speter              pos = 0;
4740251881Speter              SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool));
4741251881Speter              SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
4742251881Speter                                             &found_eof, scratch_pool));
4743251881Speter              if (numbytes == 1 && c == '\n')
4744251881Speter                {
4745251881Speter                  eol_str = "\r\n";
4746251881Speter                  len++;
4747251881Speter                }
4748251881Speter              else
4749251881Speter                {
4750251881Speter                  /* Pretend we never peeked. */
4751251881Speter                  SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
4752251881Speter                  found_eof = FALSE;
4753251881Speter                  numbytes = 1;
4754251881Speter                }
4755251881Speter            }
4756251881Speter        }
4757251881Speter      else
4758251881Speter        svn_stringbuf_appendbyte(str, c);
4759251881Speter
4760251881Speter      if (eol_str)
4761251881Speter        break;
4762251881Speter    }
4763251881Speter
4764251881Speter  if (eol)
4765251881Speter    *eol = eol_str;
4766251881Speter  if (eof)
4767251881Speter    *eof = found_eof;
4768251881Speter  *stringbuf = str;
4769251881Speter
4770251881Speter  return SVN_NO_ERROR;
4771251881Speter}
4772