⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 io.c

📁 linux subdivision ying gai ke yi le ba
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * io.c:   shared file reading, writing, and probing code.
 *
 * ====================================================================
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 */



#include <stdio.h>
#include <assert.h>

#ifndef WIN32
#include <unistd.h>
#ifndef APR_GSETID
/* Needed for fallback setgid code in dir_make */
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#endif
#endif

#ifndef APR_STATUS_IS_EPERM
#ifdef EPERM
#define APR_STATUS_IS_EPERM(s)   ((s) == EPERM)
#else
#define APR_STATUS_IS_EPERM(s)   (0)
#endif
#endif

#include <apr_lib.h>
#include <apr_pools.h>
#include <apr_file_io.h>
#include <apr_file_info.h>
#include <apr_general.h>
#include <apr_strings.h>
#include <apr_portable.h>
#include <apr_md5.h>

#include "svn_types.h"
#include "svn_path.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_io.h"
#include "svn_base64.h"
#include "svn_pools.h"
#include "svn_utf.h"
#include "svn_config.h"
#include "svn_private_config.h"


/*
  Windows is 'aided' by a number of types of applications that
  follow other applications around and open up files they have
  changed for various reasons (the most intrusive are virus
  scanners).  So, if one of these other apps has glommed onto
  our file we may get an 'access denied' error.

  This retry loop does not completely solve the problem (who
  knows how long the other app is going to hold onto it for), but
  goes a long way towards minimizing it.  It is not an infinite
  loop because there might really be an error.
*/
#ifdef WIN32
#define WIN32_RETRY_LOOP(err, expr)                                        \
  do                                                                       \
    {                                                                      \
      apr_status_t os_err = APR_TO_OS_ERROR (err);                         \
      int sleep_count = 1000;                                              \
      int retries;                                                         \
      for (retries = 0;                                                    \
           retries < 100 && (os_err == ERROR_ACCESS_DENIED                 \
                             || os_err == ERROR_SHARING_VIOLATION);        \
           ++retries, os_err = APR_TO_OS_ERROR (err))                      \
        {                                                                  \
          apr_sleep (sleep_count);                                         \
          if (sleep_count < 128000)                                        \
            sleep_count *= 2;                                              \
          (err) = (expr);                                                  \
        }                                                                  \
    }                                                                      \
  while (0)
#else
#define WIN32_RETRY_LOOP(err, expr) ((void)0)
#endif

/* Helper for svn_io_check_path() and svn_io_check_resolved_path();
   essentially the same semantics as those two, with the obvious
   interpretation for RESOLVE_SYMLINKS. */
static svn_error_t *
io_check_path (const char *path,
               svn_boolean_t resolve_symlinks,
               svn_boolean_t *is_special_p,
               svn_node_kind_t *kind,
               apr_pool_t *pool)
{
  apr_int32_t flags;
  apr_finfo_t finfo;
  apr_status_t apr_err;
  const char *path_apr;
  svn_boolean_t is_special = FALSE;

  /* Make path appropriate for error messages in advance. */
  path = svn_path_local_style (path, pool);

  /* Not using svn_io_stat() here because we want to check the
     apr_err return explicitly. */
  SVN_ERR (svn_path_cstring_from_utf8 (&path_apr, path, pool));

  flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK);
  apr_err = apr_stat (&finfo, path_apr, flags, pool);
  
  if (APR_STATUS_IS_ENOENT (apr_err))
    *kind = svn_node_none;
  else if (APR_STATUS_IS_ENOTDIR (apr_err))
    *kind = svn_node_none;
  else if (apr_err)
    return svn_error_wrap_apr (apr_err, "Can't check path '%s'", path);
  else if (finfo.filetype == APR_NOFILE)
    *kind = svn_node_unknown;
  else if (finfo.filetype == APR_REG)
    *kind = svn_node_file;
  else if (finfo.filetype == APR_DIR)
    *kind = svn_node_dir;
  else if (finfo.filetype == APR_LNK)
    {
      is_special = TRUE;
      *kind = svn_node_file;
    }
  else
    *kind = svn_node_unknown;

  *is_special_p = is_special;
  
  return SVN_NO_ERROR;
}


