📄 compat.c
字号:
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
const char compat_c_id[] =
"$Id$";
/**
* \file compat.c
* \brief Wrappers to make calls more portable. This code defines
* functions such as tor_malloc, tor_snprintf, get/set various data types,
* renaming, setting socket options, switching user IDs. It is basically
* where the non-portable items are conditionally included depending on
* the platform.
**/
/* This is required on rh7 to make strptime not complain.
* We also need it to make memmem get defined (where available)
*/
#define _GNU_SOURCE
#include "orconfig.h"
#include "compat.h"
#ifdef MS_WINDOWS
#include <process.h>
#include <windows.h>
#endif
#ifdef HAVE_UNAME
#include <sys/utsname.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_FCNTL_H
#include <sys/fcntl.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifndef HAVE_GETTIMEOFDAY
#ifdef HAVE_FTIME
#include <sys/timeb.h>
#endif
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h> /* FreeBSD needs this to know what version it is */
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#ifdef HAVE_SYS_UTIME_H
#include <sys/utime.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifdef HAVE_SYS_SYSLIMITS_H
#include <sys/syslimits.h>
#endif
#ifdef USE_BSOCKETS
#include <bsocket.h>
#endif
#include "log.h"
#include "util.h"
#include "container.h"
/* Inline the strl functions if the platform doesn't have them. */
#ifndef HAVE_STRLCPY
#include "strlcpy.c"
#endif
#ifndef HAVE_STRLCAT
#include "strlcat.c"
#endif
#ifndef INADDR_NONE
/* This is used by inet_addr, but apparently Solaris doesn't define it
* anyplace. */
#define INADDR_NONE ((unsigned long) -1)
#endif
#ifdef HAVE_SYS_MMAN_H
/** Implementation for tor_mmap_t: holds the regular tor_mmap_t, along
* with extra fields needed for mmap()-based memory mapping. */
typedef struct tor_mmap_impl_t {
tor_mmap_t base;
size_t mapping_size; /**< Size of the actual mapping. (This is this file
* size, rounded up to the nearest page.) */
} tor_mmap_impl_t;
/** Try to create a memory mapping for <b>filename</b> and return it. On
* failure, return NULL. Sets errno properly, using ERANGE to mean
* "empty file". */
tor_mmap_t *
tor_mmap_file(const char *filename)
{
int fd; /* router file */
char *string;
int page_size;
tor_mmap_impl_t *res;
size_t size, filesize;
tor_assert(filename);
fd = open(filename, O_RDONLY, 0);
if (fd<0) {
int save_errno = errno;
int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN;
log_fn(severity, LD_FS,"Could not open \"%s\" for mmap(): %s",filename,
strerror(errno));
errno = save_errno;
return NULL;
}
size = filesize = (size_t) lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
/* ensure page alignment */
page_size = getpagesize();
size += (size%page_size) ? page_size-(size%page_size) : 0;
if (!size) {
/* Zero-length file. If we call mmap on it, it will succeed but
* return NULL, and bad things will happen. So just fail. */
log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
errno = ERANGE;
close(fd);
return NULL;
}
string = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
if (string == MAP_FAILED) {
int save_errno = errno;
log_warn(LD_FS,"Could not mmap file \"%s\": %s", filename,
strerror(errno));
errno = save_errno;
return NULL;
}
res = tor_malloc_zero(sizeof(tor_mmap_impl_t));
res->base.data = string;
res->base.size = filesize;
res->mapping_size = size;
return &(res->base);
}
/** Release storage held for a memory mapping. */
void
tor_munmap_file(tor_mmap_t *handle)
{
tor_mmap_impl_t *h = SUBTYPE_P(handle, tor_mmap_impl_t, base);
munmap((char*)h->base.data, h->mapping_size);
tor_free(h);
}
#elif defined(MS_WINDOWS)
/** Implementation for tor_mmap_t: holds the regular tor_mmap_t, along
* with extra fields needed for WIN32 memory mapping. */
typedef struct win_mmap_t {
tor_mmap_t base;
HANDLE file_handle;
HANDLE mmap_handle;
} win_mmap_t;
tor_mmap_t *
tor_mmap_file(const char *filename)
{
win_mmap_t *res = tor_malloc_zero(sizeof(win_mmap_t));
int empty = 0;
res->file_handle = INVALID_HANDLE_VALUE;
res->mmap_handle = NULL;
res->file_handle = CreateFile(filename,
GENERIC_READ, FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (res->file_handle == INVALID_HANDLE_VALUE)
goto win_err;
res->base.size = GetFileSize(res->file_handle, NULL);
if (res->base.size == 0) {
log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
empty = 1;
goto err;
}
res->mmap_handle = CreateFileMapping(res->file_handle,
NULL,
PAGE_READONLY,
#if SIZEOF_SIZE_T > 4
(res->base.size >> 32),
#else
0,
#endif
(res->base.size & 0xfffffffful),
NULL);
if (res->mmap_handle == NULL)
goto win_err;
res->base.data = (char*) MapViewOfFile(res->mmap_handle,
FILE_MAP_READ,
0, 0, 0);
if (!res->base.data)
goto win_err;
return &(res->base);
win_err: {
DWORD e = GetLastError();
int severity = (e == ERROR_FILE_NOT_FOUND || e == ERROR_PATH_NOT_FOUND) ?
LOG_INFO : LOG_WARN;
char *msg = format_win32_error(e);
log_fn(severity, LD_FS, "Couldn't mmap file \"%s\": %s", filename, msg);
tor_free(msg);
if (e == ERROR_FILE_NOT_FOUND || e == ERROR_PATH_NOT_FOUND)
errno = ENOENT;
else
errno = EINVAL;
}
err:
if (empty)
errno = ERANGE;
tor_munmap_file(&res->base);
return NULL;
}
void
tor_munmap_file(tor_mmap_t *handle)
{
win_mmap_t *h = SUBTYPE_P(handle, win_mmap_t, base);
if (handle->data)
/* This is an ugly cast, but without it, "data" in struct tor_mmap_t would
have to be redefined as non-const. */
UnmapViewOfFile( (LPVOID) handle->data);
if (h->mmap_handle != NULL)
CloseHandle(h->mmap_handle);
if (h->file_handle != INVALID_HANDLE_VALUE)
CloseHandle(h->file_handle);
tor_free(h);
}
#else
tor_mmap_t *
tor_mmap_file(const char *filename)
{
struct stat st;
char *res = read_file_to_str(filename, RFTS_BIN|RFTS_IGNORE_MISSING, &st);
tor_mmap_t *handle;
if (! res)
return NULL;
handle = tor_malloc_zero(sizeof(tor_mmap_t));
handle->data = res;
handle->size = st.st_size;
return handle;
}
void
tor_munmap_file(tor_mmap_t *handle)
{
char *d = (char*)handle->data;
tor_free(d);
memset(handle, 0, sizeof(tor_mmap_t));
tor_free(handle);
}
#endif
/** Replacement for snprintf. Differs from platform snprintf in two
* ways: First, always NUL-terminates its output. Second, always
* returns -1 if the result is truncated. (Note that this return
* behavior does <i>not</i> conform to C99; it just happens to be
* easier to emulate "return -1" with conformant implementations than
* it is to emulate "return number that would be written" with
* non-conformant implementations.) */
int
tor_snprintf(char *str, size_t size, const char *format, ...)
{
va_list ap;
int r;
va_start(ap,format);
r = tor_vsnprintf(str,size,format,ap);
va_end(ap);
return r;
}
/** Replacement for vsnprintf; behavior differs as tor_snprintf differs from
* snprintf.
*/
int
tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
{
int r;
if (size == 0)
return -1; /* no place for the NUL */
if (size > SIZE_T_CEILING)
return -1;
#ifdef MS_WINDOWS
r = _vsnprintf(str, size, format, args);
#else
r = vsnprintf(str, size, format, args);
#endif
str[size-1] = '\0';
if (r < 0 || ((size_t)r) >= size)
return -1;
return r;
}
/** Given <b>hlen</b> bytes at <b>haystack</b> and <b>nlen</b> bytes at
* <b>needle</b>, return a pointer to the first occurrence of the needle
* within the haystack, or NULL if there is no such occurrence.
*
* Requires that nlen be greater than zero.
*/
const void *
tor_memmem(const void *_haystack, size_t hlen,
const void *_needle, size_t nlen)
{
#if defined(HAVE_MEMMEM) && (!defined(__GNUC__) || __GNUC__ >= 2)
tor_assert(nlen);
return memmem(_haystack, hlen, _needle, nlen);
#else
/* This isn't as fast as the GLIBC implementation, but it doesn't need to
* be. */
const char *p, *end;
const char *haystack = (const char*)_haystack;
const char *needle = (const char*)_needle;
char first;
tor_assert(nlen);
p = haystack;
end = haystack + hlen;
first = *(const char*)needle;
while ((p = memchr(p, first, end-p))) {
if (p+nlen > end)
return NULL;
if (!memcmp(p, needle, nlen))
return p;
++p;
}
return NULL;
#endif
}
#ifdef MS_WINDOWS
/** Take a filename and return a pointer to its final element. This
* function is called on __FILE__ to fix a MSVC nit where __FILE__
* contains the full path to the file. This is bad, because it
* confuses users to find the home directory of the person who
* compiled the binary in their warrning messages.
*/
const char *
tor_fix_source_file(const char *fname)
{
const char *cp1, *cp2, *r;
cp1 = strrchr(fname, '/');
cp2 = strrchr(fname, '\\');
if (cp1 && cp2) {
r = (cp1<cp2)?(cp2+1):(cp1+1);
} else if (cp1) {
r = cp1+1;
} else if (cp2) {
r = cp2+1;
} else {
r = fname;
}
return r;
}
#endif
/**
* Read a 16-bit value beginning at <b>cp</b>. Equivalent to
* *(uint16_t*)(cp), but will not cause segfaults on platforms that forbid
* unaligned memory access.
*/
uint16_t
get_uint16(const char *cp)
{
uint16_t v;
memcpy(&v,cp,2);
return v;
}
/**
* Read a 32-bit value beginning at <b>cp</b>. Equivalent to
* *(uint32_t*)(cp), but will not cause segfaults on platforms that forbid
* unaligned memory access.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -