mirror of
https://github.com/transmission/transmission
synced 2024-12-26 09:37:56 +00:00
879a2afcbd
The Berne Convention says that the copyright year is moot, so instead of adding another year to each file as in previous years, I've removed the year altogether from the source code comments in libtransmission, gtk, qt, utils, daemon, and cli. Juliusz's copyright notice in tr-dht and Johannes' copyright notice in tr-lpd have been left alone; it didn't seem appropriate to modify them.
828 lines
20 KiB
C
828 lines
20 KiB
C
/*
|
|
* This file Copyright (C) Mnemosyne LLC
|
|
*
|
|
* This file is licensed by the GPL version 2. Works owned by the
|
|
* Transmission project are granted a special exemption to clause 2(b)
|
|
* so that the bulk of its code can remain under the MIT license.
|
|
* This exemption does not extend to derived works not owned by
|
|
* the Transmission project.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
#include <w32api.h>
|
|
#define WINVER WindowsXP
|
|
#include <windows.h>
|
|
#include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
|
|
#else
|
|
#ifdef SYS_DARWIN
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
#endif
|
|
#ifdef __HAIKU__
|
|
#include <FindDirectory.h>
|
|
#endif
|
|
#define _XOPEN_SOURCE 600 /* needed for recursive locks. */
|
|
#ifndef __USE_UNIX98
|
|
#define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
|
|
#endif
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef SYS_DARWIN
|
|
#define HAVE_SYS_STATVFS_H
|
|
#define HAVE_STATVFS
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_SYS_STATVFS_H
|
|
#include <sys/statvfs.h>
|
|
#endif
|
|
#ifdef WIN32
|
|
#include <libgen.h>
|
|
#endif
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h> /* getuid getpid close */
|
|
|
|
#include "transmission.h"
|
|
#include "session.h"
|
|
#include "list.h"
|
|
#include "platform.h"
|
|
#include "utils.h"
|
|
|
|
/***
|
|
**** THREADS
|
|
***/
|
|
|
|
#ifdef WIN32
|
|
typedef DWORD tr_thread_id;
|
|
#else
|
|
typedef pthread_t tr_thread_id;
|
|
#endif
|
|
|
|
static tr_thread_id
|
|
tr_getCurrentThread( void )
|
|
{
|
|
#ifdef WIN32
|
|
return GetCurrentThreadId( );
|
|
#else
|
|
return pthread_self( );
|
|
#endif
|
|
}
|
|
|
|
static tr_bool
|
|
tr_areThreadsEqual( tr_thread_id a, tr_thread_id b )
|
|
{
|
|
#ifdef WIN32
|
|
return a == b;
|
|
#else
|
|
return pthread_equal( a, b ) != 0;
|
|
#endif
|
|
}
|
|
|
|
/** @brief portability wrapper around OS-dependent threads */
|
|
struct tr_thread
|
|
{
|
|
void ( * func )( void * );
|
|
void * arg;
|
|
tr_thread_id thread;
|
|
#ifdef WIN32
|
|
HANDLE thread_handle;
|
|
#endif
|
|
};
|
|
|
|
tr_bool
|
|
tr_amInThread( const tr_thread * t )
|
|
{
|
|
return tr_areThreadsEqual( tr_getCurrentThread( ), t->thread );
|
|
}
|
|
|
|
#ifdef WIN32
|
|
#define ThreadFuncReturnType unsigned WINAPI
|
|
#else
|
|
#define ThreadFuncReturnType void
|
|
#endif
|
|
|
|
static ThreadFuncReturnType
|
|
ThreadFunc( void * _t )
|
|
{
|
|
tr_thread * t = _t;
|
|
|
|
t->func( t->arg );
|
|
|
|
tr_free( t );
|
|
#ifdef WIN32
|
|
_endthreadex( 0 );
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
tr_thread *
|
|
tr_threadNew( void ( *func )(void *),
|
|
void * arg )
|
|
{
|
|
tr_thread * t = tr_new0( tr_thread, 1 );
|
|
|
|
t->func = func;
|
|
t->arg = arg;
|
|
|
|
#ifdef WIN32
|
|
{
|
|
unsigned int id;
|
|
t->thread_handle =
|
|
(HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0,
|
|
&id );
|
|
t->thread = (DWORD) id;
|
|
}
|
|
#else
|
|
pthread_create( &t->thread, NULL, (void*(*)(void*))ThreadFunc, t );
|
|
pthread_detach( t->thread );
|
|
|
|
#endif
|
|
|
|
return t;
|
|
}
|
|
|
|
/***
|
|
**** LOCKS
|
|
***/
|
|
|
|
/** @brief portability wrapper around OS-dependent thread mutexes */
|
|
struct tr_lock
|
|
{
|
|
int depth;
|
|
#ifdef WIN32
|
|
CRITICAL_SECTION lock;
|
|
DWORD lockThread;
|
|
#else
|
|
pthread_mutex_t lock;
|
|
pthread_t lockThread;
|
|
#endif
|
|
};
|
|
|
|
tr_lock*
|
|
tr_lockNew( void )
|
|
{
|
|
tr_lock * l = tr_new0( tr_lock, 1 );
|
|
|
|
#ifdef WIN32
|
|
InitializeCriticalSection( &l->lock ); /* supports recursion */
|
|
#else
|
|
pthread_mutexattr_t attr;
|
|
pthread_mutexattr_init( &attr );
|
|
pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
|
|
pthread_mutex_init( &l->lock, &attr );
|
|
#endif
|
|
|
|
return l;
|
|
}
|
|
|
|
void
|
|
tr_lockFree( tr_lock * l )
|
|
{
|
|
#ifdef WIN32
|
|
DeleteCriticalSection( &l->lock );
|
|
#else
|
|
pthread_mutex_destroy( &l->lock );
|
|
#endif
|
|
tr_free( l );
|
|
}
|
|
|
|
void
|
|
tr_lockLock( tr_lock * l )
|
|
{
|
|
#ifdef WIN32
|
|
EnterCriticalSection( &l->lock );
|
|
#else
|
|
pthread_mutex_lock( &l->lock );
|
|
#endif
|
|
assert( l->depth >= 0 );
|
|
if( l->depth )
|
|
assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
|
|
l->lockThread = tr_getCurrentThread( );
|
|
++l->depth;
|
|
}
|
|
|
|
int
|
|
tr_lockHave( const tr_lock * l )
|
|
{
|
|
return ( l->depth > 0 )
|
|
&& ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
|
|
}
|
|
|
|
void
|
|
tr_lockUnlock( tr_lock * l )
|
|
{
|
|
assert( l->depth > 0 );
|
|
assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
|
|
|
|
--l->depth;
|
|
assert( l->depth >= 0 );
|
|
#ifdef WIN32
|
|
LeaveCriticalSection( &l->lock );
|
|
#else
|
|
pthread_mutex_unlock( &l->lock );
|
|
#endif
|
|
}
|
|
|
|
/***
|
|
**** PATHS
|
|
***/
|
|
|
|
#ifndef WIN32
|
|
#include <pwd.h>
|
|
#endif
|
|
|
|
static const char *
|
|
getHomeDir( void )
|
|
{
|
|
static char * home = NULL;
|
|
|
|
if( !home )
|
|
{
|
|
home = tr_strdup( getenv( "HOME" ) );
|
|
|
|
if( !home )
|
|
{
|
|
#ifdef WIN32
|
|
char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
|
|
*appdata = '\0';
|
|
SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, 0, appdata );
|
|
home = tr_strdup( appdata );
|
|
#else
|
|
struct passwd * pw = getpwuid( getuid( ) );
|
|
if( pw )
|
|
home = tr_strdup( pw->pw_dir );
|
|
endpwent( );
|
|
#endif
|
|
}
|
|
|
|
if( !home )
|
|
home = tr_strdup( "" );
|
|
}
|
|
|
|
return home;
|
|
}
|
|
|
|
static const char *
|
|
getOldConfigDir( void )
|
|
{
|
|
static char * path = NULL;
|
|
|
|
if( !path )
|
|
{
|
|
#ifdef SYS_DARWIN
|
|
path = tr_buildPath( getHomeDir( ), "Library",
|
|
"Application Support",
|
|
"Transmission", NULL );
|
|
#elif defined( WIN32 )
|
|
char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
|
|
SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
|
|
path = tr_buildPath( appdata, "Transmission", NULL );
|
|
#elif defined( __HAIKU__ )
|
|
char buf[TR_PATH_MAX];
|
|
find_directory( B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof(buf) );
|
|
path = tr_buildPath( buf, "Transmission", NULL );
|
|
#else
|
|
path = tr_buildPath( getHomeDir( ), ".transmission", NULL );
|
|
#endif
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
#if defined(SYS_DARWIN) || defined(WIN32)
|
|
#define RESUME_SUBDIR "Resume"
|
|
#define TORRENT_SUBDIR "Torrents"
|
|
#else
|
|
#define RESUME_SUBDIR "resume"
|
|
#define TORRENT_SUBDIR "torrents"
|
|
#endif
|
|
|
|
static const char *
|
|
getOldTorrentsDir( void )
|
|
{
|
|
static char * path = NULL;
|
|
|
|
if( !path )
|
|
path = tr_buildPath( getOldConfigDir( ), TORRENT_SUBDIR, NULL );
|
|
|
|
return path;
|
|
}
|
|
|
|
static const char *
|
|
getOldCacheDir( void )
|
|
{
|
|
static char * path = NULL;
|
|
|
|
if( !path )
|
|
{
|
|
#if defined( WIN32 )
|
|
path = tr_buildPath( getOldConfigDir( ), "Cache", NULL );
|
|
#elif defined( SYS_DARWIN )
|
|
path = tr_buildPath( getHomeDir( ), "Library", "Caches", "Transmission", NULL );
|
|
#else
|
|
path = tr_buildPath( getOldConfigDir( ), "cache", NULL );
|
|
#endif
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
static void
|
|
moveFiles( const char * oldDir,
|
|
const char * newDir )
|
|
{
|
|
if( oldDir && newDir && strcmp( oldDir, newDir ) )
|
|
{
|
|
DIR * dirh = opendir( oldDir );
|
|
if( dirh )
|
|
{
|
|
int count = 0;
|
|
struct dirent * dirp;
|
|
while( ( dirp = readdir( dirh ) ) )
|
|
{
|
|
const char * name = dirp->d_name;
|
|
if( name && strcmp( name, "." ) && strcmp( name, ".." ) )
|
|
{
|
|
char * o = tr_buildPath( oldDir, name, NULL );
|
|
char * n = tr_buildPath( newDir, name, NULL );
|
|
rename( o, n );
|
|
++count;
|
|
tr_free( n );
|
|
tr_free( o );
|
|
}
|
|
}
|
|
|
|
if( count )
|
|
tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
|
|
count, oldDir, newDir );
|
|
closedir( dirh );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
migrateFiles( const tr_session * session )
|
|
{
|
|
static int migrated = FALSE;
|
|
|
|
if( !migrated )
|
|
{
|
|
const char * oldDir;
|
|
const char * newDir;
|
|
migrated = TRUE;
|
|
|
|
oldDir = getOldTorrentsDir( );
|
|
newDir = tr_getTorrentDir( session );
|
|
moveFiles( oldDir, newDir );
|
|
|
|
oldDir = getOldCacheDir( );
|
|
newDir = tr_getResumeDir( session );
|
|
moveFiles( oldDir, newDir );
|
|
}
|
|
}
|
|
|
|
void
|
|
tr_setConfigDir( tr_session * session, const char * configDir )
|
|
{
|
|
char * path;
|
|
|
|
session->configDir = tr_strdup( configDir );
|
|
|
|
path = tr_buildPath( configDir, RESUME_SUBDIR, NULL );
|
|
tr_mkdirp( path, 0777 );
|
|
session->resumeDir = path;
|
|
|
|
path = tr_buildPath( configDir, TORRENT_SUBDIR, NULL );
|
|
tr_mkdirp( path, 0777 );
|
|
session->torrentDir = path;
|
|
|
|
migrateFiles( session );
|
|
}
|
|
|
|
const char *
|
|
tr_sessionGetConfigDir( const tr_session * session )
|
|
{
|
|
return session->configDir;
|
|
}
|
|
|
|
const char *
|
|
tr_getTorrentDir( const tr_session * session )
|
|
{
|
|
return session->torrentDir;
|
|
}
|
|
|
|
const char *
|
|
tr_getResumeDir( const tr_session * session )
|
|
{
|
|
return session->resumeDir;
|
|
}
|
|
|
|
const char*
|
|
tr_getDefaultConfigDir( const char * appname )
|
|
{
|
|
static char * s = NULL;
|
|
|
|
if( !appname || !*appname )
|
|
appname = "Transmission";
|
|
|
|
if( !s )
|
|
{
|
|
if( ( s = getenv( "TRANSMISSION_HOME" ) ) )
|
|
{
|
|
s = tr_strdup( s );
|
|
}
|
|
else
|
|
{
|
|
#ifdef SYS_DARWIN
|
|
s = tr_buildPath( getHomeDir( ), "Library", "Application Support",
|
|
appname, NULL );
|
|
#elif defined( WIN32 )
|
|
char appdata[TR_PATH_MAX]; /* SHGetFolderPath() requires MAX_PATH */
|
|
SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
|
|
s = tr_buildPath( appdata, appname, NULL );
|
|
#elif defined( __HAIKU__ )
|
|
char buf[TR_PATH_MAX];
|
|
find_directory( B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof(buf) );
|
|
s = tr_buildPath( buf, appname, NULL );
|
|
#else
|
|
if( ( s = getenv( "XDG_CONFIG_HOME" ) ) )
|
|
s = tr_buildPath( s, appname, NULL );
|
|
else
|
|
s = tr_buildPath( getHomeDir( ), ".config", appname, NULL );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
const char*
|
|
tr_getDefaultDownloadDir( void )
|
|
{
|
|
static char * user_dir = NULL;
|
|
|
|
if( user_dir == NULL )
|
|
{
|
|
const char * config_home;
|
|
char * config_file;
|
|
char * content;
|
|
size_t content_len;
|
|
|
|
/* figure out where to look for user-dirs.dirs */
|
|
config_home = getenv( "XDG_CONFIG_HOME" );
|
|
if( config_home && *config_home )
|
|
config_file = tr_buildPath( config_home, "user-dirs.dirs", NULL );
|
|
else
|
|
config_file = tr_buildPath( getHomeDir( ), ".config", "user-dirs.dirs", NULL );
|
|
|
|
/* read in user-dirs.dirs and look for the download dir entry */
|
|
content = (char *) tr_loadFile( config_file, &content_len );
|
|
if( content && content_len>0 )
|
|
{
|
|
const char * key = "XDG_DOWNLOAD_DIR=\"";
|
|
char * line = strstr( content, key );
|
|
if( line != NULL )
|
|
{
|
|
char * value = line + strlen( key );
|
|
char * end = strchr( value, '"' );
|
|
|
|
if( end )
|
|
{
|
|
*end = '\0';
|
|
|
|
if( !memcmp( value, "$HOME/", 6 ) )
|
|
user_dir = tr_buildPath( getHomeDir( ), value+6, NULL );
|
|
else
|
|
user_dir = tr_strdup( value );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( user_dir == NULL )
|
|
#ifdef __HAIKU__
|
|
user_dir = tr_buildPath( getHomeDir( ), "Desktop", NULL );
|
|
#else
|
|
user_dir = tr_buildPath( getHomeDir( ), "Downloads", NULL );
|
|
#endif
|
|
|
|
tr_free( content );
|
|
tr_free( config_file );
|
|
}
|
|
|
|
return user_dir;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
static int
|
|
isWebClientDir( const char * path )
|
|
{
|
|
struct stat sb;
|
|
char * tmp = tr_buildPath( path, "index.html", NULL );
|
|
const int ret = !stat( tmp, &sb );
|
|
tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
|
|
tr_free( tmp );
|
|
return ret;
|
|
}
|
|
|
|
const char *
|
|
tr_getWebClientDir( const tr_session * session UNUSED )
|
|
{
|
|
static char * s = NULL;
|
|
|
|
if( !s )
|
|
{
|
|
if( ( s = getenv( "CLUTCH_HOME" ) ) )
|
|
{
|
|
s = tr_strdup( s );
|
|
}
|
|
else if( ( s = getenv( "TRANSMISSION_WEB_HOME" ) ) )
|
|
{
|
|
s = tr_strdup( s );
|
|
}
|
|
else
|
|
{
|
|
|
|
#ifdef SYS_DARWIN /* on Mac, look in the Application Support folder first, then in the app bundle. */
|
|
|
|
/* Look in the Application Support folder */
|
|
s = tr_buildPath( tr_sessionGetConfigDir( session ), "web", NULL );
|
|
|
|
if( !isWebClientDir( s ) ) {
|
|
tr_free( s );
|
|
|
|
CFURLRef appURL = CFBundleCopyBundleURL( CFBundleGetMainBundle( ) );
|
|
CFStringRef appRef = CFURLCopyFileSystemPath( appURL,
|
|
kCFURLPOSIXPathStyle );
|
|
CFIndex appLength = CFStringGetMaximumSizeForEncoding( CFStringGetLength(appRef),
|
|
CFStringGetFastestEncoding( appRef ));
|
|
|
|
char * appString = tr_malloc( appLength + 1 );
|
|
tr_bool success = CFStringGetCString( appRef,
|
|
appString,
|
|
appLength + 1,
|
|
CFStringGetFastestEncoding( appRef ));
|
|
assert( success );
|
|
|
|
CFRelease( appURL );
|
|
CFRelease( appRef );
|
|
|
|
/* Fallback to the app bundle */
|
|
s = tr_buildPath( appString, "Contents", "Resources", "web", NULL );
|
|
if( !isWebClientDir( s ) ) {
|
|
tr_free( s );
|
|
s = NULL;
|
|
}
|
|
|
|
tr_free( appString );
|
|
}
|
|
|
|
#elif defined( WIN32 )
|
|
|
|
/* SHGetFolderPath explicitly requires MAX_PATH length */
|
|
char dir[MAX_PATH];
|
|
|
|
/* Generally, Web interface should be stored in a Web subdir of
|
|
* calling executable dir. */
|
|
|
|
if( s == NULL ) {
|
|
/* First, we should check personal AppData/Transmission/Web */
|
|
SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, 0, dir );
|
|
s = tr_buildPath( dir, "Transmission", "Web", NULL );
|
|
if( !isWebClientDir( s ) ) {
|
|
tr_free( s );
|
|
s = NULL;
|
|
}
|
|
}
|
|
|
|
if( s == NULL ) {
|
|
/* check personal AppData */
|
|
SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, dir );
|
|
s = tr_buildPath( dir, "Transmission", "Web", NULL );
|
|
if( !isWebClientDir( s ) ) {
|
|
tr_free( s );
|
|
s = NULL;
|
|
}
|
|
}
|
|
|
|
if( s == NULL) {
|
|
/* check calling module place */
|
|
GetModuleFileName( GetModuleHandle( NULL ), dir, sizeof( dir ) );
|
|
s = tr_buildPath( dirname( dir ), "Web", NULL );
|
|
if( !isWebClientDir( s ) ) {
|
|
tr_free( s );
|
|
s = NULL;
|
|
}
|
|
}
|
|
|
|
#else /* everyone else, follow the XDG spec */
|
|
|
|
tr_list *candidates = NULL, *l;
|
|
const char * tmp;
|
|
|
|
/* XDG_DATA_HOME should be the first in the list of candidates */
|
|
tmp = getenv( "XDG_DATA_HOME" );
|
|
if( tmp && *tmp )
|
|
tr_list_append( &candidates, tr_strdup( tmp ) );
|
|
else {
|
|
char * dhome = tr_buildPath( getHomeDir( ), ".local", "share", NULL );
|
|
tr_list_append( &candidates, dhome );
|
|
}
|
|
|
|
/* XDG_DATA_DIRS are the backup directories */
|
|
{
|
|
const char * pkg = PACKAGE_DATA_DIR;
|
|
const char * xdg = getenv( "XDG_DATA_DIRS" );
|
|
const char * fallback = "/usr/local/share:/usr/share";
|
|
char * buf = tr_strdup_printf( "%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback );
|
|
tmp = buf;
|
|
while( tmp && *tmp ) {
|
|
const char * end = strchr( tmp, ':' );
|
|
if( end ) {
|
|
if( ( end - tmp ) > 1 )
|
|
tr_list_append( &candidates, tr_strndup( tmp, end - tmp ) );
|
|
tmp = end + 1;
|
|
} else if( tmp && *tmp ) {
|
|
tr_list_append( &candidates, tr_strdup( tmp ) );
|
|
break;
|
|
}
|
|
}
|
|
tr_free( buf );
|
|
}
|
|
|
|
/* walk through the candidates & look for a match */
|
|
for( l=candidates; l; l=l->next ) {
|
|
char * path = tr_buildPath( l->data, "transmission", "web", NULL );
|
|
const int found = isWebClientDir( path );
|
|
if( found ) {
|
|
s = path;
|
|
break;
|
|
}
|
|
tr_free( path );
|
|
}
|
|
|
|
tr_list_free( &candidates, tr_free );
|
|
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
int64_t
|
|
tr_getFreeSpace( const char * path )
|
|
{
|
|
#ifdef WIN32
|
|
uint64_t freeBytesAvailable = 0;
|
|
return GetDiskFreeSpaceEx( path, &freeBytesAvailable, NULL, NULL)
|
|
? (int64_t)freeBytesAvailable
|
|
: -1;
|
|
#elif defined(HAVE_STATVFS)
|
|
struct statvfs buf;
|
|
return statvfs( path, &buf ) ? -1 : (int64_t)buf.f_bavail * (int64_t)buf.f_bsize;
|
|
#else
|
|
#warning FIXME: not implemented
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
#ifdef WIN32
|
|
|
|
/* The following mmap functions are by Joerg Walter, and were taken from
|
|
* his paper at: http://www.genesys-e.de/jwalter/mix4win.htm
|
|
*/
|
|
|
|
#if defined(_MSC_VER)
|
|
__declspec( align( 4 ) ) static LONG volatile g_sl;
|
|
#else
|
|
static LONG volatile g_sl __attribute__ ( ( aligned ( 4 ) ) );
|
|
#endif
|
|
|
|
/* Wait for spin lock */
|
|
static int
|
|
slwait( LONG volatile *sl )
|
|
{
|
|
while( InterlockedCompareExchange ( sl, 1, 0 ) != 0 )
|
|
Sleep ( 0 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Release spin lock */
|
|
static int
|
|
slrelease( LONG volatile *sl )
|
|
{
|
|
InterlockedExchange ( sl, 0 );
|
|
return 0;
|
|
}
|
|
|
|
/* getpagesize for windows */
|
|
static long
|
|
getpagesize( void )
|
|
{
|
|
static long g_pagesize = 0;
|
|
|
|
if( !g_pagesize )
|
|
{
|
|
SYSTEM_INFO system_info;
|
|
GetSystemInfo ( &system_info );
|
|
g_pagesize = system_info.dwPageSize;
|
|
}
|
|
return g_pagesize;
|
|
}
|
|
|
|
static long
|
|
getregionsize( void )
|
|
{
|
|
static long g_regionsize = 0;
|
|
|
|
if( !g_regionsize )
|
|
{
|
|
SYSTEM_INFO system_info;
|
|
GetSystemInfo ( &system_info );
|
|
g_regionsize = system_info.dwAllocationGranularity;
|
|
}
|
|
return g_regionsize;
|
|
}
|
|
|
|
void *
|
|
mmap( void *ptr,
|
|
long size,
|
|
long prot,
|
|
long type,
|
|
long handle,
|
|
long arg )
|
|
{
|
|
static long g_pagesize;
|
|
static long g_regionsize;
|
|
|
|
/* Wait for spin lock */
|
|
slwait ( &g_sl );
|
|
/* First time initialization */
|
|
if( !g_pagesize )
|
|
g_pagesize = getpagesize ( );
|
|
if( !g_regionsize )
|
|
g_regionsize = getregionsize ( );
|
|
/* Allocate this */
|
|
ptr = VirtualAlloc ( ptr, size,
|
|
MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
|
|
PAGE_READWRITE );
|
|
if( !ptr )
|
|
{
|
|
ptr = (void *) -1;
|
|
goto mmap_exit;
|
|
}
|
|
mmap_exit:
|
|
/* Release spin lock */
|
|
slrelease ( &g_sl );
|
|
return ptr;
|
|
}
|
|
|
|
long
|
|
munmap( void *ptr,
|
|
long size )
|
|
{
|
|
static long g_pagesize;
|
|
static long g_regionsize;
|
|
int rc = -1;
|
|
|
|
/* Wait for spin lock */
|
|
slwait ( &g_sl );
|
|
/* First time initialization */
|
|
if( !g_pagesize )
|
|
g_pagesize = getpagesize ( );
|
|
if( !g_regionsize )
|
|
g_regionsize = getregionsize ( );
|
|
/* Free this */
|
|
if( !VirtualFree ( ptr, 0,
|
|
MEM_RELEASE ) )
|
|
goto munmap_exit;
|
|
rc = 0;
|
|
munmap_exit:
|
|
/* Release spin lock */
|
|
slrelease ( &g_sl );
|
|
return rc;
|
|
}
|
|
|
|
#endif
|