svn_error_t *
svn_io_check_resolved_path (const char *path,
                            svn_node_kind_t *kind,
                            apr_pool_t *pool)
{
  svn_boolean_t ignored;
  return io_check_path (path, TRUE, &ignored, kind, pool);
}

svn_error_t *
svn_io_check_path (const char *path,
                   svn_node_kind_t *kind,
                   apr_pool_t *pool)
{
  svn_boolean_t ignored;
  return io_check_path (path, FALSE, &ignored, kind, pool);
}

svn_error_t *
svn_io_check_special_path (const char *path,
                           svn_node_kind_t *kind,
                           svn_boolean_t *is_special,
                           apr_pool_t *pool)
{
  return io_check_path (path, FALSE, is_special, kind, pool);
}

svn_error_t *
svn_io_open_unique_file (apr_file_t **f,
                         const char **unique_name_p,
                         const char *path,
                         const char *suffix,
                         svn_boolean_t delete_on_close,
                         apr_pool_t *pool)
{
  unsigned int i;
  const char *unique_name;
  const char *unique_name_apr;

  for (i = 1; i <= 99999; i++)
    {
      apr_status_t apr_err;
      apr_int32_t flag = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL
                          | APR_BUFFERED);

      if (delete_on_close)
        flag |= APR_DELONCLOSE;

      /* Special case the first attempt -- if we can avoid having a
         generated numeric portion at all, that's best.  So first we
         try with just the suffix; then future tries add a number
         before the suffix.  (A do-while loop could avoid the repeated
         conditional, but it's not worth the clarity loss.)

         If the first attempt fails, the first number will be "2".
         This is good, since "1" would misleadingly imply that
         the second attempt was actually the first... and if someone's
         got conflicts on their conflicts, we probably don't want to
         add to their confusion :-). */
      if (i == 1)
        unique_name = apr_psprintf (pool, "%s%s", path, suffix);
      else
        unique_name = apr_psprintf (pool, "%s.%u%s", path, i, suffix);

      /* Hmmm.  Ideally, we would append to a native-encoding buf
         before starting iteration, then convert back to UTF-8 for
         return. But I suppose that would make the appending code
         sensitive to i18n in a way it shouldn't be... Oh well. */
      SVN_ERR (svn_path_cstring_from_utf8 (&unique_name_apr, unique_name,
                                           pool));

      apr_err = apr_file_open (f, unique_name_apr, flag,
                               APR_OS_DEFAULT, pool);

      if (APR_STATUS_IS_EEXIST (apr_err))
        continue;
      else if (apr_err)
        {
          /* On Win32, CreateFile failswith an "Access Denied" error
             code, rather than "File Already Exists", if the colliding
             name belongs to a directory. */
          if (APR_STATUS_IS_EACCES (apr_err))
            {
              apr_finfo_t finfo;
              apr_status_t apr_err_2 = apr_stat (&finfo, unique_name_apr,
                                                 APR_FINFO_TYPE, pool);

              if (!apr_err_2
                  && (finfo.filetype == APR_DIR))
                continue;

              /* Else ignore apr_err_2; better to fall through and
                 return the original error. */
            }

          *f = NULL;
          *unique_name_p = NULL;
          return svn_error_wrap_apr (apr_err, "Can't open '%s'", unique_name);
        }
      else
        {
          *unique_name_p = unique_name;
          return SVN_NO_ERROR;
        }
    }

  *f = NULL;
  *unique_name_p = NULL;
  return svn_error_createf (SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
                            NULL,
                            "Unable to make name for '%s'", path);
}

