📄 io.c
字号:
/*
* 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 + -