svn_error_t *
svn_io_create_unique_link (const char **unique_name_p,
                           const char *path,
                           const char *dest,
                           const char *suffix,
                           apr_pool_t *pool)
{
#ifdef HAVE_SYMLINK  
  unsigned int i;
  const char *unique_name;
  const char *unique_name_apr;
  int rv;

  for (i = 1; i <= 99999; i++)
    {
      apr_status_t apr_err;

      /* Special case the first attempt -- if we can avoid having a
         generated numeric portion at all, that's best.  So first we
         try with just the suffix; then future tries add a number
         before the suffix.  (A do-while loop could avoid the repeated
         conditional, but it's not worth the clarity loss.)

         If the first attempt fails, the first number will be "2".
         This is good, since "1" would misleadingly imply that
         the second attempt was actually the first... and if someone's
         got conflicts on their conflicts, we probably don't want to
         add to their confusion :-). */
      if (i == 1)
        unique_name = apr_psprintf (pool, "%s%s", path, suffix);
      else
        unique_name = apr_psprintf (pool, "%s.%u%s", path, i, suffix);

      /* Hmmm.  Ideally, we would append to a native-encoding buf
         before starting iteration, then convert back to UTF-8 for
         return. But I suppose that would make the appending code
         sensitive to i18n in a way it shouldn't be... Oh well. */
      SVN_ERR (svn_path_cstring_from_utf8 (&unique_name_apr, unique_name,
                                           pool));

      do {
        rv = symlink (dest, unique_name_apr);
      } while (rv == -1 && APR_STATUS_IS_EINTR (apr_get_os_error ()));
      
      apr_err = apr_get_os_error();
      
      if (rv == -1 && APR_STATUS_IS_EEXIST (apr_err))
        continue;
      else if (rv == -1 && apr_err)
        {
          /* On Win32, CreateFile failswith an "Access Denied" error
             code, rather than "File Already Exists", if the colliding
             name belongs to a directory. */
          if (APR_STATUS_IS_EACCES (apr_err))
            {
              apr_finfo_t finfo;
              apr_status_t apr_err_2 = apr_stat (&finfo, unique_name_apr,
                                                 APR_FINFO_TYPE, pool);

              if (!apr_err_2
                  && (finfo.filetype == APR_DIR))
                continue;

              /* Else ignore apr_err_2; better to fall through and
                 return the original error. */
            }

          *unique_name_p = NULL;
          return svn_error_wrap_apr (apr_err, "Can't open '%s'", unique_name);
        }
      else
        {
          *unique_name_p = unique_name;
          return SVN_NO_ERROR;
        }
    }

  *unique_name_p = NULL;
  return svn_error_createf (SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
                            NULL,
                            "Unable to make name for '%s'", path);
#else
  return svn_error_create (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                           "Symbolic links are not supported on this "
                           "platform");
#endif  
}

svn_error_t *
svn_io_read_link (svn_string_t **dest,
                  const char *path,
                  apr_pool_t *pool)
{
#ifdef HAVE_READLINK  
  char buf[1024];
  int rv;
  
  do {
    rv = readlink (path, buf, sizeof(buf));
  } while (rv == -1 && APR_STATUS_IS_EINTR (apr_get_os_error ()));

  if (rv == -1)
    return svn_error_wrap_apr
      (apr_get_os_error (), "Can't read contents of link");

  *dest = svn_string_ncreate (buf, rv, pool);
  
  return SVN_NO_ERROR;
#else
  return svn_error_create (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                           "Symbolic links are not supported on this "
                           "platform");
#endif  
}


svn_error_t *
svn_io_copy_link (const char *src,
                  const char *dst,
                  apr_pool_t *pool)

{
#ifdef HAVE_READLINK
  svn_string_t *link_dest;
  const char *dst_tmp;

  /* Notice what the link is pointing at... */
  SVN_ERR (svn_io_read_link (&link_dest, src, pool));

  /* Make a tmp-link pointing at the same thing. */
  SVN_ERR (svn_io_create_unique_link (&dst_tmp, dst, link_dest->data,
                                      ".tmp", pool));
  
  /* Move the tmp-link to link. */
  return svn_io_file_rename (dst_tmp, dst, pool);

#else
  return svn_error_create (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                           "Symbolic links are not supported on this "
                           "platform");
#endif
}


#if 1 /* TODO: Remove this code when APR 0.9.6 is released. */
#include "apr_env.h"

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -