3835 lines
106 KiB
C
3835 lines
106 KiB
C
/*
|
|
Copyright (c) 2013 Anton Titov.
|
|
|
|
Copyright (c) 2013 pCloud Ltd. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met: Redistributions of source code must retain the above
|
|
copyright notice, this list of conditions and the following
|
|
disclaimer. Redistributions in binary form must reproduce the
|
|
above copyright notice, this list of conditions and the following
|
|
disclaimer in the documentation and/or materials provided with the
|
|
distribution. Neither the name of pCloud Ltd nor the names of its
|
|
contributors may be used to endorse or promote products derived
|
|
from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud
|
|
Ltd BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
DAMAGE.
|
|
*/
|
|
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <time.h>
|
|
#include "pcompat.h"
|
|
#include "psynclib.h"
|
|
#include "plibs.h"
|
|
#include "psettings.h"
|
|
#include "pssl.h"
|
|
#include "ptimer.h"
|
|
|
|
#if defined(P_OS_LINUX)
|
|
#include <sys/sysinfo.h>
|
|
#endif
|
|
|
|
#if defined(P_OS_MACOSX)
|
|
#include <sys/sysctl.h>
|
|
#include <sys/attr.h>
|
|
#include <SystemConfiguration/SystemConfiguration.h>
|
|
#endif
|
|
|
|
#if defined(P_OS_POSIX)
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/statvfs.h>
|
|
#include <sys/utsname.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/mman.h>
|
|
#include <netinet/tcp.h>
|
|
#include <net/if.h>
|
|
#include <utime.h>
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <ifaddrs.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <ctype.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
|
|
extern char **environ;
|
|
|
|
#if defined(MAP_ANONYMOUS)
|
|
#define PSYNC_MAP_ANONYMOUS MAP_ANONYMOUS
|
|
#elif defined(MAP_ANON)
|
|
#define PSYNC_MAP_ANONYMOUS MAP_ANON
|
|
#endif
|
|
|
|
#elif defined(P_OS_WINDOWS)
|
|
|
|
#include <process.h>
|
|
#include <windows.h>
|
|
#include <winhttp.h>
|
|
#include <ws2tcpip.h>
|
|
#include <wincrypt.h>
|
|
#include <tlhelp32.h>
|
|
#include <iphlpapi.h>
|
|
#include <shlobj.h>
|
|
|
|
#pragma comment(lib, "winhttp.lib")
|
|
|
|
#endif
|
|
|
|
#define PROXY_NONE 0
|
|
#define PROXY_CONNECT 1
|
|
|
|
typedef struct {
|
|
psync_thread_start0 run;
|
|
const char *name;
|
|
} psync_run_data0;
|
|
|
|
typedef struct {
|
|
psync_thread_start1 run;
|
|
void *ptr;
|
|
const char *name;
|
|
} psync_run_data1;
|
|
|
|
#if defined(P_OS_POSIX)
|
|
static uid_t psync_uid;
|
|
static gid_t psync_gid;
|
|
static gid_t *psync_gids;
|
|
static int psync_gids_cnt;
|
|
#endif
|
|
|
|
static int proxy_type=PROXY_NONE;
|
|
static int proxy_detected=0;
|
|
static char proxy_host[256];
|
|
static char proxy_port[8];
|
|
|
|
static int psync_page_size;
|
|
|
|
static const char *psync_software_name=PSYNC_LIB_VERSION;
|
|
static const char *psync_os_name=NULL;
|
|
|
|
PSYNC_THREAD const char *psync_thread_name="no name";
|
|
static pthread_mutex_t socket_mutex=PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
const unsigned char psync_invalid_filename_chars[256]={
|
|
#if defined(P_OS_WINDOWS)
|
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
#elif defined(P_OS_LINUX)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
#elif defined(P_OS_MACOSX)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
#else
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
#endif
|
|
};
|
|
|
|
int psync_user_is_admin(){
|
|
#if defined(P_OS_MACOSX)
|
|
struct group *ag;
|
|
int i;
|
|
if (psync_uid==0)
|
|
return 1;
|
|
else if (psync_gids_cnt==0)
|
|
return 0;
|
|
ag=getgrnam("admin");
|
|
if (!ag)
|
|
return 0;
|
|
for (i=0; i<psync_gids_cnt; i++)
|
|
if (ag->gr_gid==psync_gids[i])
|
|
return 1;
|
|
return 0;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
#if defined(P_OS_WINDOWS)
|
|
#if !defined(gmtime_r)
|
|
struct tm *gmtime_r(const time_t *timep, struct tm *result){
|
|
struct tm *res=gmtime(timep);
|
|
*result=*res;
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
/*static wchar_t *utf8_to_wchar(const char *str){
|
|
int len;
|
|
wchar_t *ret;
|
|
len=MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
|
ret=psync_new_cnt(wchar_t, len);
|
|
MultiByteToWideChar(CP_UTF8, 0, str, -1, ret, len);
|
|
return ret;
|
|
}*/
|
|
|
|
static wchar_t *utf8_to_wchar_path(const char *str){
|
|
int len;
|
|
wchar_t *ret;
|
|
len=MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
|
/* MAX_PATH seems not to be apporopriate here as it is defined as 260, and CreateDirectory() description says:
|
|
* There is a default string size limit for paths of 248 characters. This limit is related to how the CreateDirectory function parses paths.
|
|
*/
|
|
// if (len>=248){
|
|
ret=psync_new_cnt(wchar_t, len+4);
|
|
memcpy(ret, L"\\\\?\\", 4*sizeof(wchar_t));
|
|
MultiByteToWideChar(CP_UTF8, 0, str, -1, ret+4, len);
|
|
// }
|
|
// else{
|
|
// ret=psync_new_cnt(wchar_t, len);
|
|
// MultiByteToWideChar(CP_UTF8, 0, str, -1, ret, len);
|
|
// }
|
|
return ret;
|
|
}
|
|
|
|
static char *wchar_to_utf8(const wchar_t *str){
|
|
int len;
|
|
char *ret;
|
|
len=WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
|
|
ret=psync_new_cnt(char, len);
|
|
WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, len, NULL, NULL);
|
|
return ret;
|
|
}
|
|
|
|
int psync_stat(const char *path, psync_stat_t *st){
|
|
wchar_t *wpath;
|
|
HANDLE fd;
|
|
BOOL ret;
|
|
DWORD flag, attr;
|
|
wpath=utf8_to_wchar_path(path);
|
|
retry:
|
|
attr=GetFileAttributesW(wpath);
|
|
if (attr==INVALID_FILE_ATTRIBUTES){
|
|
psync_free(wpath);
|
|
return -1;
|
|
}
|
|
if (attr&FILE_ATTRIBUTE_DIRECTORY)
|
|
flag=FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_POSIX_SEMANTICS;
|
|
else
|
|
flag=FILE_ATTRIBUTE_NORMAL|FILE_FLAG_POSIX_SEMANTICS;
|
|
|
|
fd=CreateFileW(wpath, 0, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flag, NULL);
|
|
if (unlikely_log(fd==INVALID_HANDLE_VALUE)){
|
|
if (GetLastError()==ERROR_SHARING_VIOLATION){
|
|
debug(D_WARNING, "file %s is locked by another process, will retry after sleep", path);
|
|
psync_milisleep(PSYNC_SLEEP_ON_OS_LOCK);
|
|
goto retry;
|
|
}
|
|
else
|
|
debug(D_NOTICE, "could not open file %s, error %d", path, (int)GetLastError());
|
|
psync_free(wpath);
|
|
return -1;
|
|
}
|
|
psync_free(wpath);
|
|
ret=GetFileInformationByHandle(fd, st);
|
|
CloseHandle(fd);
|
|
return psync_bool_to_zero(ret);
|
|
}
|
|
#endif
|
|
|
|
void psync_compat_init(){
|
|
#if defined(P_OS_POSIX)
|
|
struct rlimit limit;
|
|
limit.rlim_cur=limit.rlim_max=2048;
|
|
if (setrlimit(RLIMIT_NOFILE, &limit))
|
|
debug(D_ERROR, "setrlimit failed errno=%d", errno);
|
|
#if IS_DEBUG
|
|
if (getrlimit(RLIMIT_CORE, &limit))
|
|
debug(D_ERROR, "getrlimit failed errno=%d", errno);
|
|
else{
|
|
limit.rlim_cur=limit.rlim_max;
|
|
if (setrlimit(RLIMIT_CORE, &limit))
|
|
debug(D_ERROR, "setrlimit failed errno=%d", errno);
|
|
}
|
|
#endif
|
|
signal(SIGPIPE, SIG_IGN);
|
|
psync_uid=getuid();
|
|
psync_gid=getgid();
|
|
psync_gids_cnt=getgroups(0, NULL);
|
|
psync_gids=psync_new_cnt(gid_t, psync_gids_cnt);
|
|
if (unlikely_log(getgroups(psync_gids_cnt, psync_gids)!=psync_gids_cnt))
|
|
psync_gids_cnt=0;
|
|
#if defined(PAGESIZE)
|
|
psync_page_size=PAGESIZE;
|
|
#else
|
|
psync_page_size=sysconf(_SC_PAGESIZE);
|
|
#endif
|
|
#elif defined(P_OS_WINDOWS)
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo(&si);
|
|
psync_page_size=si.dwPageSize;
|
|
#else
|
|
psync_page_size=-1;
|
|
#endif
|
|
debug(D_NOTICE, "detected page size %d", psync_page_size);
|
|
}
|
|
|
|
int psync_stat_mode_ok(psync_stat_t *buf, unsigned int bits){
|
|
#if defined(P_OS_POSIX)
|
|
int i;
|
|
if (psync_uid==0)
|
|
return 1;
|
|
if (buf->st_uid==psync_uid){
|
|
bits<<=6;
|
|
return (buf->st_mode&bits)==bits;
|
|
}
|
|
if (buf->st_gid==psync_gid){
|
|
bits<<=3;
|
|
return (buf->st_mode&bits)==bits;
|
|
}
|
|
for (i=0; i<psync_gids_cnt; i++)
|
|
if (buf->st_gid==psync_gids[i]){
|
|
bits<<=3;
|
|
return (buf->st_mode&bits)==bits;
|
|
}
|
|
return (buf->st_mode&bits)==bits;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
char *psync_get_default_database_path_old(){
|
|
#if defined(P_OS_POSIX)
|
|
struct stat st;
|
|
const char *dir;
|
|
dir=getenv("HOME");
|
|
if (unlikely_log(!dir) || unlikely_log(stat(dir, &st)) || unlikely_log(!psync_stat_mode_ok(&st, 7))){
|
|
struct passwd pwd;
|
|
struct passwd *result;
|
|
char buff[4096];
|
|
if (unlikely_log(getpwuid_r(getuid(), &pwd, buff, sizeof(buff), &result)) || unlikely_log(stat(result->pw_dir, &st)) ||
|
|
unlikely_log(!psync_stat_mode_ok(&st, 7)))
|
|
return NULL;
|
|
dir=result->pw_dir;
|
|
}
|
|
return psync_strcat(dir, PSYNC_DIRECTORY_SEPARATOR, PSYNC_DEFAULT_POSIX_DBNAME, NULL);
|
|
#elif defined(P_OS_WINDOWS)
|
|
const char *dir;
|
|
dir=getenv("UserProfile");
|
|
if (unlikely_log(!dir))
|
|
return NULL;
|
|
return psync_strcat(dir, PSYNC_DIRECTORY_SEPARATOR, PSYNC_DEFAULT_WINDOWS_DBNAME, NULL);
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
static char *psync_get_pcloud_path_nc(){
|
|
#if defined(P_OS_POSIX)
|
|
struct stat st;
|
|
const char *dir;
|
|
dir=getenv("HOME");
|
|
if (unlikely_log(!dir) || unlikely_log(stat(dir, &st)) || unlikely_log(!psync_stat_mode_ok(&st, 7))){
|
|
struct passwd pwd;
|
|
struct passwd *result;
|
|
char buff[4096];
|
|
if (unlikely_log(getpwuid_r(getuid(), &pwd, buff, sizeof(buff), &result)) || unlikely_log(stat(result->pw_dir, &st)) ||
|
|
unlikely_log(!psync_stat_mode_ok(&st, 7)))
|
|
return NULL;
|
|
dir=result->pw_dir;
|
|
}
|
|
return psync_strcat(dir, PSYNC_DIRECTORY_SEPARATOR, PSYNC_DEFAULT_POSIX_DIR, NULL);
|
|
#elif defined(P_OS_WINDOWS)
|
|
wchar_t path[MAX_PATH], *wdir;
|
|
char *dir, *ret;
|
|
if (SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, path)==S_OK)
|
|
wdir=path;
|
|
else{
|
|
wdir=_wgetenv(L"LOCALAPPDATA");
|
|
if (!wdir){
|
|
wdir=_wgetenv(L"APPDATA");
|
|
if (unlikely_log(!wdir))
|
|
return NULL;
|
|
}
|
|
}
|
|
dir=wchar_to_utf8(wdir);
|
|
ret=psync_strcat(dir, PSYNC_DIRECTORY_SEPARATOR, PSYNC_DEFAULT_WINDOWS_DIR, NULL);
|
|
psync_free(dir);
|
|
return ret;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
char *psync_get_pcloud_path(){
|
|
char *path;
|
|
psync_stat_t st;
|
|
path=psync_get_pcloud_path_nc();
|
|
if (unlikely_log(!path))
|
|
return NULL;
|
|
if (psync_stat(path, &st) && unlikely_log(psync_mkdir(path))){
|
|
psync_free(path);
|
|
return NULL;
|
|
}
|
|
return path;
|
|
}
|
|
|
|
char *psync_get_private_dir(char *name){
|
|
char *path, *rpath;
|
|
psync_stat_t st;
|
|
path=psync_get_pcloud_path();
|
|
if (!path)
|
|
return NULL;
|
|
rpath=psync_strcat(path, PSYNC_DIRECTORY_SEPARATOR, name, NULL);
|
|
free(path);
|
|
if (psync_stat(rpath, &st) && psync_mkdir(rpath)){
|
|
psync_free(rpath);
|
|
return NULL;
|
|
}
|
|
return rpath;
|
|
}
|
|
|
|
char *psync_get_private_tmp_dir(){
|
|
return psync_get_private_dir(PSYNC_DEFAULT_TMP_DIR);
|
|
}
|
|
|
|
char *psync_get_default_database_path(){
|
|
char *dirpath, *path;
|
|
psync_stat_t st;
|
|
dirpath=psync_get_pcloud_path();
|
|
if (!dirpath)
|
|
return NULL;
|
|
path=psync_strcat(dirpath, PSYNC_DIRECTORY_SEPARATOR, PSYNC_DEFAULT_DB_NAME, NULL);
|
|
psync_free(dirpath);
|
|
if (psync_stat(path, &st) && (dirpath=psync_get_default_database_path_old())){
|
|
if (!psync_stat(dirpath, &st)){
|
|
if (psync_sql_reopen(dirpath)){
|
|
psync_free(path);
|
|
return dirpath;
|
|
}
|
|
else
|
|
psync_file_rename(dirpath, path);
|
|
}
|
|
psync_free(dirpath);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
char *psync_get_home_dir(){
|
|
#if defined(P_OS_POSIX)
|
|
struct stat st;
|
|
const char *dir;
|
|
dir=getenv("HOME");
|
|
if (unlikely_log(!dir) || unlikely_log(stat(dir, &st)) || unlikely_log(!psync_stat_mode_ok(&st, 7))){
|
|
struct passwd pwd;
|
|
struct passwd *result;
|
|
char buff[4096];
|
|
if (unlikely_log(getpwuid_r(getuid(), &pwd, buff, sizeof(buff), &result)) || unlikely_log(stat(result->pw_dir, &st)) ||
|
|
unlikely_log(!psync_stat_mode_ok(&st, 7)))
|
|
return NULL;
|
|
dir=result->pw_dir;
|
|
}
|
|
return psync_strdup(dir);
|
|
#elif defined(P_OS_WINDOWS)
|
|
const char *dir;
|
|
dir=getenv("UserProfile");
|
|
if (unlikely_log(!dir))
|
|
return NULL;
|
|
return psync_strdup(dir);
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
void psync_yield_cpu(){
|
|
#if defined(_POSIX_PRIORITY_SCHEDULING)
|
|
sched_yield();
|
|
#elif defined(P_OS_WINDOWS)
|
|
SwitchToThread();
|
|
#else
|
|
psync_milisleep(1);
|
|
#endif
|
|
}
|
|
|
|
static void thread_started(){
|
|
//debug(D_NOTICE, "thread started"); //This repeats too many times because of the overlays
|
|
}
|
|
|
|
static void thread_exited(){
|
|
// debug(D_NOTICE, "thread exited"); //This repeats too many times because of the overlays
|
|
}
|
|
|
|
static void *thread_entry0(void *data){
|
|
psync_thread_start0 run;
|
|
run=((psync_run_data0 *)data)->run;
|
|
psync_thread_name=((psync_run_data0 *)data)->name;
|
|
psync_free(data);
|
|
thread_started();
|
|
run();
|
|
thread_exited();
|
|
return NULL;
|
|
}
|
|
|
|
void psync_run_thread(const char *name, psync_thread_start0 run){
|
|
psync_run_data0 *data;
|
|
pthread_t thread;
|
|
pthread_attr_t attr;
|
|
data=psync_new(psync_run_data0);
|
|
data->run=run;
|
|
data->name=name;
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
pthread_attr_setstacksize(&attr, PSYNC_STACK_SIZE);
|
|
pthread_create(&thread, &attr, thread_entry0, data);
|
|
pthread_attr_destroy(&attr);
|
|
}
|
|
|
|
static void *thread_entry1(void *data){
|
|
psync_thread_start1 run;
|
|
void *ptr;
|
|
run=((psync_run_data1 *)data)->run;
|
|
ptr=((psync_run_data1 *)data)->ptr;
|
|
psync_thread_name=((psync_run_data1 *)data)->name;
|
|
psync_free(data);
|
|
thread_started();
|
|
run(ptr);
|
|
thread_exited();
|
|
return NULL;
|
|
}
|
|
|
|
void psync_run_thread1(const char *name, psync_thread_start1 run, void *ptr){
|
|
psync_run_data1 *data;
|
|
pthread_t thread;
|
|
pthread_attr_t attr;
|
|
data=psync_new(psync_run_data1);
|
|
data->run=run;
|
|
data->ptr=ptr;
|
|
data->name=name;
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
pthread_attr_setstacksize(&attr, PSYNC_STACK_SIZE);
|
|
pthread_create(&thread, &attr, thread_entry1, data);
|
|
pthread_attr_destroy(&attr);
|
|
}
|
|
|
|
static void psync_check_no_sql_lock(uint64_t millisec){
|
|
#if IS_DEBUG
|
|
if (psync_sql_islocked()){
|
|
debug(D_CRITICAL, "trying to sleep while holding sql lock, aborting");
|
|
psync_sql_dump_locks();
|
|
abort();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void psync_milisleep_nosqlcheck(uint64_t millisec){
|
|
#if defined(P_OS_POSIX)
|
|
struct timespec tm;
|
|
tm.tv_sec=millisec/1000;
|
|
tm.tv_nsec=(millisec%1000)*1000000;
|
|
nanosleep(&tm, NULL);
|
|
#elif defined(P_OS_WINDOWS)
|
|
Sleep(millisec);
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
void psync_milisleep(uint64_t millisec){
|
|
psync_check_no_sql_lock(millisec);
|
|
psync_milisleep_nosqlcheck(millisec);
|
|
}
|
|
|
|
time_t psync_time(){
|
|
#if defined(P_OS_MACOSX)
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
return tv.tv_sec;
|
|
#elif defined(_POSIX_TIMERS) && _POSIX_TIMERS>0
|
|
struct timespec ts;
|
|
if (likely_log(clock_gettime(CLOCK_REALTIME, &ts)==0))
|
|
return ts.tv_sec;
|
|
else
|
|
return time(NULL);
|
|
#else
|
|
return time(NULL);
|
|
#endif
|
|
}
|
|
|
|
void psync_nanotime(struct timespec *tm){
|
|
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS>0
|
|
clock_gettime(CLOCK_REALTIME, tm);
|
|
#elif defined(P_OS_WINDOWS)
|
|
FILETIME ft;
|
|
uint64_t t;
|
|
GetSystemTimeAsFileTime(&ft);
|
|
t=psync_32to64(ft.dwHighDateTime, ft.dwLowDateTime)-116444736000000000ULL;
|
|
tm->tv_sec=t/10000000UL;
|
|
tm->tv_nsec=(t%10000000UL)*100;
|
|
#elif defined(P_OS_POSIX)
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
tm->tv_sec=tv.tv_sec;
|
|
tm->tv_nsec=tv.tv_usec*1000;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
uint64_t psync_millitime(){
|
|
struct timespec tm;
|
|
psync_nanotime(&tm);
|
|
return tm.tv_sec*1000+tm.tv_nsec/1000000;
|
|
}
|
|
|
|
#if defined(P_OS_POSIX)
|
|
static void psync_add_file_to_seed(const char *fn, psync_lhash_ctx *hctx, size_t max){
|
|
char buff[4096];
|
|
ssize_t rd;
|
|
int fd, mode;
|
|
mode=O_RDONLY;
|
|
#if defined(O_NONBLOCK)
|
|
mode+=O_NONBLOCK;
|
|
#elif defined(O_NDELAY)
|
|
mode+=O_NDELAY;
|
|
#endif
|
|
fd=open(fn, mode);
|
|
if (fd!=-1){
|
|
if (!max || max>sizeof(buff))
|
|
max=sizeof(buff);
|
|
rd=read(fd, buff, max);
|
|
if (rd>0)
|
|
psync_lhash_update(hctx, buff, rd);
|
|
close(fd);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(P_OS_LINUX)
|
|
static void psync_get_random_seed_linux(psync_lhash_ctx *hctx){
|
|
struct sysinfo si;
|
|
if (likely_log(!sysinfo(&si)))
|
|
psync_lhash_update(hctx, &si, sizeof(si));
|
|
psync_add_file_to_seed("/proc/stat", hctx, 0);
|
|
psync_add_file_to_seed("/proc/vmstat", hctx, 0);
|
|
psync_add_file_to_seed("/proc/meminfo", hctx, 0);
|
|
psync_add_file_to_seed("/proc/modules", hctx, 0);
|
|
psync_add_file_to_seed("/proc/mounts", hctx, 0);
|
|
psync_add_file_to_seed("/proc/diskstats", hctx, 0);
|
|
psync_add_file_to_seed("/proc/interrupts", hctx, 0);
|
|
psync_add_file_to_seed("/proc/net/dev", hctx, 0);
|
|
psync_add_file_to_seed("/proc/net/arp", hctx, 0);
|
|
}
|
|
#endif
|
|
|
|
static void psync_get_random_seed_from_query(psync_lhash_ctx *hctx, psync_sql_res *res){
|
|
psync_variant_row row;
|
|
struct timespec tm;
|
|
int i;
|
|
while ((row=psync_sql_fetch_row(res))){
|
|
for (i=0; i<res->column_count; i++)
|
|
if (row[i].type==PSYNC_TSTRING)
|
|
psync_lhash_update(hctx, row[i].str, row[i].length);
|
|
psync_lhash_update(hctx, row, sizeof(psync_variant)*res->column_count);
|
|
}
|
|
psync_sql_free_result(res);
|
|
psync_nanotime(&tm);
|
|
psync_lhash_update(hctx, &tm, sizeof(&tm));
|
|
}
|
|
|
|
static void psync_get_random_seed_from_db(psync_lhash_ctx *hctx){
|
|
psync_sql_res *res;
|
|
struct timespec tm;
|
|
unsigned char rnd[PSYNC_LHASH_DIGEST_LEN];
|
|
psync_nanotime(&tm);
|
|
psync_lhash_update(hctx, &tm, sizeof(&tm));
|
|
res=psync_sql_query_rdlock("SELECT * FROM setting ORDER BY RANDOM()");
|
|
psync_get_random_seed_from_query(hctx, res);
|
|
res=psync_sql_query_rdlock("SELECT * FROM resolver ORDER BY RANDOM() LIMIT 50");
|
|
psync_get_random_seed_from_query(hctx, res);
|
|
/* res=psync_sql_query_rdlock("SELECT * FROM filerevision ORDER BY RANDOM() LIMIT 50");
|
|
psync_get_random_seed_from_query(hctx, res);
|
|
res=psync_sql_query_rdlock("SELECT * FROM file ORDER BY RANDOM() LIMIT 50");
|
|
psync_get_random_seed_from_query(hctx, res);
|
|
res=psync_sql_query_rdlock("SELECT * FROM localfile ORDER BY RANDOM() LIMIT 50");
|
|
psync_get_random_seed_from_query(hctx, res);
|
|
res=psync_sql_query_rdlock("SELECT * FROM folder ORDER BY RANDOM() LIMIT 25");
|
|
psync_get_random_seed_from_query(hctx, res);
|
|
res=psync_sql_query_rdlock("SELECT * FROM localfolder ORDER BY RANDOM() LIMIT 25");
|
|
psync_get_random_seed_from_query(hctx, res);
|
|
res=psync_sql_query_rdlock("SELECT * FROM hashchecksum ORDER BY RANDOM() LIMIT 25");
|
|
psync_get_random_seed_from_query(hctx, res);
|
|
res=psync_sql_query_rdlock("SELECT * FROM pagecache WHERE type=1 AND rowid>(ABS(RANDOM())%(SELECT MAX(rowid)+1 FROM pagecache)) ORDER BY rowid LIMIT 50");
|
|
psync_get_random_seed_from_query(hctx, res); */
|
|
psync_sql_statement("REPLACE INTO setting (id, value) VALUES ('random', RANDOM())");
|
|
psync_nanotime(&tm);
|
|
psync_lhash_update(hctx, &tm, sizeof(&tm));
|
|
psync_sql_sync();
|
|
psync_nanotime(&tm);
|
|
psync_lhash_update(hctx, &tm, sizeof(&tm));
|
|
sqlite3_randomness(sizeof(rnd), rnd);
|
|
psync_lhash_update(hctx, rnd, sizeof(rnd));
|
|
}
|
|
|
|
static void psync_rehash_cnt(unsigned char *hashbin, psync_uint_t cnt){
|
|
psync_lhash_ctx hctx;
|
|
psync_uint_t i;
|
|
struct timespec tm;
|
|
for (i=0; i<cnt; i++){
|
|
psync_lhash_init(&hctx);
|
|
if ((i&511)==0){
|
|
psync_nanotime(&tm);
|
|
psync_lhash_update(&hctx, &tm, sizeof(&tm));
|
|
}
|
|
else
|
|
psync_lhash_update(&hctx, &i, sizeof(i));
|
|
psync_lhash_update(&hctx, hashbin, PSYNC_LHASH_DIGEST_LEN);
|
|
psync_lhash_final(hashbin, &hctx);
|
|
}
|
|
}
|
|
|
|
static void psync_store_seed_in_db(const unsigned char *seed){
|
|
psync_sql_res *res;
|
|
unsigned char hashbin[PSYNC_LHASH_DIGEST_LEN];
|
|
char hashhex[PSYNC_LHASH_DIGEST_HEXLEN], nm[16];
|
|
memcpy(hashbin, seed, PSYNC_LHASH_DIGEST_LEN);
|
|
psync_rehash_cnt(hashbin, 2000);
|
|
psync_binhex(hashhex, hashbin, PSYNC_LHASH_DIGEST_LEN);
|
|
res=psync_sql_prep_statement("REPLACE INTO setting (id, value) VALUES ('randomhash', ?)");
|
|
psync_sql_bind_lstring(res, 1, hashhex, PSYNC_LHASH_DIGEST_HEXLEN);
|
|
psync_sql_run_free(res);
|
|
psync_rehash_cnt(hashbin, 2000);
|
|
psync_binhex(hashhex, hashbin, PSYNC_LHASH_DIGEST_LEN);
|
|
memcpy(nm, "randomhash", 10);
|
|
nm[10]=hashhex[0];
|
|
nm[11]=0;
|
|
res=psync_sql_prep_statement("REPLACE INTO setting (id, value) VALUES (?, ?)");
|
|
psync_sql_bind_lstring(res, 1, nm, 11);
|
|
psync_sql_bind_lstring(res, 2, hashhex, PSYNC_LHASH_DIGEST_HEXLEN);
|
|
psync_sql_run_free(res);
|
|
}
|
|
|
|
void psync_get_random_seed(unsigned char *seed, const void *addent, size_t aelen, int fast){
|
|
static unsigned char lastseed[PSYNC_LHASH_DIGEST_LEN];
|
|
psync_lhash_ctx hctx;
|
|
struct timespec tm;
|
|
psync_stat_t st;
|
|
char *home;
|
|
void *ptr;
|
|
psync_uint_t i, j;
|
|
int64_t i64;
|
|
pthread_t threadid;
|
|
unsigned char lsc[64][PSYNC_LHASH_DIGEST_LEN];
|
|
#if defined(P_OS_POSIX)
|
|
debug(D_NOTICE, "in");
|
|
struct utsname un;
|
|
struct statvfs stfs;
|
|
char **env;
|
|
pid_t pid;
|
|
psync_nanotime(&tm);
|
|
psync_lhash_init(&hctx);
|
|
psync_lhash_update(&hctx, &tm, sizeof(tm));
|
|
if (likely_log(!uname(&un)))
|
|
psync_lhash_update(&hctx, &un, sizeof(un));
|
|
pid=getpid();
|
|
psync_lhash_update(&hctx, &pid, sizeof(pid));
|
|
if (!statvfs("/", &stfs))
|
|
psync_lhash_update(&hctx, &stfs, sizeof(stfs));
|
|
for (env=environ; *env!=NULL; env++)
|
|
psync_lhash_update(&hctx, *env, strlen(*env));
|
|
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK)
|
|
if (likely_log(!clock_gettime(CLOCK_MONOTONIC, &tm)))
|
|
psync_lhash_update(&hctx, &tm, sizeof(tm));
|
|
#endif
|
|
#if defined(P_OS_LINUX)
|
|
psync_add_file_to_seed("/dev/urandom", &hctx, PSYNC_HASH_DIGEST_LEN);
|
|
#else
|
|
psync_add_file_to_seed("/dev/random", &hctx, PSYNC_HASH_DIGEST_LEN);
|
|
#endif
|
|
#elif defined(P_OS_WINDOWS)
|
|
SYSTEM_INFO si;
|
|
OSVERSIONINFO osvi;
|
|
CURSORINFO ci;
|
|
PROCESSENTRY32 pe;
|
|
THREADENTRY32 te;
|
|
MODULEENTRY32 me;
|
|
MEMORYSTATUSEX ms;
|
|
LARGE_INTEGER li;
|
|
TCHAR ib[1024];
|
|
DWORD ibc;
|
|
HCRYPTPROV cprov;
|
|
HANDLE pr;
|
|
debug(D_NOTICE, "in");
|
|
psync_nanotime(&tm);
|
|
psync_lhash_init(&hctx);
|
|
psync_lhash_update(&hctx, &tm, sizeof(tm));
|
|
if (CryptAcquireContext(&cprov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)){
|
|
if (CryptGenRandom(cprov, PSYNC_HASH_DIGEST_LEN, lsc[0]))
|
|
psync_lhash_update(&hctx, lsc[0], PSYNC_HASH_DIGEST_LEN);
|
|
CryptReleaseContext(cprov, 0);
|
|
}
|
|
if ((pr=CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0))!=INVALID_HANDLE_VALUE){
|
|
pe.dwSize=sizeof(pe);
|
|
if (Process32First(pr, &pe))
|
|
do {
|
|
psync_lhash_update(&hctx, &pe, sizeof(pe));
|
|
} while(Process32Next(pr, &pe));
|
|
te.dwSize=sizeof(te);
|
|
if (Thread32First(pr, &te))
|
|
do {
|
|
psync_lhash_update(&hctx, &te, sizeof(te));
|
|
} while(Thread32Next(pr, &te));
|
|
me.dwSize=sizeof(me);
|
|
if (Module32First(pr, &me))
|
|
do {
|
|
psync_lhash_update(&hctx, &me, sizeof(me));
|
|
} while(Module32Next(pr, &me));
|
|
CloseHandle(pr);
|
|
}
|
|
ms.dwLength=sizeof(ms);
|
|
if (GlobalMemoryStatusEx(&ms))
|
|
psync_lhash_update(&hctx, &ms, sizeof(ms));
|
|
ci.cbSize=sizeof(ci);
|
|
if (GetCursorInfo(&ci))
|
|
psync_lhash_update(&hctx, &ci, sizeof(ci));
|
|
GetSystemInfo(&si);
|
|
psync_lhash_update(&hctx, &si, sizeof(si));
|
|
ibc=ARRAY_SIZE(ib);
|
|
if (GetComputerName(ib, &ibc))
|
|
psync_lhash_update(&hctx, ib, sizeof(TCHAR)*ibc);
|
|
ibc=ARRAY_SIZE(ib);
|
|
if (GetUserName(ib, &ibc))
|
|
psync_lhash_update(&hctx, ib, sizeof(TCHAR)*ibc);
|
|
memset(&osvi, 0, sizeof(OSVERSIONINFO));
|
|
osvi.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
|
|
if (GetVersionEx(&osvi))
|
|
psync_lhash_update(&hctx, &osvi, sizeof(osvi));
|
|
ibc=GetCurrentProcessId();
|
|
psync_lhash_update(&hctx, &ibc, sizeof(ibc));
|
|
ibc=GetTickCount();
|
|
psync_lhash_update(&hctx, &ibc, sizeof(ibc));
|
|
if (QueryPerformanceCounter(&li))
|
|
psync_lhash_update(&hctx, &li, sizeof(li));
|
|
#endif
|
|
#if defined(P_OS_LINUX)
|
|
psync_get_random_seed_linux(&hctx);
|
|
#endif
|
|
threadid=pthread_self();
|
|
psync_lhash_update(&hctx, &threadid, sizeof(threadid));
|
|
ptr=(void *)&ptr;
|
|
psync_lhash_update(&hctx, &ptr, sizeof(ptr));
|
|
ptr=(void *)psync_get_random_seed;
|
|
psync_lhash_update(&hctx, &ptr, sizeof(ptr));
|
|
ptr=(void *)pthread_self;
|
|
psync_lhash_update(&hctx, &ptr, sizeof(ptr));
|
|
ptr=(void *)malloc;
|
|
psync_lhash_update(&hctx, &ptr, sizeof(ptr));
|
|
ptr=(void *)&lastseed;
|
|
psync_lhash_update(&hctx, &ptr, sizeof(ptr));
|
|
home=psync_get_home_dir();
|
|
if (home){
|
|
i64=psync_get_free_space_by_path(home);
|
|
psync_lhash_update(&hctx, &i64, sizeof(i64));
|
|
psync_lhash_update(&hctx, home, strlen(home));
|
|
if (likely_log(!psync_stat(home, &st)))
|
|
psync_lhash_update(&hctx, &st, sizeof(st));
|
|
psync_free(home);
|
|
}
|
|
if (!fast){
|
|
debug(D_NOTICE, "db in");
|
|
psync_get_random_seed_from_db(&hctx);
|
|
debug(D_NOTICE, "db out");
|
|
}
|
|
if (aelen)
|
|
psync_lhash_update(&hctx, addent, aelen);
|
|
debug(D_NOTICE, "adding bulk data");
|
|
for (i=0; i<ARRAY_SIZE(lsc); i++){
|
|
memcpy(&lsc[i], lastseed, PSYNC_LHASH_DIGEST_LEN);
|
|
for (j=0; j<PSYNC_LHASH_DIGEST_LEN; j++)
|
|
lsc[i][j]^=(unsigned char)i;
|
|
}
|
|
for (j=fast?3:0; j<5; j++){
|
|
for (i=0; i<100; i++){
|
|
psync_lhash_update(&hctx, &i, sizeof(i));
|
|
psync_lhash_update(&hctx, &j, sizeof(j));
|
|
psync_lhash_update(&hctx, lsc, sizeof(lsc));
|
|
}
|
|
psync_nanotime(&tm);
|
|
psync_lhash_update(&hctx, &tm, sizeof(&tm));
|
|
}
|
|
psync_lhash_final(seed, &hctx);
|
|
memcpy(lastseed, seed, PSYNC_LHASH_DIGEST_LEN);
|
|
debug(D_NOTICE, "storing in db");
|
|
psync_store_seed_in_db(seed);
|
|
debug(D_NOTICE, "out");
|
|
}
|
|
|
|
static int psync_wait_socket_writable_microsec(psync_socket_t sock, long sec, long usec){
|
|
fd_set wfds;
|
|
struct timeval tv;
|
|
int res;
|
|
tv.tv_sec=sec;
|
|
tv.tv_usec=usec;
|
|
FD_ZERO(&wfds);
|
|
FD_SET(sock, &wfds);
|
|
res=select(sock+1, NULL, &wfds, NULL, &tv);
|
|
if (res==1)
|
|
return 0;
|
|
if (res==0)
|
|
psync_sock_set_err(P_TIMEDOUT);
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
#define psync_wait_socket_writable(sock, sec) psync_wait_socket_writable_microsec(sock, sec, 0)
|
|
|
|
int psync_wait_socket_write_timeout(psync_socket_t sock){
|
|
return psync_wait_socket_writable(sock, PSYNC_SOCK_WRITE_TIMEOUT);
|
|
}
|
|
|
|
static int psync_wait_socket_readable_microsec(psync_socket_t sock, long sec, long usec){
|
|
fd_set rfds;
|
|
struct timeval tv;
|
|
#if IS_DEBUG
|
|
struct timespec start, end;
|
|
unsigned long msec;
|
|
#endif
|
|
int res;
|
|
tv.tv_sec=sec;
|
|
tv.tv_usec=usec;
|
|
FD_ZERO(&rfds);
|
|
FD_SET(sock, &rfds);
|
|
#if IS_DEBUG
|
|
psync_nanotime(&start);
|
|
#endif
|
|
res=select(sock+1, &rfds, NULL, NULL, &tv);
|
|
|
|
if (res==1){
|
|
#if IS_DEBUG
|
|
psync_nanotime(&end);
|
|
msec=(end.tv_sec-start.tv_sec)*1000+end.tv_nsec/1000000-start.tv_nsec/1000000;
|
|
if (msec>=30000)
|
|
debug(D_WARNING, "got response from socket after %lu milliseconds", msec);
|
|
else if (msec>=5000)
|
|
debug(D_NOTICE, "got response from socket after %lu milliseconds", msec);
|
|
#endif
|
|
return 0;
|
|
}
|
|
if (res==0){
|
|
if (sec)
|
|
debug(D_WARNING, "socket read timeouted on %ld seconds", sec);
|
|
psync_sock_set_err(P_TIMEDOUT);
|
|
}
|
|
else
|
|
debug(D_WARNING, "select returned %d", res);
|
|
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
#define psync_wait_socket_readable(sock, sec) psync_wait_socket_readable_microsec(sock, sec, 0)
|
|
|
|
int psync_wait_socket_read_timeout(psync_socket_t sock){
|
|
return psync_wait_socket_readable(sock, PSYNC_SOCK_READ_TIMEOUT);
|
|
}
|
|
|
|
static psync_socket_t connect_res(struct addrinfo *res){
|
|
psync_socket_t sock;
|
|
#if defined(SOCK_NONBLOCK)
|
|
#if defined(SOCK_CLOEXEC)
|
|
#define PSOCK_TYPE_OR (SOCK_NONBLOCK|SOCK_CLOEXEC)
|
|
#else
|
|
#define PSOCK_TYPE_OR SOCK_NONBLOCK
|
|
#endif
|
|
#else
|
|
#define PSOCK_TYPE_OR 0
|
|
#define PSOCK_NEED_NOBLOCK
|
|
#endif
|
|
while (res){
|
|
sock=socket(res->ai_family, res->ai_socktype|PSOCK_TYPE_OR, res->ai_protocol);
|
|
#if defined(P_OS_WINDOWS)
|
|
if (unlikely(sock==INVALID_SOCKET && WSAGetLastError()==WSANOTINITIALISED)){
|
|
WSADATA wsaData;
|
|
if (!WSAStartup(MAKEWORD(2, 2), &wsaData))
|
|
sock=socket(res->ai_family, res->ai_socktype|PSOCK_TYPE_OR, res->ai_protocol);
|
|
}
|
|
#endif
|
|
if (likely_log(sock!=INVALID_SOCKET)){
|
|
#if defined(PSOCK_NEED_NOBLOCK)
|
|
#if defined(P_OS_WINDOWS)
|
|
static const unsigned long mode=1;
|
|
static int need_snd_buf=0;
|
|
ioctlsocket(sock, FIONBIO, &mode);
|
|
if (need_snd_buf==0){
|
|
unsigned ver=GetVersion();
|
|
ver=LOBYTE(LOWORD(ver))*10+HIBYTE(LOWORD(ver));
|
|
if (ver<=61){
|
|
need_snd_buf=1;
|
|
debug(D_NOTICE, "detected windows %u, setting socket buffers", ver);
|
|
}
|
|
else{
|
|
need_snd_buf=-1;
|
|
debug(D_NOTICE, "detected windows %u, not setting socket buffers", ver);
|
|
}
|
|
}
|
|
if (need_snd_buf==1){
|
|
int bufsize=PSYNC_SOCK_WIN_SNDBUF;
|
|
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize, sizeof(bufsize));
|
|
}
|
|
#elif defined(P_OS_POSIX)
|
|
fcntl(sock, F_SETFD, FD_CLOEXEC);
|
|
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL)|O_NONBLOCK);
|
|
#else
|
|
#error "Need to set non-blocking for your OS"
|
|
#endif
|
|
#endif
|
|
if ((connect(sock, res->ai_addr, res->ai_addrlen)!=SOCKET_ERROR) ||
|
|
(psync_sock_err()==P_INPROGRESS && !psync_wait_socket_writable(sock, PSYNC_SOCK_CONNECT_TIMEOUT)))
|
|
return sock;
|
|
psync_close_socket(sock);
|
|
}
|
|
res=res->ai_next;
|
|
}
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
psync_socket_t psync_create_socket(int domain, int type, int protocol){
|
|
psync_socket_t ret;
|
|
ret=socket(domain, type, protocol);
|
|
#if defined(P_OS_WINDOWS)
|
|
if (unlikely(ret==INVALID_SOCKET && WSAGetLastError()==WSANOTINITIALISED)){
|
|
WSADATA wsaData;
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
|
|
return INVALID_SOCKET;
|
|
ret=socket(domain, type, protocol);
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static void addr_save_to_db(const char *host, const char *port, struct addrinfo *addr){
|
|
psync_sql_res *res;
|
|
uint64_t id;
|
|
if (psync_sql_isrdlocked()){
|
|
if (psync_sql_tryupgradelock())
|
|
return;
|
|
else
|
|
debug(D_NOTICE, "upgraded read to write lock to save data to DB");
|
|
}
|
|
psync_sql_start_transaction();
|
|
res=psync_sql_prep_statement("DELETE FROM resolver WHERE hostname=? AND port=?");
|
|
psync_sql_bind_string(res, 1, host);
|
|
psync_sql_bind_string(res, 2, port);
|
|
psync_sql_run_free(res);
|
|
res=psync_sql_prep_statement("INSERT INTO resolver (hostname, port, prio, created, family, socktype, protocol, data) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
psync_sql_bind_string(res, 1, host);
|
|
psync_sql_bind_string(res, 2, port);
|
|
psync_sql_bind_uint(res, 4, psync_timer_time());
|
|
id=0;
|
|
do {
|
|
psync_sql_bind_uint(res, 3, id++);
|
|
psync_sql_bind_int(res, 5, addr->ai_family);
|
|
psync_sql_bind_int(res, 6, addr->ai_socktype);
|
|
psync_sql_bind_int(res, 7, addr->ai_protocol);
|
|
psync_sql_bind_blob(res, 8, (char *)addr->ai_addr, addr->ai_addrlen);
|
|
psync_sql_run(res);
|
|
addr=addr->ai_next;
|
|
} while (addr);
|
|
psync_sql_free_result(res);
|
|
psync_sql_commit_transaction();
|
|
}
|
|
|
|
static struct addrinfo *addr_load_from_db(const char *host, const char *port){
|
|
psync_sql_res *res;
|
|
psync_uint_row row;
|
|
psync_variant_row vrow;
|
|
struct addrinfo *ret;
|
|
char *data;
|
|
const char *str;
|
|
uint64_t i;
|
|
size_t len;
|
|
psync_sql_rdlock();
|
|
res=psync_sql_query_nolock("SELECT COUNT(*), SUM(LENGTH(data)) FROM resolver WHERE hostname=? AND port=?");
|
|
psync_sql_bind_string(res, 1, host);
|
|
psync_sql_bind_string(res, 2, port);
|
|
if (!(row=psync_sql_fetch_rowint(res)) || row[0]==0){
|
|
psync_sql_free_result(res);
|
|
psync_sql_rdunlock();
|
|
return NULL;
|
|
}
|
|
ret=(struct addrinfo *)psync_malloc(sizeof(struct addrinfo)*row[0]+row[1]);
|
|
data=(char *)(ret+row[0]);
|
|
for (i=0; i<row[0]-1; i++)
|
|
ret[i].ai_next=&ret[i+1];
|
|
ret[i].ai_next=NULL;
|
|
psync_sql_free_result(res);
|
|
res=psync_sql_query_nolock("SELECT family, socktype, protocol, data FROM resolver WHERE hostname=? AND port=? ORDER BY prio");
|
|
psync_sql_bind_string(res, 1, host);
|
|
psync_sql_bind_string(res, 2, port);
|
|
i=0;
|
|
while ((vrow=psync_sql_fetch_row(res))){
|
|
ret[i].ai_family=psync_get_snumber(vrow[0]);
|
|
ret[i].ai_socktype=psync_get_snumber(vrow[1]);
|
|
ret[i].ai_protocol=psync_get_snumber(vrow[2]);
|
|
str=psync_get_lstring(vrow[3], &len);
|
|
ret[i].ai_addr=(struct sockaddr *)data;
|
|
ret[i].ai_addrlen=len;
|
|
i++;
|
|
memcpy(data, str, len);
|
|
data+=len;
|
|
}
|
|
psync_sql_free_result(res);
|
|
psync_sql_rdunlock();
|
|
return ret;
|
|
}
|
|
|
|
static int addr_still_valid(struct addrinfo *olda, struct addrinfo *newa){
|
|
struct addrinfo *a;
|
|
do {
|
|
a=newa;
|
|
while (1){
|
|
if (a->ai_addrlen==olda->ai_addrlen && !memcmp(a->ai_addr, olda->ai_addr, a->ai_addrlen))
|
|
break;
|
|
a=a->ai_next;
|
|
if (!a)
|
|
return 0;
|
|
}
|
|
olda=olda->ai_next;
|
|
} while (olda);
|
|
return 1;
|
|
}
|
|
|
|
typedef struct {
|
|
const char *host;
|
|
const char *port;
|
|
} resolve_host_port;
|
|
|
|
static void connect_res_callback(void *h, void *ptr){
|
|
struct addrinfo *res;
|
|
psync_socket_t sock;
|
|
int r;
|
|
res=(struct addrinfo *)ptr;
|
|
sock=connect_res(res);
|
|
r=psync_task_complete(h, (void *)(uintptr_t)sock);
|
|
psync_free(res);
|
|
if (r && sock!=INVALID_SOCKET)
|
|
psync_close_socket(sock);
|
|
}
|
|
|
|
static void resolve_callback(void *h, void *ptr){
|
|
resolve_host_port *hp;
|
|
struct addrinfo *res;
|
|
struct addrinfo hints;
|
|
int rc;
|
|
hp=(resolve_host_port *)ptr;
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family=AF_UNSPEC;
|
|
hints.ai_socktype=SOCK_STREAM;
|
|
res=NULL;
|
|
rc=getaddrinfo(hp->host, hp->port, &hints, &res);
|
|
#if defined(P_OS_WINDOWS)
|
|
if (unlikely(rc==WSANOTINITIALISED)){
|
|
WSADATA wsaData;
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData)){
|
|
psync_task_complete(h, NULL);
|
|
return;
|
|
}
|
|
rc=getaddrinfo(hp->host, hp->port, &hints, &res);
|
|
}
|
|
#endif
|
|
if (unlikely(rc!=0))
|
|
res=NULL;
|
|
psync_task_complete(h, res);
|
|
}
|
|
|
|
#if defined(P_OS_WINDOWS)
|
|
static void gfree_ptr(HGLOBAL ptr){
|
|
if (ptr!=NULL)
|
|
GlobalFree(ptr);
|
|
}
|
|
|
|
static int try_set_proxy(LPWSTR pstr){
|
|
char *str, *c;
|
|
size_t hl, pl;
|
|
if (!pstr)
|
|
return 0;
|
|
str=wchar_to_utf8(pstr);
|
|
c=strchr(str, ':');
|
|
if (!c)
|
|
goto err;
|
|
hl=c-str;
|
|
c++;
|
|
pl=strlen(c);
|
|
if (pl && hl && hl<sizeof(proxy_host) && pl<sizeof(proxy_port)) {
|
|
proxy_host[hl]=0;
|
|
memcpy(proxy_host, str, hl);
|
|
proxy_port[pl]=0;
|
|
memcpy(proxy_port, c, pl);
|
|
psync_free(str);
|
|
proxy_type=PROXY_CONNECT;
|
|
debug(D_NOTICE, "auto detected proxy %s:%s", proxy_host, proxy_port);
|
|
return 1;
|
|
}
|
|
err:
|
|
psync_free(str);
|
|
return 0;
|
|
}
|
|
|
|
#define PSYNC_HAS_PROXY_CODE
|
|
|
|
#endif
|
|
|
|
#if defined(P_OS_MACOSX) && 0
|
|
|
|
#define PSYNC_HAS_PROXY_CODE
|
|
|
|
const void *get_value_cstr(CFDictionaryRef dict, const char *key) {
|
|
CFStringRef str;
|
|
const void *ret;
|
|
str=CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8);
|
|
ret=CFDictionaryGetValue(dict, str);
|
|
CFRelease(str);
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(PSYNC_HAS_PROXY_CODE)
|
|
static int recent_detect(){
|
|
static time_t lastdetect=0;
|
|
if (psync_timer_time()<lastdetect+60)
|
|
return 1;
|
|
else{
|
|
lastdetect=psync_timer_time();
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void detect_proxy(){
|
|
#if defined(P_OS_WINDOWS)
|
|
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieconf;
|
|
WINHTTP_AUTOPROXY_OPTIONS aopt;
|
|
WINHTTP_PROXY_INFO pinfo;
|
|
HINTERNET hi;
|
|
if (recent_detect())
|
|
return;
|
|
proxy_type=PROXY_NONE;
|
|
hi=NULL;
|
|
pinfo.lpszProxy=NULL;
|
|
pinfo.lpszProxyBypass=NULL;
|
|
if (!WinHttpGetIEProxyConfigForCurrentUser(&ieconf))
|
|
return;
|
|
if (ieconf.fAutoDetect){
|
|
hi=WinHttpOpen(L"pCloud", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
|
|
if (!hi){
|
|
debug(D_NOTICE, "WinHttpOpen failed");
|
|
goto manual;
|
|
}
|
|
memset(&aopt, 0, sizeof(aopt));
|
|
aopt.dwFlags=WINHTTP_AUTOPROXY_AUTO_DETECT;
|
|
aopt.dwAutoDetectFlags=WINHTTP_AUTO_DETECT_TYPE_DHCP|WINHTTP_AUTO_DETECT_TYPE_DNS_A;
|
|
if (ieconf.lpszAutoConfigUrl){
|
|
aopt.dwFlags|=WINHTTP_AUTOPROXY_CONFIG_URL;
|
|
aopt.lpszAutoConfigUrl=ieconf.lpszAutoConfigUrl;
|
|
}
|
|
aopt.fAutoLogonIfChallenged=TRUE;
|
|
if (WinHttpGetProxyForUrl(hi, L"https://api.pcloud.com/", &aopt, &pinfo) && pinfo.dwAccessType==WINHTTP_ACCESS_TYPE_NAMED_PROXY &&
|
|
try_set_proxy(pinfo.lpszProxy))
|
|
goto ex;
|
|
}
|
|
manual:
|
|
try_set_proxy(pinfo.lpszProxy);
|
|
ex:
|
|
if (hi)
|
|
WinHttpCloseHandle(hi);
|
|
gfree_ptr(pinfo.lpszProxy);
|
|
gfree_ptr(pinfo.lpszProxyBypass);
|
|
gfree_ptr(ieconf.lpszProxy);
|
|
gfree_ptr(ieconf.lpszProxyBypass);
|
|
gfree_ptr(ieconf.lpszAutoConfigUrl);
|
|
#elif defined(P_OS_MACOSX)
|
|
/* CFDictionaryRef proxies;
|
|
CFStringRef hostr, portr;
|
|
CFNumberRef enabledr;
|
|
UInt32 enabled;
|
|
if (recent_detect())
|
|
return;
|
|
proxies=SCDynamicStoreCopyProxies(NULL);
|
|
enabledr=(CFNumberRef)get_value_cstr(proxies, "HTTPSEnable");
|
|
if (enabledr!=NULL){
|
|
if (CFNumberGetValue(enabledr, kCFNumberIntType, &enabled) && enabled){
|
|
hostr=(CFStringRef)get_value_cstr(proxies, "HTTPSProxy");
|
|
portr=(CFStringRef)get_value_cstr(proxies, "HTTPSPort");
|
|
if (hostr!=NULL && portr!=NULL){
|
|
|
|
}
|
|
}
|
|
}
|
|
CFRelease(proxies);*/
|
|
#endif
|
|
}
|
|
|
|
static psync_socket_t connect_socket_direct(const char *host, const char *port){
|
|
struct addrinfo *res, *dbres;
|
|
struct addrinfo hints;
|
|
psync_socket_t sock;
|
|
int rc;
|
|
debug(D_NOTICE, "connecting to %s:%s", host, port);
|
|
dbres=addr_load_from_db(host, port);
|
|
if (dbres){
|
|
resolve_host_port resolv;
|
|
void *params[2];
|
|
psync_task_callback_t callbacks[2];
|
|
psync_task_manager_t tasks;
|
|
resolv.host=host;
|
|
resolv.port=port;
|
|
params[0]=dbres;
|
|
params[1]=&resolv;
|
|
callbacks[0]=connect_res_callback;
|
|
callbacks[1]=resolve_callback;
|
|
tasks=psync_task_run_tasks(callbacks, params, 2);
|
|
res=(struct addrinfo *)psync_task_get_result(tasks, 1);
|
|
if (unlikely(!res)){
|
|
psync_task_free(tasks);
|
|
detect_proxy();
|
|
debug(D_WARNING, "failed to resolve %s", host);
|
|
return INVALID_SOCKET;
|
|
}
|
|
addr_save_to_db(host, port, res);
|
|
if (addr_still_valid(dbres, res)){
|
|
debug(D_NOTICE, "successfully reused cached IP for %s:%s", host, port);
|
|
sock=(psync_socket_t)(uintptr_t)psync_task_get_result(tasks, 0);
|
|
}
|
|
else{
|
|
debug(D_NOTICE, "cached IP not valid for %s:%s", host, port);
|
|
sock=connect_res(res);
|
|
}
|
|
freeaddrinfo(res);
|
|
psync_task_free(tasks);
|
|
}
|
|
else{
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family=AF_UNSPEC;
|
|
hints.ai_socktype=SOCK_STREAM;
|
|
res=NULL;
|
|
rc=getaddrinfo(host, port, &hints, &res);
|
|
#if defined(P_OS_WINDOWS)
|
|
if (unlikely(rc==WSANOTINITIALISED)){
|
|
WSADATA wsaData;
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
|
|
return INVALID_SOCKET;
|
|
rc=getaddrinfo(host, port, &hints, &res);
|
|
}
|
|
#endif
|
|
if (unlikely(rc!=0)){
|
|
debug(D_WARNING, "failed to resolve %s", host);
|
|
detect_proxy();
|
|
return INVALID_SOCKET;
|
|
}
|
|
addr_save_to_db(host, port, res);
|
|
sock=connect_res(res);
|
|
freeaddrinfo(res);
|
|
}
|
|
if (likely(sock!=INVALID_SOCKET)){
|
|
int sock_opt=1;
|
|
#if defined(P_OS_LINUX)
|
|
setsockopt(sock, SOL_TCP, TCP_NODELAY, (char *)&sock_opt, sizeof(sock_opt));
|
|
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&sock_opt, sizeof(sock_opt));
|
|
#elif defined(P_OS_WINDOWS)
|
|
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&sock_opt, sizeof(sock_opt));
|
|
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&sock_opt, sizeof(sock_opt));
|
|
#elif defined(P_OS_MACOSX) || defined(P_OS_BSD)
|
|
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&sock_opt, sizeof(sock_opt));
|
|
setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, (char*)&sock_opt, sizeof(sock_opt));
|
|
#endif
|
|
#if defined(SOL_TCP)
|
|
#if defined(TCP_KEEPCNT)
|
|
sock_opt=3;
|
|
setsockopt(sock, SOL_TCP, TCP_KEEPCNT, (char *)&sock_opt, sizeof(sock_opt));
|
|
#endif
|
|
#if defined(TCP_KEEPIDLE)
|
|
sock_opt=60;
|
|
setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, (char *)&sock_opt, sizeof(sock_opt));
|
|
#endif
|
|
#if defined(TCP_KEEPINTVL)
|
|
sock_opt=20;
|
|
setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, (char *)&sock_opt, sizeof(sock_opt));
|
|
#endif
|
|
#endif
|
|
}
|
|
else{
|
|
detect_proxy();
|
|
debug(D_WARNING, "failed to connect to %s:%s", host, port);
|
|
}
|
|
return sock;
|
|
}
|
|
|
|
static int check_http_resp(char *str) {
|
|
if (memcmp(str, "HTTP", 4)){
|
|
debug(D_WARNING, "bad proxy response %s", str);
|
|
return 0;
|
|
}
|
|
while (*str && !isspace(*str))
|
|
str++;
|
|
while (*str && isspace(*str))
|
|
str++;
|
|
if (!isdigit(*str)){
|
|
debug(D_WARNING, "bad proxy response %s", str);
|
|
return 0;
|
|
}
|
|
if (atoi(str)!=200) {
|
|
debug(D_NOTICE, "proxy returned HTTP code %d", atoi(str));
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static psync_socket_t connect_socket_connect_proxy(const char *host, const char *port){
|
|
char buff[2048], *str;
|
|
psync_socket_t sock;
|
|
int ln, wr, r, rc;
|
|
sock=connect_socket_direct(proxy_host, proxy_port);
|
|
if (unlikely(sock==INVALID_SOCKET)){
|
|
debug(D_NOTICE, "connection to proxy %s:%s failed", proxy_host, proxy_port);
|
|
goto err0;
|
|
}
|
|
ln=psync_slprintf(buff, sizeof(buff), "CONNECT %s:%s HTTP/1.0\015\012User-Agent: %s\015\012\015\012", host, port, psync_software_name);
|
|
wr=0;
|
|
while (wr<ln){
|
|
r=psync_write_socket(sock, buff+wr, ln-wr);
|
|
if (unlikely(r==SOCKET_ERROR)){
|
|
if (likely_log((psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN || psync_sock_err()==P_INTR) && !psync_wait_socket_write_timeout(sock)))
|
|
continue;
|
|
else
|
|
goto err1;
|
|
}
|
|
wr+=r;
|
|
}
|
|
wr=0;
|
|
rc=0;
|
|
while (1){
|
|
if (unlikely(psync_wait_socket_read_timeout(sock))){
|
|
debug(D_WARNING, "connection to %s:%s via %s:%s timeouted", host, port, proxy_host, proxy_port);
|
|
goto err1;
|
|
}
|
|
r=psync_read_socket(sock, buff+wr, sizeof(buff)-1-wr);
|
|
if (unlikely(r==0 || r==SOCKET_ERROR)){
|
|
if (r==0){
|
|
debug(D_NOTICE, "proxy server %s:%s closed connection", proxy_host, proxy_port);
|
|
goto err1;
|
|
}
|
|
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN || psync_sock_err()==P_INTR))
|
|
continue;
|
|
else
|
|
goto err1;
|
|
}
|
|
wr+=r;
|
|
buff[wr]=0;
|
|
str=strstr(buff, "\015\012\015\012");
|
|
if (str){
|
|
if (rc || check_http_resp(buff)){
|
|
debug(D_NOTICE, "connected to %s:%s via %s:%s", host, port, proxy_host, proxy_port);
|
|
return sock;
|
|
}else
|
|
goto err1;
|
|
}
|
|
if (wr==sizeof(buff)-1){
|
|
rc=check_http_resp(buff);
|
|
if (!rc)
|
|
goto err1;
|
|
memcpy(buff, buff+sizeof(buff)-8, 8);
|
|
wr=7; // yes, 7
|
|
}
|
|
}
|
|
err1:
|
|
psync_close_socket(sock);
|
|
err0:
|
|
detect_proxy();
|
|
if (proxy_type!=PROXY_CONNECT)
|
|
return connect_socket_direct(host, port);
|
|
else
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
static psync_socket_t connect_socket(const char *host, const char *port){
|
|
if (unlikely(!proxy_detected)){
|
|
proxy_detected=1;
|
|
detect_proxy();
|
|
}
|
|
if (likely(proxy_type!=PROXY_CONNECT))
|
|
return connect_socket_direct(host, port);
|
|
else
|
|
return connect_socket_connect_proxy(host, port);
|
|
}
|
|
|
|
static int wait_sock_ready_for_ssl(psync_socket_t sock){
|
|
fd_set fds, *rfds, *wfds;
|
|
struct timeval tv;
|
|
int res;
|
|
FD_ZERO(&fds);
|
|
FD_SET(sock, &fds);
|
|
if (psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ){
|
|
rfds=&fds;
|
|
wfds=NULL;
|
|
tv.tv_sec=PSYNC_SOCK_READ_TIMEOUT;
|
|
}
|
|
else if (psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE){
|
|
rfds=NULL;
|
|
wfds=&fds;
|
|
tv.tv_sec=PSYNC_SOCK_WRITE_TIMEOUT;
|
|
}
|
|
else{
|
|
debug(D_BUG, "this functions should only be called when SSL returns WANT_READ/WANT_WRITE");
|
|
psync_sock_set_err(P_INVAL);
|
|
return SOCKET_ERROR;
|
|
}
|
|
tv.tv_usec=0;
|
|
res=select(sock+1, rfds, wfds, NULL, &tv);
|
|
if (res==1)
|
|
return 0;
|
|
if (res==0){
|
|
debug(D_WARNING, "socket timeouted");
|
|
psync_sock_set_err(P_TIMEDOUT);
|
|
}
|
|
return PRINT_RETURN_CONST(SOCKET_ERROR);
|
|
}
|
|
|
|
psync_socket *psync_socket_connect(const char *host, int unsigned port, int ssl){
|
|
psync_socket *ret;
|
|
void *sslc;
|
|
psync_socket_t sock;
|
|
char sport[8];
|
|
psync_slprintf(sport, sizeof(sport), "%d", port);
|
|
sock=connect_socket(host, sport);
|
|
if (unlikely_log(sock==INVALID_SOCKET))
|
|
return NULL;
|
|
if (ssl){
|
|
ssl=psync_ssl_connect(sock, &sslc, host);
|
|
while (ssl==PSYNC_SSL_NEED_FINISH){
|
|
if (wait_sock_ready_for_ssl(sock)){
|
|
psync_ssl_free(sslc);
|
|
break;
|
|
}
|
|
ssl=psync_ssl_connect_finish(sslc, host);
|
|
}
|
|
if (unlikely_log(ssl!=PSYNC_SSL_SUCCESS)){
|
|
psync_close_socket(sock);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
sslc=NULL;
|
|
ret=psync_new(psync_socket);
|
|
ret->ssl=sslc;
|
|
ret->buffer=NULL;
|
|
ret->sock=sock;
|
|
ret->pending=0;
|
|
return ret;
|
|
}
|
|
|
|
void psync_socket_close(psync_socket *sock){
|
|
if (sock->ssl)
|
|
while (psync_ssl_shutdown(sock->ssl)==PSYNC_SSL_NEED_FINISH)
|
|
if (wait_sock_ready_for_ssl(sock->sock)){
|
|
psync_ssl_free(sock->ssl);
|
|
break;
|
|
}
|
|
psync_socket_clear_write_buffered(sock);
|
|
psync_close_socket(sock->sock);
|
|
psync_free(sock);
|
|
}
|
|
|
|
void psync_socket_close_bad(psync_socket *sock){
|
|
if (sock->ssl)
|
|
psync_ssl_free(sock->ssl);
|
|
psync_socket_clear_write_buffered(sock);
|
|
psync_close_socket(sock->sock);
|
|
psync_free(sock);
|
|
}
|
|
|
|
void psync_socket_set_write_buffered(psync_socket *sock){
|
|
psync_socket_buffer *sb;
|
|
if (sock->buffer)
|
|
return;
|
|
sb=(psync_socket_buffer *)psync_malloc(offsetof(psync_socket_buffer, buff)+PSYNC_FIRST_SOCK_WRITE_BUFF_SIZE);
|
|
sb->next=NULL;
|
|
sb->size=PSYNC_FIRST_SOCK_WRITE_BUFF_SIZE;
|
|
sb->woffset=0;
|
|
sb->roffset=0;
|
|
sock->buffer=sb;
|
|
}
|
|
|
|
void psync_socket_set_write_buffered_thread(psync_socket *sock){
|
|
pthread_mutex_lock(&socket_mutex);
|
|
psync_socket_set_write_buffered(sock);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
}
|
|
|
|
void psync_socket_clear_write_buffered(psync_socket *sock){
|
|
psync_socket_buffer *nb;
|
|
while (sock->buffer){
|
|
nb=sock->buffer->next;
|
|
free(sock->buffer);
|
|
sock->buffer=nb;
|
|
}
|
|
}
|
|
|
|
void psync_socket_clear_write_buffered_thread(psync_socket *sock){
|
|
pthread_mutex_lock(&socket_mutex);
|
|
psync_socket_clear_write_buffered(sock);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
}
|
|
|
|
int psync_socket_set_recvbuf(psync_socket *sock, int bufsize){
|
|
#if defined(SO_RCVBUF) && defined(SOL_SOCKET)
|
|
return setsockopt(sock->sock, SOL_SOCKET, SO_RCVBUF, (const char*)&bufsize, sizeof(bufsize));
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int psync_socket_set_sendbuf(psync_socket *sock, int bufsize){
|
|
#if defined(SO_SNDBUF) && defined(SOL_SOCKET)
|
|
return setsockopt(sock->sock, SOL_SOCKET, SO_SNDBUF, (const char*)&bufsize, sizeof(bufsize));
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int psync_socket_isssl(psync_socket *sock){
|
|
if (sock->ssl)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int psync_socket_pendingdata(psync_socket *sock){
|
|
if (sock->pending)
|
|
return 1;
|
|
if (sock->ssl)
|
|
return psync_ssl_pendingdata(sock->ssl);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int psync_socket_pendingdata_buf(psync_socket *sock){
|
|
int ret;
|
|
#if defined(P_OS_POSIX) && defined(FIONREAD)
|
|
if (ioctl(sock->sock, FIONREAD, &ret))
|
|
return -1;
|
|
#elif defined(P_OS_WINDOWS)
|
|
u_long l;
|
|
if (ioctlsocket(sock->sock, FIONREAD, &l))
|
|
return -1;
|
|
else
|
|
ret=l;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
if (sock->ssl)
|
|
ret+=psync_ssl_pendingdata(sock->ssl);
|
|
return ret;
|
|
}
|
|
|
|
int psync_socket_pendingdata_buf_thread(psync_socket *sock){
|
|
int ret;
|
|
pthread_mutex_lock(&socket_mutex);
|
|
ret=psync_socket_pendingdata_buf(sock);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
return ret;
|
|
}
|
|
|
|
int psync_socket_try_write_buffer(psync_socket *sock){
|
|
if (sock->buffer){
|
|
psync_socket_buffer *b;
|
|
int wrt, cw;
|
|
wrt=0;
|
|
while ((b=sock->buffer)){
|
|
if (b->roffset==b->woffset){
|
|
sock->buffer=b->next;
|
|
psync_free(b);
|
|
continue;
|
|
}
|
|
if (sock->ssl){
|
|
cw=psync_ssl_write(sock->ssl, b->buff+b->roffset, b->woffset-b->roffset);
|
|
if (cw==PSYNC_SSL_FAIL){
|
|
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE))
|
|
break;
|
|
else{
|
|
if (!wrt)
|
|
wrt=-1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
cw=psync_write_socket(sock->sock, b->buff+b->roffset, b->woffset-b->roffset);
|
|
if (cw==SOCKET_ERROR){
|
|
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN || psync_sock_err()==P_INTR))
|
|
break;
|
|
else{
|
|
if (!wrt)
|
|
wrt=-1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
wrt+=cw;
|
|
b->roffset+=cw;
|
|
if (b->roffset!=b->woffset)
|
|
break;
|
|
}
|
|
if (wrt>0)
|
|
debug(D_NOTICE, "wrote %d bytes to socket from buffers", wrt);
|
|
return wrt;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int psync_socket_try_write_buffer_thread(psync_socket *sock){
|
|
int ret;
|
|
pthread_mutex_lock(&socket_mutex);
|
|
ret=psync_socket_try_write_buffer(sock);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
return ret;
|
|
}
|
|
|
|
int psync_socket_readable(psync_socket *sock){
|
|
psync_socket_try_write_buffer(sock);
|
|
if (sock->ssl && psync_ssl_pendingdata(sock->ssl))
|
|
return 1;
|
|
else if (psync_wait_socket_readable(sock->sock, 0))
|
|
return 0;
|
|
else{
|
|
sock->pending=1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int psync_socket_writable(psync_socket *sock){
|
|
if (sock->buffer)
|
|
return 1;
|
|
return !psync_wait_socket_writable(sock->sock, 0);
|
|
}
|
|
|
|
static int psync_socket_read_ssl(psync_socket *sock, void *buff, int num){
|
|
int r;
|
|
psync_socket_try_write_buffer(sock);
|
|
if (!psync_ssl_pendingdata(sock->ssl) && !sock->pending && psync_wait_socket_read_timeout(sock->sock))
|
|
return -1;
|
|
sock->pending=0;
|
|
while (1){
|
|
psync_socket_try_write_buffer(sock);
|
|
r=psync_ssl_read(sock->ssl, buff, num);
|
|
if (r==PSYNC_SSL_FAIL){
|
|
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE)){
|
|
if (wait_sock_ready_for_ssl(sock->sock)){
|
|
if (sock->buffer)
|
|
debug(D_WARNING, "timeouted on socket with pending buffers");
|
|
return -1;
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
else{
|
|
psync_sock_set_err(P_CONNRESET);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
return r;
|
|
}
|
|
}
|
|
|
|
static int psync_socket_read_plain(psync_socket *sock, void *buff, int num){
|
|
int r;
|
|
while (1){
|
|
psync_socket_try_write_buffer(sock);
|
|
if (sock->pending)
|
|
sock->pending=0;
|
|
else if (psync_wait_socket_read_timeout(sock->sock)){
|
|
debug(D_WARNING, "timeouted on socket with pending buffers");
|
|
return -1;
|
|
}
|
|
else
|
|
psync_socket_try_write_buffer(sock);
|
|
r=psync_read_socket(sock->sock, buff, num);
|
|
if (r==SOCKET_ERROR){
|
|
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN))
|
|
continue;
|
|
else
|
|
return -1;
|
|
}
|
|
else
|
|
return r;
|
|
}
|
|
}
|
|
|
|
int psync_socket_read(psync_socket *sock, void *buff, int num){
|
|
if (sock->ssl)
|
|
return psync_socket_read_ssl(sock, buff, num);
|
|
else
|
|
return psync_socket_read_plain(sock, buff, num);
|
|
}
|
|
|
|
static int psync_socket_read_noblock_ssl(psync_socket *sock, void *buff, int num){
|
|
int r;
|
|
r=psync_ssl_read(sock->ssl, buff, num);
|
|
if (r==PSYNC_SSL_FAIL){
|
|
sock->pending=0;
|
|
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE))
|
|
return PSYNC_SOCKET_WOULDBLOCK;
|
|
else{
|
|
psync_sock_set_err(P_CONNRESET);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
return r;
|
|
}
|
|
|
|
static int psync_socket_read_noblock_plain(psync_socket *sock, void *buff, int num){
|
|
int r;
|
|
r=psync_read_socket(sock->sock, buff, num);
|
|
if (r==SOCKET_ERROR){
|
|
sock->pending=0;
|
|
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN))
|
|
return PSYNC_SOCKET_WOULDBLOCK;
|
|
else
|
|
return -1;
|
|
}
|
|
else
|
|
return r;
|
|
}
|
|
|
|
int psync_socket_read_noblock(psync_socket *sock, void *buff, int num){
|
|
psync_socket_try_write_buffer(sock);
|
|
if (sock->ssl)
|
|
return psync_socket_read_noblock_ssl(sock, buff, num);
|
|
else
|
|
return psync_socket_read_noblock_plain(sock, buff, num);
|
|
}
|
|
|
|
static int psync_socket_read_ssl_thread(psync_socket *sock, void *buff, int num){
|
|
int r;
|
|
pthread_mutex_lock(&socket_mutex);
|
|
psync_socket_try_write_buffer(sock);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
if (!psync_ssl_pendingdata(sock->ssl) && !sock->pending && psync_wait_socket_read_timeout(sock->sock))
|
|
return -1;
|
|
sock->pending=0;
|
|
while (1){
|
|
pthread_mutex_lock(&socket_mutex);
|
|
psync_socket_try_write_buffer(sock);
|
|
r=psync_ssl_read(sock->ssl, buff, num);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
if (r==PSYNC_SSL_FAIL){
|
|
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE)){
|
|
if (wait_sock_ready_for_ssl(sock->sock))
|
|
return -1;
|
|
else
|
|
continue;
|
|
}
|
|
else{
|
|
psync_sock_set_err(P_CONNRESET);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
return r;
|
|
}
|
|
}
|
|
|
|
static int psync_socket_read_plain_thread(psync_socket *sock, void *buff, int num){
|
|
int r;
|
|
pthread_mutex_lock(&socket_mutex);
|
|
psync_socket_try_write_buffer(sock);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
while (1){
|
|
if (sock->pending)
|
|
sock->pending=0;
|
|
else if (psync_wait_socket_read_timeout(sock->sock))
|
|
return -1;
|
|
pthread_mutex_lock(&socket_mutex);
|
|
psync_socket_try_write_buffer(sock);
|
|
r=psync_read_socket(sock->sock, buff, num);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
if (r==SOCKET_ERROR){
|
|
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN))
|
|
continue;
|
|
else
|
|
return -1;
|
|
}
|
|
else
|
|
return r;
|
|
}
|
|
}
|
|
|
|
int psync_socket_read_thread(psync_socket *sock, void *buff, int num){
|
|
if (sock->ssl)
|
|
return psync_socket_read_ssl_thread(sock, buff, num);
|
|
else
|
|
return psync_socket_read_plain_thread(sock, buff, num);
|
|
}
|
|
|
|
static int psync_socket_write_to_buf(psync_socket *sock, const void *buff, int num){
|
|
psync_socket_buffer *b;
|
|
assert(sock->buffer);
|
|
b=sock->buffer;
|
|
while (b->next)
|
|
b=b->next;
|
|
if (likely(b->size-b->woffset>=num)){
|
|
memcpy(b->buff+b->woffset, buff, num);
|
|
b->woffset+=num;
|
|
return num;
|
|
}
|
|
else{
|
|
uint32_t rnum, wr;
|
|
rnum=num;
|
|
do {
|
|
wr=b->size-b->woffset;
|
|
if (!wr){
|
|
b->next=(psync_socket_buffer *)psync_malloc(offsetof(psync_socket_buffer, buff)+PSYNC_SECOND_SOCK_WRITE_BUFF_SIZE);
|
|
b=b->next;
|
|
b->next=NULL;
|
|
b->size=PSYNC_SECOND_SOCK_WRITE_BUFF_SIZE;
|
|
b->woffset=0;
|
|
b->roffset=0;
|
|
wr=PSYNC_SECOND_SOCK_WRITE_BUFF_SIZE;
|
|
}
|
|
if (wr>rnum)
|
|
wr=rnum;
|
|
memcpy(b->buff+b->woffset, buff, wr);
|
|
b->woffset+=wr;
|
|
buff=(const char *)buff+wr;
|
|
rnum-=wr;
|
|
} while (rnum);
|
|
return num;
|
|
}
|
|
}
|
|
|
|
int psync_socket_write(psync_socket *sock, const void *buff, int num){
|
|
int r;
|
|
if (sock->buffer)
|
|
return psync_socket_write_to_buf(sock, buff, num);
|
|
if (psync_wait_socket_write_timeout(sock->sock))
|
|
return -1;
|
|
if (sock->ssl){
|
|
r=psync_ssl_write(sock->ssl, buff, num);
|
|
if (r==PSYNC_SSL_FAIL){
|
|
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
}
|
|
else{
|
|
r=psync_write_socket(sock->sock, buff, num);
|
|
if (r==SOCKET_ERROR){
|
|
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN || psync_sock_err()==P_INTR))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static int psync_socket_readall_ssl(psync_socket *sock, void *buff, int num){
|
|
int br, r;
|
|
br=0;
|
|
|
|
psync_socket_try_write_buffer(sock);
|
|
|
|
if (!psync_ssl_pendingdata(sock->ssl) && !sock->pending && psync_wait_socket_read_timeout(sock->sock)) {
|
|
return -1;
|
|
}
|
|
|
|
sock->pending=0;
|
|
|
|
while (br<num){
|
|
psync_socket_try_write_buffer(sock);
|
|
|
|
r=psync_ssl_read(sock->ssl, (char *)buff+br, num-br);
|
|
|
|
if (r==PSYNC_SSL_FAIL){
|
|
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE)){
|
|
if (wait_sock_ready_for_ssl(sock->sock))
|
|
return -1;
|
|
else
|
|
continue;
|
|
}
|
|
else{
|
|
psync_sock_set_err(P_CONNRESET);
|
|
return -1;
|
|
}
|
|
}
|
|
if (r==0)
|
|
return br;
|
|
br+=r;
|
|
}
|
|
|
|
return br;
|
|
}
|
|
|
|
static int psync_socket_readall_plain(psync_socket *sock, void *buff, int num){
|
|
int br, r;
|
|
br=0;
|
|
while (br<num){
|
|
psync_socket_try_write_buffer(sock);
|
|
if (sock->pending)
|
|
sock->pending=0;
|
|
else if (psync_wait_socket_read_timeout(sock->sock))
|
|
return -1;
|
|
else
|
|
psync_socket_try_write_buffer(sock);
|
|
r=psync_read_socket(sock->sock, (char *)buff+br, num-br);
|
|
if (r==SOCKET_ERROR){
|
|
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN))
|
|
continue;
|
|
else
|
|
return -1;
|
|
}
|
|
if (r==0)
|
|
return br;
|
|
br+=r;
|
|
}
|
|
return br;
|
|
}
|
|
|
|
int psync_socket_readall(psync_socket* sock, void* buff, int num) {
|
|
if (sock->ssl) {
|
|
return psync_socket_readall_ssl(sock, buff, num);
|
|
}
|
|
else {
|
|
return psync_socket_readall_plain(sock, buff, num);
|
|
}
|
|
}
|
|
|
|
|
|
static int psync_socket_writeall_ssl(psync_socket *sock, const void *buff, int num){
|
|
int br, r;
|
|
br=0;
|
|
while (br<num){
|
|
r=psync_ssl_write(sock->ssl, (char *)buff+br, num-br);
|
|
if (r==PSYNC_SSL_FAIL){
|
|
if (psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE){
|
|
if (wait_sock_ready_for_ssl(sock->sock))
|
|
return -1;
|
|
else
|
|
continue;
|
|
}
|
|
else{
|
|
psync_sock_set_err(P_CONNRESET);
|
|
return -1;
|
|
}
|
|
}
|
|
if (r==0)
|
|
return br;
|
|
br+=r;
|
|
}
|
|
return br;
|
|
}
|
|
|
|
static int psync_socket_writeall_plain(psync_socket_t sock, const void *buff, int num){
|
|
int br, r;
|
|
br=0;
|
|
while (br<num){
|
|
r=psync_write_socket(sock, (const char *)buff+br, num-br);
|
|
if (r==SOCKET_ERROR){
|
|
if (psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN){
|
|
if (psync_wait_socket_write_timeout(sock))
|
|
return -1;
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
br+=r;
|
|
}
|
|
return br;
|
|
}
|
|
|
|
int psync_socket_writeall(psync_socket *sock, const void *buff, int num){
|
|
if (sock->buffer)
|
|
return psync_socket_write_to_buf(sock, buff, num);
|
|
if (sock->ssl)
|
|
return psync_socket_writeall_ssl(sock, buff, num);
|
|
else
|
|
return psync_socket_writeall_plain(sock->sock, buff, num);
|
|
}
|
|
|
|
static int psync_socket_readall_ssl_thread(psync_socket *sock, void *buff, int num){
|
|
int br, r;
|
|
br=0;
|
|
pthread_mutex_lock(&socket_mutex);
|
|
psync_socket_try_write_buffer(sock);
|
|
r=psync_ssl_pendingdata(sock->ssl);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
if (!r && !sock->pending && psync_wait_socket_read_timeout(sock->sock))
|
|
return -1;
|
|
sock->pending=0;
|
|
while (br<num){
|
|
pthread_mutex_lock(&socket_mutex);
|
|
psync_socket_try_write_buffer(sock);
|
|
r=psync_ssl_read(sock->ssl, (char *)buff+br, num-br);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
if (r==PSYNC_SSL_FAIL){
|
|
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE)){
|
|
if (wait_sock_ready_for_ssl(sock->sock))
|
|
return -1;
|
|
else
|
|
continue;
|
|
}
|
|
else{
|
|
psync_sock_set_err(P_CONNRESET);
|
|
return -1;
|
|
}
|
|
}
|
|
if (r==0)
|
|
return br;
|
|
br+=r;
|
|
}
|
|
return br;
|
|
}
|
|
|
|
static int psync_socket_readall_plain_thread(psync_socket *sock, void *buff, int num){
|
|
int br, r;
|
|
br=0;
|
|
pthread_mutex_lock(&socket_mutex);
|
|
psync_socket_try_write_buffer(sock);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
while (br<num){
|
|
if (sock->pending)
|
|
sock->pending=0;
|
|
else if (psync_wait_socket_read_timeout(sock->sock))
|
|
return -1;
|
|
pthread_mutex_lock(&socket_mutex);
|
|
psync_socket_try_write_buffer(sock);
|
|
r=psync_read_socket(sock->sock, (char *)buff+br, num-br);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
if (r==SOCKET_ERROR){
|
|
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN))
|
|
continue;
|
|
else
|
|
return -1;
|
|
}
|
|
if (r==0)
|
|
return br;
|
|
br+=r;
|
|
}
|
|
return br;
|
|
}
|
|
|
|
int psync_socket_readall_thread(psync_socket *sock, void *buff, int num){
|
|
if (sock->ssl)
|
|
return psync_socket_readall_ssl_thread(sock, buff, num);
|
|
else
|
|
return psync_socket_readall_plain_thread(sock, buff, num);
|
|
}
|
|
|
|
|
|
static int psync_socket_writeall_ssl_thread(psync_socket *sock, const void *buff, int num){
|
|
int br, r;
|
|
br=0;
|
|
while (br<num){
|
|
pthread_mutex_lock(&socket_mutex);
|
|
if (sock->buffer)
|
|
r=psync_socket_write_to_buf(sock, buff, num);
|
|
else
|
|
r=psync_ssl_write(sock->ssl, (char *)buff+br, num-br);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
if (r==PSYNC_SSL_FAIL){
|
|
if (psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE){
|
|
if (wait_sock_ready_for_ssl(sock->sock))
|
|
return -1;
|
|
else
|
|
continue;
|
|
}
|
|
else{
|
|
psync_sock_set_err(P_CONNRESET);
|
|
return -1;
|
|
}
|
|
}
|
|
if (r==0)
|
|
return br;
|
|
br+=r;
|
|
}
|
|
return br;
|
|
}
|
|
|
|
static int psync_socket_writeall_plain_thread(psync_socket *sock, const void *buff, int num){
|
|
int br, r;
|
|
br=0;
|
|
while (br<num){
|
|
pthread_mutex_lock(&socket_mutex);
|
|
if (sock->buffer)
|
|
r=psync_socket_write_to_buf(sock, buff, num);
|
|
else
|
|
r=psync_write_socket(sock->sock, (const char *)buff+br, num-br);
|
|
pthread_mutex_unlock(&socket_mutex);
|
|
if (r==SOCKET_ERROR){
|
|
if (psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN){
|
|
if (psync_wait_socket_write_timeout(sock->sock))
|
|
return -1;
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
br+=r;
|
|
}
|
|
return br;
|
|
}
|
|
|
|
int psync_socket_writeall_thread(psync_socket *sock, const void *buff, int num){
|
|
if (sock->ssl)
|
|
return psync_socket_writeall_ssl_thread(sock, buff, num);
|
|
else
|
|
return psync_socket_writeall_plain_thread(sock, buff, num);
|
|
}
|
|
|
|
static void copy_address(struct sockaddr_storage *dst, const struct sockaddr *src) {
|
|
dst->ss_family=src->sa_family;
|
|
if (src->sa_family==AF_INET)
|
|
memcpy(&((struct sockaddr_in *)dst)->sin_addr, &((const struct sockaddr_in *)src)->sin_addr, sizeof(((struct sockaddr_in *)dst)->sin_addr));
|
|
else
|
|
memcpy(&((struct sockaddr_in6 *)dst)->sin6_addr, &((const struct sockaddr_in6 *)src)->sin6_addr, sizeof(((struct sockaddr_in6 *)dst)->sin6_addr));
|
|
}
|
|
|
|
psync_interface_list_t *psync_list_ip_adapters(){
|
|
psync_interface_list_t *ret;
|
|
size_t cnt;
|
|
#if defined(P_OS_POSIX)
|
|
struct ifaddrs *addrs, *addr;
|
|
sa_family_t family;
|
|
size_t sz;
|
|
if (unlikely_log(getifaddrs(&addrs)))
|
|
goto empty;
|
|
cnt=0;
|
|
addr=addrs;
|
|
while (addr){
|
|
if (addr->ifa_addr){
|
|
family=addr->ifa_addr->sa_family;
|
|
if ((family==AF_INET || family==AF_INET6) && addr->ifa_broadaddr && addr->ifa_netmask)
|
|
cnt++;
|
|
}
|
|
addr=addr->ifa_next;
|
|
}
|
|
ret=psync_malloc(offsetof(psync_interface_list_t, interfaces)+sizeof(psync_interface_t)*cnt);
|
|
memset(ret, 0, offsetof(psync_interface_list_t, interfaces)+sizeof(psync_interface_t)*cnt);
|
|
ret->interfacecnt=cnt;
|
|
addr=addrs;
|
|
cnt=0;
|
|
while (addr){
|
|
if (addr->ifa_addr){
|
|
family=addr->ifa_addr->sa_family;
|
|
if ((family==AF_INET || family==AF_INET6) && addr->ifa_broadaddr && addr->ifa_netmask){
|
|
if (family==AF_INET)
|
|
sz=sizeof(struct sockaddr_in);
|
|
else
|
|
sz=sizeof(struct sockaddr_in6);
|
|
copy_address(&ret->interfaces[cnt].address, addr->ifa_addr);
|
|
copy_address(&ret->interfaces[cnt].broadcast, addr->ifa_broadaddr);
|
|
copy_address(&ret->interfaces[cnt].netmask, addr->ifa_netmask);
|
|
ret->interfaces[cnt].addrsize=sz;
|
|
cnt++;
|
|
}
|
|
}
|
|
addr=addr->ifa_next;
|
|
}
|
|
freeifaddrs(addrs);
|
|
return ret;
|
|
#elif defined(P_OS_WINDOWS)
|
|
{
|
|
IP_ADAPTER_ADDRESSES *adapters, *adapter;
|
|
IP_ADAPTER_UNICAST_ADDRESS *addr;
|
|
ULONG sz, rt, fl;
|
|
int isz;
|
|
sz=16*1024;
|
|
adapters=(IP_ADAPTER_ADDRESSES *)psync_malloc(sz);
|
|
fl=GAA_FLAG_SKIP_DNS_SERVER|GAA_FLAG_SKIP_FRIENDLY_NAME|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|0x0800;//GAA_FLAG_SKIP_DNS_INFO;
|
|
rt=GetAdaptersAddresses(AF_UNSPEC, fl, NULL, adapters, &sz);
|
|
if (rt==ERROR_BUFFER_OVERFLOW){
|
|
adapters=(IP_ADAPTER_ADDRESSES *)psync_realloc(adapters, sz);
|
|
rt=GetAdaptersAddresses(AF_UNSPEC, fl, NULL, adapters, &sz);
|
|
}
|
|
if (rt!=ERROR_SUCCESS){
|
|
psync_free(adapters);
|
|
goto empty;
|
|
}
|
|
adapter=adapters;
|
|
cnt=0;
|
|
while (adapter){
|
|
addr=adapter->FirstUnicastAddress;
|
|
while (addr){
|
|
if (!(addr->Flags&IP_ADAPTER_ADDRESS_TRANSIENT) && (addr->Address.lpSockaddr->sa_family==AF_INET || addr->Address.lpSockaddr->sa_family==AF_INET6))
|
|
cnt++;
|
|
addr=addr->Next;
|
|
}
|
|
adapter=adapter->Next;
|
|
}
|
|
ret=psync_malloc(offsetof(psync_interface_list_t, interfaces)+sizeof(psync_interface_t)*cnt);
|
|
memset(ret, 0, offsetof(psync_interface_list_t, interfaces)+sizeof(psync_interface_t)*cnt);
|
|
ret->interfacecnt=cnt;
|
|
adapter=adapters;
|
|
cnt=0;
|
|
while (adapter){
|
|
addr=adapter->FirstUnicastAddress;
|
|
while (addr){
|
|
if (!(addr->Flags&IP_ADAPTER_ADDRESS_TRANSIENT) && (addr->Address.lpSockaddr->sa_family==AF_INET || addr->Address.lpSockaddr->sa_family==AF_INET6)){
|
|
isz=addr->Address.iSockaddrLength;
|
|
memcpy(&ret->interfaces[cnt].address, addr->Address.lpSockaddr, isz);
|
|
if (addr->Address.lpSockaddr->sa_family==AF_INET){
|
|
ret->interfaces[cnt].broadcast.ss_family=AF_INET;
|
|
memset(&(((struct sockaddr_in *)(&ret->interfaces[cnt].broadcast))->sin_addr), 0xff, sizeof(((struct sockaddr_in *)NULL)->sin_addr));
|
|
}
|
|
else{
|
|
ret->interfaces[cnt].broadcast.ss_family=AF_INET6;
|
|
memset(&(((struct sockaddr_in6 *)(&ret->interfaces[cnt].broadcast))->sin6_addr), 0xff, sizeof(((struct sockaddr_in6 *)NULL)->sin6_addr));
|
|
}
|
|
ret->interfaces[cnt].addrsize=isz;
|
|
cnt++;
|
|
}
|
|
addr=addr->Next;
|
|
}
|
|
adapter=adapter->Next;
|
|
}
|
|
psync_free(adapters);
|
|
return ret;
|
|
}
|
|
#endif
|
|
empty:
|
|
ret=psync_malloc(offsetof(psync_interface_list_t, interfaces));
|
|
ret->interfacecnt=0;
|
|
return ret;
|
|
}
|
|
|
|
#if !defined(P_OS_POSIX)
|
|
static int psync_compat_socketpair(psync_socket_t sockfd[2]){
|
|
psync_socket_t sock;
|
|
struct sockaddr_in addr;
|
|
socklen_t addrlen;
|
|
sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (sock==INVALID_SOCKET){
|
|
#if defined(P_OS_WINDOWS)
|
|
if (psync_sock_err()==WSANOTINITIALISED){
|
|
WSADATA wsaData;
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData) || (sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET)
|
|
goto err0;
|
|
}
|
|
else
|
|
#endif
|
|
goto err0;
|
|
}
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family=AF_INET;
|
|
addr.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
|
|
addrlen=sizeof(addr);
|
|
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))==SOCKET_ERROR ||
|
|
listen(sock, 1)==SOCKET_ERROR ||
|
|
getsockname(sock, (struct sockaddr *)&addr, &addrlen)==SOCKET_ERROR ||
|
|
(sockfd[0]=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET)
|
|
goto err1;
|
|
if (connect(sockfd[0], (struct sockaddr *)&addr, addrlen)==SOCKET_ERROR ||
|
|
(sockfd[1]=accept(sock, NULL, NULL))==INVALID_SOCKET)
|
|
goto err2;
|
|
psync_close_socket(sock);
|
|
return 0;
|
|
err2:
|
|
psync_close_socket(sockfd[0]);
|
|
err1:
|
|
psync_close_socket(sock);
|
|
err0:
|
|
return SOCKET_ERROR;
|
|
}
|
|
#endif
|
|
|
|
int psync_pipe(psync_socket_t pipefd[2]){
|
|
#if defined(P_OS_POSIX)
|
|
return pipe(pipefd);
|
|
#else
|
|
return psync_compat_socketpair(pipefd);
|
|
#endif
|
|
}
|
|
|
|
int psync_pipe_close(psync_socket_t pfd){
|
|
return psync_close_socket(pfd);
|
|
}
|
|
|
|
int psync_pipe_read(psync_socket_t pfd, void *buff, int num){
|
|
#if defined(P_OS_POSIX)
|
|
return read(pfd, buff, num);
|
|
#elif defined(P_OS_WINDOWS)
|
|
return recv(pfd, (char *)buff, num, 0);
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_pipe_write(psync_socket_t pfd, const void *buff, int num){
|
|
#if defined(P_OS_POSIX)
|
|
return write(pfd, buff, num);
|
|
#elif defined(P_OS_WINDOWS)
|
|
return send(pfd, (const char *)buff, num, 0);
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_socket_pair(psync_socket_t sfd[2]){
|
|
#if defined(P_OS_POSIX)
|
|
return socketpair(AF_UNIX, SOCK_STREAM, 0, sfd);
|
|
#else
|
|
return psync_compat_socketpair(sfd);
|
|
#endif
|
|
}
|
|
|
|
int psync_socket_is_broken(psync_socket_t sock){
|
|
fd_set rfds;
|
|
struct timeval tv;
|
|
memset(&tv, 0, sizeof(tv));
|
|
FD_ZERO(&rfds);
|
|
FD_SET(sock, &rfds);
|
|
return select(sock+1, NULL, NULL, &rfds, &tv)==1;
|
|
}
|
|
|
|
int psync_select_in(psync_socket_t *sockets, int cnt, int64_t timeoutmillisec){
|
|
fd_set rfds;
|
|
struct timeval tv, *ptv;
|
|
psync_socket_t max;
|
|
int i;
|
|
if (timeoutmillisec<0)
|
|
ptv=NULL;
|
|
else{
|
|
tv.tv_sec=timeoutmillisec/1000;
|
|
tv.tv_usec=(timeoutmillisec%1000)*1000;
|
|
ptv=&tv;
|
|
}
|
|
FD_ZERO(&rfds);
|
|
max=0;
|
|
for (i=0; i<cnt; i++){
|
|
FD_SET(sockets[i], &rfds);
|
|
if (sockets[i]>=max)
|
|
max=sockets[i]+1;
|
|
}
|
|
i=select(max, &rfds, NULL, NULL, ptv);
|
|
if (i>0){
|
|
for (i=0; i<cnt; i++)
|
|
if (FD_ISSET(sockets[i], &rfds))
|
|
return i;
|
|
}
|
|
else if (i==0)
|
|
psync_sock_set_err(P_TIMEDOUT);
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
int psync_list_dir(const char *path, psync_list_dir_callback callback, void *ptr){
|
|
#if defined(P_OS_POSIX)
|
|
psync_pstat pst;
|
|
DIR *dh;
|
|
char *cpath;
|
|
size_t pl, entrylen;
|
|
long namelen;
|
|
struct dirent *entry, *de;
|
|
dh=opendir(path);
|
|
if (unlikely(!dh)){
|
|
debug(D_WARNING, "could not open directory %s", path);
|
|
goto err1;
|
|
}
|
|
pl=strlen(path);
|
|
namelen=pathconf(path, _PC_NAME_MAX);
|
|
if (unlikely_log(namelen==-1))
|
|
namelen=255;
|
|
if (namelen<sizeof(de->d_name)-1)
|
|
namelen=sizeof(de->d_name)-1;
|
|
entrylen=offsetof(struct dirent, d_name)+namelen+1;
|
|
cpath=(char *)psync_malloc(pl+namelen+2);
|
|
entry=(struct dirent *)psync_malloc(entrylen);
|
|
memcpy(cpath, path, pl);
|
|
if (!pl || cpath[pl-1]!=PSYNC_DIRECTORY_SEPARATORC)
|
|
cpath[pl++]=PSYNC_DIRECTORY_SEPARATORC;
|
|
pst.path=cpath;
|
|
while (!readdir_r(dh, entry, &de) && de)
|
|
if (de->d_name[0]!='.' || (de->d_name[1]!=0 && (de->d_name[1]!='.' || de->d_name[2]!=0))){
|
|
psync_strlcpy(cpath+pl, de->d_name, namelen+1);
|
|
if (likely_log(!lstat(cpath, &pst.stat)) && (S_ISREG(pst.stat.st_mode) || S_ISDIR(pst.stat.st_mode))){
|
|
#if defined(P_OS_MACOSX)
|
|
if (pst.stat.st_flags&(UF_HIDDEN|UF_IMMUTABLE|SF_IMMUTABLE))
|
|
continue;
|
|
#endif
|
|
pst.name=de->d_name;
|
|
callback(ptr, &pst);
|
|
}
|
|
}
|
|
psync_free(entry);
|
|
psync_free(cpath);
|
|
closedir(dh);
|
|
return 0;
|
|
err1:
|
|
psync_error=PERROR_LOCAL_FOLDER_NOT_FOUND;
|
|
return -1;
|
|
#elif defined(P_OS_WINDOWS)
|
|
psync_pstat pst;
|
|
char *spath, *name;
|
|
wchar_t *wpath;
|
|
WIN32_FIND_DATAW st;
|
|
HANDLE dh;
|
|
spath=psync_strcat(path, PSYNC_DIRECTORY_SEPARATOR "*", NULL);
|
|
wpath=utf8_to_wchar_path(spath);
|
|
psync_free(spath);
|
|
dh=FindFirstFileW(wpath, &st);
|
|
psync_free(wpath);
|
|
if (dh==INVALID_HANDLE_VALUE){
|
|
if (GetLastError()==ERROR_FILE_NOT_FOUND)
|
|
return 0;
|
|
else{
|
|
psync_error=PERROR_LOCAL_FOLDER_NOT_FOUND;
|
|
return -1;
|
|
}
|
|
}
|
|
do {
|
|
if (st.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY && (!wcscmp(st.cFileName, L".") || !wcscmp(st.cFileName, L"..")))
|
|
continue;
|
|
if (st.dwFileAttributes&(FILE_ATTRIBUTE_SYSTEM|/*FILE_ATTRIBUTE_TEMPORARY|*/FILE_ATTRIBUTE_DEVICE|FILE_ATTRIBUTE_HIDDEN)){
|
|
if (st.dwFileAttributes&FILE_ATTRIBUTE_SYSTEM)
|
|
debug(D_NOTICE, "Ignoring file %ls with FILE_ATTRIBUTE_SYSTEM attribute", st.cFileName);
|
|
if (st.dwFileAttributes&FILE_ATTRIBUTE_TEMPORARY)
|
|
debug(D_NOTICE, "Ignoring file %ls with FILE_ATTRIBUTE_TEMPORARY attribute", st.cFileName);
|
|
if (st.dwFileAttributes&FILE_ATTRIBUTE_DEVICE)
|
|
debug(D_NOTICE, "Ignoring file %ls with FILE_ATTRIBUTE_DEVICE attribute", st.cFileName);
|
|
if (st.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN)
|
|
debug(D_NOTICE, "Ignoring file %ls with FILE_ATTRIBUTE_HIDDEN attribute", st.cFileName);
|
|
continue;
|
|
}
|
|
name=wchar_to_utf8(st.cFileName);
|
|
spath=psync_strcat(path, PSYNC_DIRECTORY_SEPARATOR, name, NULL);
|
|
pst.name=name;
|
|
pst.path=spath;
|
|
if (likely_log(!psync_stat(spath, &pst.stat)))
|
|
callback(ptr, &pst);
|
|
psync_free(name);
|
|
psync_free(spath);
|
|
} while (FindNextFileW(dh, &st));
|
|
FindClose(dh);
|
|
return 0;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_list_dir_fast(const char *path, psync_list_dir_callback_fast callback, void *ptr){
|
|
#if defined(P_OS_MACOSX)
|
|
return psync_list_dir(path, callback, ptr);
|
|
#elif defined(P_OS_POSIX)
|
|
psync_pstat_fast pst;
|
|
struct stat st;
|
|
DIR *dh;
|
|
char *cpath;
|
|
size_t pl, entrylen;
|
|
long namelen;
|
|
struct dirent *entry, *de;
|
|
dh=opendir(path);
|
|
if (unlikely_log(!dh))
|
|
goto err1;
|
|
pl=strlen(path);
|
|
namelen=pathconf(path, _PC_NAME_MAX);
|
|
if (namelen==-1)
|
|
namelen=255;
|
|
if (namelen<sizeof(de->d_name)-1)
|
|
namelen=sizeof(de->d_name)-1;
|
|
entrylen=offsetof(struct dirent, d_name)+namelen+1;
|
|
cpath=(char *)psync_malloc(pl+namelen+2);
|
|
entry=(struct dirent *)psync_malloc(entrylen);
|
|
memcpy(cpath, path, pl);
|
|
if (!pl || cpath[pl-1]!=PSYNC_DIRECTORY_SEPARATORC)
|
|
cpath[pl++]=PSYNC_DIRECTORY_SEPARATORC;
|
|
while (!readdir_r(dh, entry, &de) && de)
|
|
if (de->d_name[0]!='.' || (de->d_name[1]!=0 && (de->d_name[1]!='.' || de->d_name[2]!=0))){
|
|
#if defined(DT_UNKNOWN) && defined(DT_DIR) && defined(DT_REG)
|
|
pst.name=de->d_name;
|
|
if (de->d_type==DT_UNKNOWN){
|
|
psync_strlcpy(cpath+pl, de->d_name, namelen+1);
|
|
if (unlikely_log(lstat(cpath, &st)))
|
|
continue;
|
|
pst.isfolder=S_ISDIR(st.st_mode);
|
|
}
|
|
else if (de->d_type==DT_DIR)
|
|
pst.isfolder=1;
|
|
else if (de->d_type==DT_REG)
|
|
pst.isfolder=0;
|
|
else
|
|
continue;
|
|
callback(ptr, &pst);
|
|
#else
|
|
psync_strlcpy(cpath+pl, de->d_name, namelen+1);
|
|
if (likely_log(!lstat(cpath, &st))){
|
|
pst.name=de->d_name;
|
|
pst.isfolder=S_ISDIR(st.st_mode);
|
|
callback(ptr, &pst);
|
|
}
|
|
#endif
|
|
}
|
|
psync_free(entry);
|
|
psync_free(cpath);
|
|
closedir(dh);
|
|
return 0;
|
|
err1:
|
|
psync_error=PERROR_LOCAL_FOLDER_NOT_FOUND;
|
|
return -1;
|
|
#elif defined(P_OS_WINDOWS)
|
|
psync_pstat_fast pst;
|
|
char *spath, *name;
|
|
wchar_t *wpath;
|
|
WIN32_FIND_DATAW st;
|
|
HANDLE dh;
|
|
spath=psync_strcat(path, PSYNC_DIRECTORY_SEPARATOR "*", NULL);
|
|
wpath=utf8_to_wchar_path(spath);
|
|
psync_free(spath);
|
|
dh=FindFirstFileW(wpath, &st);
|
|
psync_free(wpath);
|
|
if (dh==INVALID_HANDLE_VALUE){
|
|
if (GetLastError()==ERROR_FILE_NOT_FOUND)
|
|
return 0;
|
|
else{
|
|
psync_error=PERROR_LOCAL_FOLDER_NOT_FOUND;
|
|
return -1;
|
|
}
|
|
}
|
|
do {
|
|
name=wchar_to_utf8(st.cFileName);
|
|
pst.name=name;
|
|
pst.isfolder=(st.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)>0;
|
|
callback(ptr, &pst);
|
|
psync_free(name);
|
|
} while (FindNextFileW(dh, &st));
|
|
FindClose(dh);
|
|
return 0;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int64_t psync_get_free_space_by_path(const char *path){
|
|
#if defined(P_OS_POSIX)
|
|
struct statvfs buf;
|
|
if (unlikely_log(statvfs(path, &buf)))
|
|
return -1;
|
|
else
|
|
return (int64_t)buf.f_bavail*(int64_t)buf.f_frsize;
|
|
#elif defined(P_OS_WINDOWS)
|
|
ULARGE_INTEGER free;
|
|
wchar_t *wpath;
|
|
BOOL ret;
|
|
wpath=utf8_to_wchar_path(path);
|
|
ret=GetDiskFreeSpaceExW(wpath, &free, NULL, NULL);
|
|
psync_free(wpath);
|
|
if (likely_log(ret))
|
|
return free.QuadPart;
|
|
else
|
|
return -1;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_mkdir(const char *path){
|
|
#if defined(P_OS_POSIX)
|
|
return mkdir(path, PSYNC_DEFAULT_POSIX_FOLDER_MODE);
|
|
#elif defined(P_OS_WINDOWS)
|
|
wchar_t *wpath;
|
|
int ret;
|
|
wpath=utf8_to_wchar_path(path);
|
|
ret=psync_bool_to_zero(CreateDirectoryW(wpath, NULL));
|
|
psync_free(wpath);
|
|
return ret;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_rmdir(const char *path){
|
|
#if defined(P_OS_POSIX)
|
|
return rmdir(path);
|
|
#elif defined(P_OS_WINDOWS)
|
|
wchar_t *wpath;
|
|
int ret;
|
|
wpath=utf8_to_wchar_path(path);
|
|
ret=psync_bool_to_zero(RemoveDirectoryW(wpath));
|
|
psync_free(wpath);
|
|
return ret;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_file_rename(const char *oldpath, const char *newpath){
|
|
#if defined(P_OS_POSIX)
|
|
return rename(oldpath, newpath);
|
|
#elif defined(P_OS_WINDOWS) // should we just use rename() here?
|
|
wchar_t *oldwpath, *newwpath;
|
|
int ret;
|
|
oldwpath=utf8_to_wchar_path(oldpath);
|
|
newwpath=utf8_to_wchar_path(newpath);
|
|
retry:
|
|
ret=psync_bool_to_zero(MoveFileW(oldwpath, newwpath));
|
|
if (ret && GetLastError()==ERROR_SHARING_VIOLATION){
|
|
debug(D_WARNING, "file %s is locked by another process, will retry after sleep", oldpath);
|
|
psync_milisleep(PSYNC_SLEEP_ON_OS_LOCK);
|
|
goto retry;
|
|
}
|
|
psync_free(oldwpath);
|
|
psync_free(newwpath);
|
|
return ret;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_file_rename_overwrite(const char *oldpath, const char *newpath){
|
|
if (!psync_filename_cmp(oldpath, newpath))
|
|
return 0;
|
|
#if defined(P_OS_POSIX)
|
|
return rename(oldpath, newpath);
|
|
#elif defined(P_OS_WINDOWS) // should we just use rename() here?
|
|
else{
|
|
wchar_t *oldwpath, *newwpath;
|
|
int ret;
|
|
oldwpath=utf8_to_wchar_path(oldpath);
|
|
newwpath=utf8_to_wchar_path(newpath);
|
|
retry:
|
|
ret=psync_bool_to_zero(MoveFileExW(oldwpath, newwpath, MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING));
|
|
if (ret && GetLastError()==ERROR_SHARING_VIOLATION){
|
|
debug(D_WARNING, "file %s is locked by another process, will retry after sleep", oldpath);
|
|
psync_milisleep(PSYNC_SLEEP_ON_OS_LOCK);
|
|
goto retry;
|
|
}
|
|
else if (ret)
|
|
debug(D_WARNING, "rename from %s to %s failed with error %d", oldpath, newpath, (int)GetLastError());
|
|
psync_free(oldwpath);
|
|
psync_free(newwpath);
|
|
return ret;
|
|
}
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_file_delete(const char *path){
|
|
#if defined(P_OS_POSIX)
|
|
return unlink(path);
|
|
#elif defined(P_OS_WINDOWS)
|
|
wchar_t *wpath;
|
|
int ret;
|
|
wpath=utf8_to_wchar_path(path);
|
|
ret=psync_bool_to_zero(DeleteFileW(wpath));
|
|
psync_free(wpath);
|
|
return ret;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
psync_file_t psync_file_open(const char *path, int access, int flags){
|
|
#if defined(P_OS_POSIX)
|
|
int fd;
|
|
#if defined(O_CLOEXEC)
|
|
flags|=O_CLOEXEC;
|
|
#endif
|
|
#if defined(O_NOATIME)
|
|
flags|=O_NOATIME;
|
|
#endif
|
|
fd=open(path, access|flags, PSYNC_DEFAULT_POSIX_FILE_MODE);
|
|
if (unlikely(fd==-1)){
|
|
while (errno==EINTR){
|
|
debug(D_NOTICE, "got EINTR while opening file");
|
|
fd=open(path, access|flags, PSYNC_DEFAULT_POSIX_FILE_MODE);
|
|
if (fd!=-1)
|
|
return fd;
|
|
}
|
|
}
|
|
return fd;
|
|
#elif defined(P_OS_WINDOWS)
|
|
DWORD cdis;
|
|
wchar_t *wpath;
|
|
HANDLE ret;
|
|
if (flags&P_O_EXCL)
|
|
cdis=CREATE_NEW;
|
|
else if ((flags&(P_O_CREAT|P_O_TRUNC))==(P_O_CREAT|P_O_TRUNC))
|
|
cdis=CREATE_ALWAYS;
|
|
else if (flags&P_O_CREAT)
|
|
cdis=OPEN_ALWAYS;
|
|
else if (flags&P_O_TRUNC)
|
|
cdis=TRUNCATE_EXISTING;
|
|
else
|
|
cdis=OPEN_EXISTING;
|
|
wpath=utf8_to_wchar_path(path);
|
|
ret=CreateFileW(wpath, access, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, cdis, FILE_FLAG_POSIX_SEMANTICS|FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (IS_DEBUG && ret==INVALID_HANDLE_VALUE && GetLastError()!=ERROR_FILE_NOT_FOUND)
|
|
debug(D_WARNING, "could not open file %s, error %d", path, (int)GetLastError());
|
|
psync_free(wpath);
|
|
return ret;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_file_close(psync_file_t fd){
|
|
#if defined(P_OS_POSIX)
|
|
return close(fd);
|
|
#elif defined(P_OS_WINDOWS)
|
|
return psync_bool_to_zero(CloseHandle(fd));
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_file_sync(psync_file_t fd){
|
|
#if defined(F_FULLFSYNC) && defined(P_OS_POSIX)
|
|
if (unlikely(fcntl(fd, F_FULLFSYNC))){
|
|
while (errno==EINTR){
|
|
debug(D_NOTICE, "got EINTR while fsyncing file");
|
|
if (!fcntl(fd, F_FULLFSYNC))
|
|
return 0;
|
|
}
|
|
debug(D_NOTICE, "got error %d, when doing fcntl(F_FULLFSYNC), trying fsync()", (int)errno);
|
|
if (fsync(fd)){
|
|
debug(D_NOTICE, "fsync also failed, error %d", (int)errno);
|
|
return -1;
|
|
}
|
|
else{
|
|
debug(D_NOTICE, "fsync succeded");
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
return 0;
|
|
#elif defined(P_OS_POSIX)
|
|
#if _POSIX_SYNCHRONIZED_IO>0
|
|
if (unlikely(fdatasync(fd))){
|
|
#else
|
|
if (unlikely(fsync(fd))){
|
|
#endif
|
|
while (errno==EINTR){
|
|
debug(D_NOTICE, "got EINTR while fsyncing file");
|
|
if (!fsync(fd))
|
|
return 0;
|
|
}
|
|
debug(D_NOTICE, "got error %d", (int)errno);
|
|
return -1;
|
|
}
|
|
else
|
|
return 0;
|
|
#elif defined(P_OS_WINDOWS)
|
|
return psync_bool_to_zero(FlushFileBuffers(fd));
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_file_schedulesync(psync_file_t fd){
|
|
#if defined(P_OS_LINUX) && defined(SYNC_FILE_RANGE_WRITE)
|
|
return sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE);
|
|
#elif defined(P_OS_POSIX) && _POSIX_MAPPED_FILES>0 && _POSIX_SYNCHRONIZED_IO>0
|
|
struct stat st;
|
|
void *fmap;
|
|
int ret;
|
|
if (unlikely(fstat(fd, &st))){
|
|
debug(D_NOTICE, "fstat failed, errno=%d", (int)errno);
|
|
return -1;
|
|
}
|
|
fmap=mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (unlikely(fmap==MAP_FAILED)){
|
|
debug(D_NOTICE, "mmap failed, errno=%d", (int)errno);
|
|
return -1;
|
|
}
|
|
ret=msync(fmap, st.st_size, MS_ASYNC);
|
|
if (unlikely(ret))
|
|
debug(D_NOTICE, "msync failed, errno=%d", (int)errno);
|
|
munmap(fmap, st.st_size);
|
|
return ret;
|
|
#elif defined(P_OS_WINDOWS)
|
|
HANDLE mapping;
|
|
void *fmap;
|
|
int ret;
|
|
ret=-1;
|
|
mapping=CreateFileMapping(fd, NULL, PAGE_READWRITE, 0, 0, NULL);
|
|
if (mapping!=NULL){
|
|
fmap=MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
|
|
if (fmap!=NULL){
|
|
if (FlushViewOfFile(fmap, 0))
|
|
ret=0;
|
|
else
|
|
debug(D_NOTICE, "FlushViewOfFile failed, error %u", (unsigned)GetLastError());
|
|
UnmapViewOfFile(fmap);
|
|
}
|
|
else
|
|
debug(D_NOTICE, "MapViewOfFile failed, error %u", (unsigned)GetLastError());
|
|
CloseHandle(mapping);
|
|
}
|
|
else
|
|
debug(D_NOTICE, "CreateFileMapping failed, error %u", (unsigned)GetLastError());
|
|
return ret;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int psync_folder_sync(const char *path){
|
|
#if defined(P_OS_POSIX)
|
|
int fd, ret;
|
|
fd=open(path, O_RDONLY);
|
|
if (fd==-1){
|
|
debug(D_NOTICE, "could not open folder %s, error %d", path, (int)errno);
|
|
return -1;
|
|
}
|
|
if (unlikely(psync_file_sync(fd))){
|
|
debug(D_NOTICE, "could not fsync folder %s, error %d", path, (int)errno);
|
|
ret=-1;
|
|
}
|
|
else
|
|
ret=0;
|
|
close(fd);
|
|
return ret;
|
|
#elif defined(P_OS_WINDOWS)
|
|
wchar_t *wpath;
|
|
HANDLE fd;
|
|
int ret;
|
|
wpath=utf8_to_wchar_path(path);
|
|
fd=CreateFileW(wpath, 0, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_POSIX_SEMANTICS, NULL);
|
|
psync_free(wpath);
|
|
if (fd==INVALID_HANDLE_VALUE){
|
|
debug(D_NOTICE, "could not open folder %s", path);
|
|
return -1;
|
|
}
|
|
if (FlushFileBuffers(fd))
|
|
ret=0;
|
|
else{
|
|
debug(D_NOTICE, "could not flush folder %s err=%u", path, GetLastError());
|
|
ret=-1;
|
|
}
|
|
CloseHandle(fd);
|
|
return ret;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
psync_file_t psync_file_dup(psync_file_t fd){
|
|
#if defined(P_OS_POSIX)
|
|
return dup(fd);
|
|
#elif defined(P_OS_WINDOWS)
|
|
HANDLE process, fddup;
|
|
process=GetCurrentProcess();
|
|
if (!DuplicateHandle(process, fd, process, &fddup, 0, FALSE, DUPLICATE_SAME_ACCESS))
|
|
return INVALID_HANDLE_VALUE;
|
|
else
|
|
return fddup;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_file_set_creation(psync_file_t fd, time_t ctime){
|
|
#if defined(P_OS_WINDOWS)
|
|
FILETIME fctime;
|
|
uint64_t lctime;
|
|
lctime=Int32x32To64(ctime, 10000000)+116444736000000000;
|
|
fctime.dwLowDateTime=(DWORD)lctime;
|
|
fctime.dwHighDateTime=lctime>>32;
|
|
return psync_bool_to_zero(SetFileTime(fd, &fctime, NULL, NULL));
|
|
#elif defined(P_OS_MACOSX)
|
|
struct attrlist attr;
|
|
struct timespec crtime;
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.bitmapcount=ATTR_BIT_MAP_COUNT;
|
|
attr.commonattr=ATTR_CMN_CRTIME;
|
|
crtime.tv_sec=ctime;
|
|
crtime.tv_nsec=0;
|
|
return fsetattrlist(fd, &attr, &crtime, sizeof(struct timespec), FSOPT_NOFOLLOW);
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int psync_set_crtime_mtime(const char *path, time_t crtime, time_t mtime){
|
|
#if defined(P_OS_WINDOWS)
|
|
wchar_t *wpath;
|
|
FILETIME fctime, fmtime, *pfctime, *pfmtime;
|
|
uint64_t tm64;
|
|
HANDLE fd;
|
|
int ret;
|
|
wpath=utf8_to_wchar_path(path);
|
|
fd=CreateFileW(wpath, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_POSIX_SEMANTICS, NULL);
|
|
psync_free(wpath);
|
|
if (fd==INVALID_HANDLE_VALUE){
|
|
debug(D_NOTICE, "could not open file/folder %s", path);
|
|
return -1;
|
|
}
|
|
if (crtime){
|
|
tm64=Int32x32To64(crtime, 10000000)+116444736000000000;
|
|
fctime.dwLowDateTime=(DWORD)tm64;
|
|
fctime.dwHighDateTime=tm64>>32;
|
|
pfctime=&fctime;
|
|
}
|
|
else
|
|
pfctime=NULL;
|
|
if (mtime){
|
|
tm64=Int32x32To64(mtime, 10000000)+116444736000000000;
|
|
fmtime.dwLowDateTime=(DWORD)tm64;
|
|
fmtime.dwHighDateTime=tm64>>32;
|
|
pfmtime=&fmtime;
|
|
}
|
|
else
|
|
pfmtime=NULL;
|
|
ret=psync_bool_to_zero(SetFileTime(fd, pfctime, NULL, pfmtime));
|
|
CloseHandle(fd);
|
|
return ret;
|
|
#elif defined(P_OS_MACOSX)
|
|
if (crtime){
|
|
struct attrlist attr;
|
|
struct timespec crtimes;
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.bitmapcount=ATTR_BIT_MAP_COUNT;
|
|
attr.commonattr=ATTR_CMN_CRTIME;
|
|
crtimes.tv_sec=crtime;
|
|
crtimes.tv_nsec=0;
|
|
if (setattrlist(path, &attr, &crtimes, sizeof(struct timespec), FSOPT_NOFOLLOW))
|
|
return -1;
|
|
}
|
|
if (mtime){
|
|
struct timeval tm[2];
|
|
tm[0].tv_sec=mtime;
|
|
tm[0].tv_usec=0;
|
|
tm[1].tv_sec=mtime;
|
|
tm[1].tv_usec=0;
|
|
return utimes(path, tm);
|
|
}
|
|
return 0;
|
|
#elif defined(P_OS_POSIX)
|
|
if (mtime){
|
|
struct timeval tm[2];
|
|
tm[0].tv_sec=mtime;
|
|
tm[0].tv_usec=0;
|
|
tm[1].tv_sec=mtime;
|
|
tm[1].tv_usec=0;
|
|
if (unlikely(utimes(path, tm))){
|
|
debug(D_NOTICE, "got errno %d while setting modification time of %s to %lu: %s", errno, path, (unsigned long)mtime, strerror(errno));
|
|
return -1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
return 0;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int psync_set_crtime_mtime_by_fd(psync_file_t fd, const char *path, time_t crtime, time_t mtime){
|
|
#if defined(P_OS_WINDOWS)
|
|
FILETIME fctime, fmtime, *pfctime, *pfmtime;
|
|
uint64_t tm64;
|
|
int ret;
|
|
if (crtime){
|
|
tm64=Int32x32To64(crtime, 10000000)+116444736000000000;
|
|
fctime.dwLowDateTime=(DWORD)tm64;
|
|
fctime.dwHighDateTime=tm64>>32;
|
|
pfctime=&fctime;
|
|
}
|
|
else
|
|
pfctime=NULL;
|
|
if (mtime){
|
|
tm64=Int32x32To64(mtime, 10000000)+116444736000000000;
|
|
fmtime.dwLowDateTime=(DWORD)tm64;
|
|
fmtime.dwHighDateTime=tm64>>32;
|
|
pfmtime=&fmtime;
|
|
}
|
|
else
|
|
pfmtime=NULL;
|
|
ret=psync_bool_to_zero(SetFileTime(fd, pfctime, NULL, pfmtime));
|
|
return ret;
|
|
#elif defined(P_OS_MACOSX)
|
|
if (crtime){
|
|
struct attrlist attr;
|
|
struct timespec crtimes;
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.bitmapcount=ATTR_BIT_MAP_COUNT;
|
|
attr.commonattr=ATTR_CMN_CRTIME;
|
|
crtimes.tv_sec=crtime;
|
|
crtimes.tv_nsec=0;
|
|
if (fsetattrlist(fd, &attr, &crtimes, sizeof(struct timespec), FSOPT_NOFOLLOW))
|
|
return -1;
|
|
}
|
|
if (mtime){
|
|
struct timeval tm[2];
|
|
tm[0].tv_sec=mtime;
|
|
tm[0].tv_usec=0;
|
|
tm[1].tv_sec=mtime;
|
|
tm[1].tv_usec=0;
|
|
return futimes(fd, tm);
|
|
}
|
|
return 0;
|
|
#elif defined(_BSD_SOURCE) || defined(P_OS_BSD)
|
|
if (mtime){
|
|
struct timeval tm[2];
|
|
tm[0].tv_sec=mtime;
|
|
tm[0].tv_usec=0;
|
|
tm[1].tv_sec=mtime;
|
|
tm[1].tv_usec=0;
|
|
if (unlikely(futimes(fd, tm))){
|
|
debug(D_NOTICE, "got errno %d while setting modification time of %s to %lu: %s", errno, path, (unsigned long)mtime, strerror(errno));
|
|
return -1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
return 0;
|
|
#else
|
|
return psync_set_crtime_mtime(path, crtime, mtime);
|
|
#endif
|
|
}
|
|
|
|
typedef struct {
|
|
uint64_t offset;
|
|
size_t count;
|
|
psync_file_t fd;
|
|
} psync_file_preread_t;
|
|
|
|
static void psync_file_preread_thread(void *ptr){
|
|
char buff[16*1024];
|
|
psync_file_preread_t *pr;
|
|
ssize_t rd;
|
|
pr=(psync_file_preread_t *)ptr;
|
|
while (pr->count){
|
|
rd=psync_file_pread(pr->fd, buff, pr->count>sizeof(buff)?sizeof(buff):pr->count, pr->offset);
|
|
if (rd<=0)
|
|
break;
|
|
pr->offset+=rd;
|
|
pr->count-=rd;
|
|
}
|
|
psync_file_close(pr->fd);
|
|
psync_free(pr);
|
|
}
|
|
|
|
int psync_file_preread(psync_file_t fd, uint64_t offset, size_t count){
|
|
psync_file_preread_t *pr;
|
|
psync_file_t cfd;
|
|
cfd=psync_file_dup(fd);
|
|
if (cfd==INVALID_HANDLE_VALUE)
|
|
return -1;
|
|
pr=psync_new(psync_file_preread_t);
|
|
pr->offset=offset;
|
|
pr->count=count;
|
|
pr->fd=cfd;
|
|
psync_run_thread1("pre-read (readahead) thread", psync_file_preread_thread, pr);
|
|
return 0;
|
|
}
|
|
|
|
int psync_file_readahead(psync_file_t fd, uint64_t offset, size_t count){
|
|
#if defined(P_OS_POSIX) && defined(POSIX_FADV_WILLNEED)
|
|
return posix_fadvise(fd, offset, count, POSIX_FADV_WILLNEED);
|
|
#elif defined(P_OS_POSIX) && defined(F_RDADVISE)
|
|
struct radvisory ra;
|
|
ra.ra_offset=offset;
|
|
ra.ra_count=count;
|
|
return fcntl(fd, F_RDADVISE, &ra);
|
|
#else
|
|
return psync_file_preread(fd, offset, count);
|
|
#endif
|
|
}
|
|
|
|
ssize_t psync_file_read(psync_file_t fd, void *buf, size_t count){
|
|
#if defined(P_OS_POSIX)
|
|
ssize_t ret;
|
|
ret=read(fd, buf, count);
|
|
if (unlikely(ret==-1)){
|
|
while (errno==EINTR){
|
|
debug(D_NOTICE, "got EINTR while reading from file");
|
|
ret=read(fd, buf, count);
|
|
if (ret!=-1)
|
|
return ret;
|
|
}
|
|
debug(D_NOTICE, "got error %d", (int)errno);
|
|
}
|
|
return ret;
|
|
#elif defined(P_OS_WINDOWS)
|
|
DWORD ret;
|
|
if (ReadFile(fd, buf, count, &ret, NULL))
|
|
return ret;
|
|
else
|
|
return -1;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
ssize_t psync_file_pread(psync_file_t fd, void *buf, size_t count, uint64_t offset){
|
|
#if defined(P_OS_POSIX)
|
|
ssize_t ret;
|
|
ret=pread(fd, buf, count, offset);
|
|
if (unlikely(ret==-1)){
|
|
while (errno==EINTR){
|
|
debug(D_NOTICE, "got EINTR while writing to file");
|
|
ret=pread(fd, buf, count, offset);
|
|
if (ret!=-1)
|
|
return ret;
|
|
}
|
|
debug(D_NOTICE, "got error %d", (int)errno);
|
|
}
|
|
return ret;
|
|
#elif defined(P_OS_WINDOWS)
|
|
OVERLAPPED ov;
|
|
LARGE_INTEGER li;
|
|
DWORD ret;
|
|
li.QuadPart=offset;
|
|
memset(&ov, 0, sizeof(ov));
|
|
ov.Offset=li.LowPart;
|
|
ov.OffsetHigh=li.HighPart;
|
|
if (ReadFile(fd, buf, count, &ret, &ov))
|
|
return ret;
|
|
else{
|
|
if (GetLastError()==ERROR_HANDLE_EOF)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
ssize_t psync_file_write(psync_file_t fd, const void *buf, size_t count){
|
|
#if defined(P_OS_POSIX)
|
|
ssize_t ret;
|
|
ret=write(fd, buf, count);
|
|
if (unlikely(ret==-1)){
|
|
while (errno==EINTR){
|
|
debug(D_NOTICE, "got EINTR while writing to file");
|
|
ret=write(fd, buf, count);
|
|
if (ret!=-1)
|
|
return ret;
|
|
|
|
}
|
|
debug(D_NOTICE, "got error %d", (int)errno);
|
|
}
|
|
return ret;
|
|
#elif defined(P_OS_WINDOWS)
|
|
DWORD ret;
|
|
if (WriteFile(fd, buf, count, &ret, NULL))
|
|
return ret;
|
|
else
|
|
return -1;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
ssize_t psync_file_pwrite(psync_file_t fd, const void *buf, size_t count, uint64_t offset){
|
|
#if defined(P_OS_POSIX)
|
|
ssize_t ret;
|
|
ret=pwrite(fd, buf, count, offset);
|
|
if (unlikely(ret==-1)){
|
|
while (errno==EINTR){
|
|
debug(D_NOTICE, "got EINTR while writing to file");
|
|
ret=pwrite(fd, buf, count, offset);
|
|
if (ret!=-1)
|
|
return ret;
|
|
}
|
|
debug(D_NOTICE, "got error %d", (int)errno);
|
|
}
|
|
return ret;
|
|
#elif defined(P_OS_WINDOWS)
|
|
OVERLAPPED ov;
|
|
LARGE_INTEGER li;
|
|
DWORD ret;
|
|
li.QuadPart=offset;
|
|
memset(&ov, 0, sizeof(ov));
|
|
ov.Offset=li.LowPart;
|
|
ov.OffsetHigh=li.HighPart;
|
|
if (WriteFile(fd, buf, count, &ret, &ov))
|
|
return ret;
|
|
else
|
|
return -1;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int64_t psync_file_seek(psync_file_t fd, uint64_t offset, int whence){
|
|
#if defined(P_OS_POSIX)
|
|
return lseek(fd, offset, whence);
|
|
#elif defined(P_OS_WINDOWS)
|
|
LARGE_INTEGER li;
|
|
li.QuadPart=offset;
|
|
li.LowPart=SetFilePointer(fd, li.LowPart, &li.HighPart, whence);
|
|
if (li.LowPart==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR){
|
|
debug(D_WARNING, "got error %d from SetFilePointer", (int)GetLastError());
|
|
return -1;
|
|
}
|
|
else
|
|
return li.QuadPart;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int psync_file_truncate(psync_file_t fd){
|
|
#if defined(P_OS_POSIX)
|
|
off_t off;
|
|
off=lseek(fd, 0, SEEK_CUR);
|
|
if (likely_log(off!=(off_t)-1)){
|
|
if (unlikely(ftruncate(fd, off))){
|
|
while (errno==EINTR){
|
|
debug(D_NOTICE, "got EINTR while truncating file");
|
|
if (!ftruncate(fd, off))
|
|
return 0;
|
|
}
|
|
debug(D_NOTICE, "got error %d", (int)errno);
|
|
return -1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
return -1;
|
|
#elif defined(P_OS_WINDOWS)
|
|
#if IS_DEBUG
|
|
if (SetEndOfFile(fd))
|
|
return 0;
|
|
else{
|
|
debug(D_WARNING, "got error %d from SetEndOfFile", (int)GetLastError());
|
|
return -1;
|
|
}
|
|
#else
|
|
return psync_bool_to_zero(SetEndOfFile(fd));
|
|
#endif
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
int64_t psync_file_size(psync_file_t fd){
|
|
#if defined(P_OS_POSIX)
|
|
struct stat st;
|
|
if (unlikely_log(fstat(fd, &st)))
|
|
return -1;
|
|
else
|
|
return st.st_size;
|
|
#elif defined(P_OS_WINDOWS)
|
|
ULARGE_INTEGER li;
|
|
li.LowPart=GetFileSize(fd, &li.HighPart);
|
|
if (unlikely_log(li.LowPart==INVALID_FILE_SIZE && GetLastError()!=NO_ERROR))
|
|
return -1;
|
|
else
|
|
return li.QuadPart;
|
|
#else
|
|
#error "Function not implemented for your operating system"
|
|
#endif
|
|
}
|
|
|
|
void psync_set_software_name(const char *snm){
|
|
psync_software_name=snm;
|
|
}
|
|
|
|
void psync_set_os_name(const char *osnm){
|
|
psync_os_name=osnm;
|
|
}
|
|
|
|
char *psync_deviceos(){
|
|
return psync_os_name?psync_strdup(psync_os_name):psync_deviceid();
|
|
}
|
|
|
|
char *psync_device_string(){
|
|
#if defined(P_OS_LINUX)
|
|
char *osname=psync_deviceos();
|
|
char *ret = psync_strcat(osname, ", ", psync_software_name, NULL);
|
|
free(osname);
|
|
return ret;
|
|
|
|
#endif
|
|
return psync_strcat(psync_deviceid(), ", ", psync_software_name, NULL);
|
|
}
|
|
|
|
const char *psync_appname(){
|
|
return psync_software_name;
|
|
}
|
|
|
|
char *psync_deviceid(){
|
|
char *device;
|
|
#if defined(P_OS_WINDOWS)
|
|
SYSTEM_POWER_STATUS bat;
|
|
const char *hardware, *ver;
|
|
char versbuff[32];
|
|
DWORD vers, vmajor, vminor;
|
|
if (GetSystemMetrics(SM_TABLETPC))
|
|
hardware="Tablet";
|
|
else if (GetSystemPowerStatus(&bat) || (bat.BatteryFlag&128))
|
|
hardware="Desktop";
|
|
else
|
|
hardware="Laptop";
|
|
vers=GetVersion();
|
|
vmajor=(DWORD)(LOBYTE(LOWORD(vers)));
|
|
vminor=(DWORD)(HIBYTE(LOWORD(vers)));
|
|
if (vmajor==6){
|
|
switch (vminor){
|
|
case 3: ver="8.1"; break;
|
|
case 2: ver="8.0"; break;
|
|
case 1: ver="7.0"; break;
|
|
case 0: ver="Vista"; break;
|
|
default: psync_slprintf(versbuff, sizeof(versbuff), "6.%u", (unsigned int)vminor); ver=versbuff;
|
|
}
|
|
}
|
|
else if (vmajor==5){
|
|
switch (vminor){
|
|
case 2: ver="XP 64bit"; break;
|
|
case 1: ver="XP"; break;
|
|
case 0: ver="2000"; break;
|
|
default: psync_slprintf(versbuff, sizeof(versbuff), "5.%u", (unsigned int)vminor); ver=versbuff;
|
|
}
|
|
}
|
|
else if (vmajor==10){
|
|
switch (vminor){
|
|
case 0: ver="10.0"; break;
|
|
default: psync_slprintf(versbuff, sizeof(versbuff), "10.%u", (unsigned int)vminor); ver=versbuff;
|
|
}
|
|
}
|
|
else{
|
|
psync_slprintf(versbuff, sizeof(versbuff), "%u.%u", (unsigned int)vmajor, (unsigned int)vminor);
|
|
ver=versbuff;
|
|
}
|
|
device=psync_strcat(hardware, ", Windows ", ver, NULL);
|
|
#elif defined(P_OS_MACOSX)
|
|
struct utsname un;
|
|
const char *ver;
|
|
size_t len;
|
|
char versbuff[64], modelname[256];
|
|
int v;
|
|
if (uname(&un))
|
|
ver="Mac OS X";
|
|
else{
|
|
v=atoi(un.release);
|
|
switch (v){
|
|
case 16: ver="macOS 10.12 Sierra"; break;
|
|
case 15: ver="OS X 10.11 El Capitan"; break;
|
|
case 14: ver="OS X 10.10 Yosemite"; break;
|
|
case 13: ver="OS X 10.9 Mavericks"; break;
|
|
case 12: ver="OS X 10.8 Mountain Lion"; break;
|
|
case 11: ver="OS X 10.7 Lion"; break;
|
|
case 10: ver="OS X 10.6 Snow Leopard"; break;
|
|
default: psync_slprintf(versbuff, sizeof(versbuff), "Mac/Darwin %s", un.release); ver=versbuff;
|
|
}
|
|
}
|
|
len=sizeof(modelname);
|
|
if (sysctlbyname("hw.model", modelname, &len, NULL, 0))
|
|
psync_strlcpy(modelname, "Mac", sizeof(modelname));
|
|
versbuff[sizeof(versbuff)-1]=0;
|
|
device=psync_strcat(modelname, ", ", ver, NULL);
|
|
#elif defined(P_OS_LINUX)
|
|
DIR *dh;
|
|
struct dirent entry, *de;
|
|
const char *hardware;
|
|
char *path, buf[8];
|
|
int fd;
|
|
hardware="Desktop";
|
|
dh=opendir("/sys/class/power_supply");
|
|
if (dh){
|
|
while (!readdir_r(dh, &entry, &de) && de)
|
|
if (de->d_name[0]!='.' || (de->d_name[1]!=0 && (de->d_name[1]!='.' || de->d_name[2]!=0))){
|
|
path=psync_strcat("/sys/class/power_supply/", de->d_name, "/type", NULL);
|
|
fd=open(path, O_RDONLY);
|
|
psync_free(path);
|
|
if (fd==-1)
|
|
continue;
|
|
if (read(fd, buf, 7)==7 && !memcmp(buf, "Battery", 7)){
|
|
close(fd);
|
|
hardware="Laptop";
|
|
break;
|
|
}
|
|
close(fd);
|
|
}
|
|
closedir(dh);
|
|
}
|
|
device=psync_strcat(hardware, ", Linux", NULL);
|
|
#else
|
|
device=psync_strcat("Desktop", NULL);
|
|
#endif
|
|
debug(D_NOTICE, "detected device: %s", device);
|
|
return device;
|
|
}
|
|
|
|
int psync_run_update_file(const char *path){
|
|
#if defined(P_OS_LINUX) || defined(P_OS_MACOSX)
|
|
#if defined(P_OS_LINUX)
|
|
#if defined(P_OS_DEBIAN)
|
|
#define PSYNC_RUN_CMD "/usr/lib/psyncgui/debinstall.sh"
|
|
#else
|
|
#define PSYNC_RUN_CMD "qapt-deb-installer"
|
|
#endif
|
|
#else
|
|
#define PSYNC_RUN_CMD "open"
|
|
#endif
|
|
pid_t pid;
|
|
debug(D_NOTICE, "running %s with "PSYNC_RUN_CMD, path);
|
|
pid=fork();
|
|
if (unlikely(pid==-1)){
|
|
debug(D_ERROR, "fork failed");
|
|
return -1;
|
|
}
|
|
else if (pid){
|
|
int status;
|
|
psync_milisleep(100);
|
|
if (waitpid(pid, &status, WNOHANG)==0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
else{
|
|
char *ex;
|
|
int fd;
|
|
fd=open("/dev/null", O_RDWR);
|
|
dup2(fd, STDIN_FILENO);
|
|
dup2(fd, STDOUT_FILENO);
|
|
dup2(fd, STDERR_FILENO);
|
|
close(fd);
|
|
setsid();
|
|
ex=psync_strcat(PSYNC_RUN_CMD" \"", path, "\"", NULL);
|
|
execl("/bin/sh", "/bin/sh", "-c", ex, NULL);
|
|
debug(D_ERROR, "exec of %s failed", ex);
|
|
psync_free(ex);
|
|
exit(1);
|
|
}
|
|
#elif defined(P_OS_WINDOWS)
|
|
if ((int)ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT)>32)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int psync_invalidate_os_cache_needed(){
|
|
#if defined(P_OS_WINDOWS)
|
|
return 1;
|
|
#elif defined(P_OS_MACOSX)
|
|
return 0;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
#define REBUILD_ICON_BUFFER_SIZE 1024
|
|
|
|
extern int overlays_running;
|
|
|
|
#if defined(P_OS_WINDOWS)
|
|
void psync_rebuild_icons(){
|
|
TCHAR buf[REBUILD_ICON_BUFFER_SIZE] = { 0 };
|
|
HKEY hRegKey = 0;
|
|
DWORD dwRegValue;
|
|
DWORD dwRegValueTemp;
|
|
DWORD dwSize;
|
|
DWORD_PTR dwResult;
|
|
LONG lRegResult;
|
|
int result = 0;
|
|
|
|
// we're going to change the Shell Icon Size value
|
|
const TCHAR* sRegValueName = L"Shell Icon Size";
|
|
|
|
if (!overlays_running)
|
|
return;
|
|
|
|
lRegResult = RegOpenKeyEx(HKEY_CURRENT_USER, L"Control Panel\\Desktop\\WindowMetrics",
|
|
0, KEY_READ | KEY_WRITE, &hRegKey);
|
|
if (lRegResult != ERROR_SUCCESS)
|
|
goto Cleanup;
|
|
|
|
// Read registry value
|
|
dwSize = REBUILD_ICON_BUFFER_SIZE;
|
|
lRegResult = RegQueryValueEx(hRegKey, sRegValueName, NULL, NULL,
|
|
(LPBYTE)buf, &dwSize);
|
|
if (lRegResult != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
// If registry key doesn't exist create it using system current setting
|
|
int iDefaultIconSize = GetSystemMetrics(SM_CXICON);
|
|
if (0 == iDefaultIconSize)
|
|
iDefaultIconSize = 32;
|
|
_snprintf(buf, REBUILD_ICON_BUFFER_SIZE, L"%d", iDefaultIconSize);
|
|
}
|
|
else if (lRegResult != ERROR_SUCCESS)
|
|
goto Cleanup;
|
|
|
|
// Change registry value
|
|
dwRegValue = _wtoi(buf);
|
|
dwRegValueTemp = dwRegValue - 1;
|
|
|
|
dwSize = _snprintf(buf, REBUILD_ICON_BUFFER_SIZE, L"%lu", dwRegValueTemp) + sizeof(TCHAR);
|
|
lRegResult = RegSetValueEx(hRegKey, sRegValueName, 0, REG_SZ,
|
|
(LPBYTE)buf, dwSize);
|
|
if (lRegResult != ERROR_SUCCESS)
|
|
goto Cleanup;
|
|
|
|
|
|
// Update all windows
|
|
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
|
|
0, SMTO_ABORTIFHUNG, 5000, &dwResult);
|
|
|
|
// Reset registry value
|
|
dwSize = _snprintf(buf, REBUILD_ICON_BUFFER_SIZE, L"%lu", dwRegValue) + sizeof(TCHAR);
|
|
lRegResult = RegSetValueEx(hRegKey, sRegValueName, 0, REG_SZ,
|
|
(LPBYTE)buf, dwSize);
|
|
if (lRegResult != ERROR_SUCCESS)
|
|
goto Cleanup;
|
|
|
|
// Update all windows
|
|
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
|
|
0, SMTO_ABORTIFHUNG, 5000, &dwResult);
|
|
|
|
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
|
|
|
|
result = 1;
|
|
|
|
Cleanup:
|
|
if (hRegKey != 0)
|
|
{
|
|
RegCloseKey(hRegKey);
|
|
}
|
|
return;
|
|
}
|
|
#elif defined(P_OS_MACOSX)
|
|
void psync_rebuild_icons(){
|
|
int ret = 0;
|
|
|
|
if (!overlays_running)
|
|
return;
|
|
|
|
debug(D_NOTICE, "Stopping finder plugin to refresh all icons.");
|
|
ret = system("/bin/sh -c \"pluginkit -e ignore -i com.pcloud.pcloud.macos.pCloudFinderExt;sleep 0.5;pluginkit -e use -i com.pcloud.pcloud.macos.pCloudFinderExt;\"");
|
|
debug(D_ERROR, "Reseting Finder Ext");
|
|
}
|
|
#else
|
|
void psync_rebuild_icons(){
|
|
if (!overlays_running)
|
|
return;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
int psync_invalidate_os_cache(const char *path){
|
|
#if defined(P_OS_WINDOWS)
|
|
wchar_t *wpath;
|
|
debug(D_NOTICE, "got invalidate for path %s", path);
|
|
wpath=utf8_to_wchar_path(path);
|
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, wpath, NULL);
|
|
psync_free(wpath);
|
|
return 0;
|
|
#elif defined(P_OS_MACOSX)
|
|
int pfds[2];
|
|
pid_t pid;
|
|
debug(D_NOTICE, "running osascript to refresh finder");
|
|
if (unlikely(pipe(pfds))){
|
|
debug(D_ERROR, "pipe failed");
|
|
return -1;
|
|
}
|
|
pid=fork();
|
|
if (unlikely(pid==-1)){
|
|
close(pfds[0]);
|
|
close(pfds[1]);
|
|
debug(D_ERROR, "fork failed");
|
|
return -1;
|
|
}
|
|
else if (pid){
|
|
const char *cmd="tell application \"Finder\"\n\
|
|
repeat with i from 1 to count of Finder windows\n\
|
|
tell window i\n\
|
|
try\n\
|
|
update every item in every folder with necessity\n\
|
|
end try\n\
|
|
end tell\n\
|
|
end repeat\n\
|
|
end tell\n";
|
|
int status;
|
|
close(pfds[0]);
|
|
status=strlen(cmd);
|
|
if (write(pfds[1], cmd, status)!=status || close(pfds[1])!=0){
|
|
debug(D_ERROR, "write to pipe failed");
|
|
kill(pid, SIGKILL);
|
|
waitpid(pid, &status, 0);
|
|
return -1;
|
|
}
|
|
if (waitpid(pid, &status, 0)==0 && WIFEXITED(status) && WEXITSTATUS(status)==0){
|
|
debug(D_NOTICE, "execution of osascript succeded");
|
|
return 0;
|
|
}
|
|
else{
|
|
debug(D_ERROR, "execution of osascript failed");
|
|
return -1;
|
|
}
|
|
}
|
|
else{
|
|
int fd;
|
|
close(pfds[1]);
|
|
dup2(pfds[0], STDIN_FILENO);
|
|
close(pfds[0]);
|
|
fd=open("/dev/null", O_RDWR);
|
|
dup2(fd, STDOUT_FILENO);
|
|
dup2(fd, STDERR_FILENO);
|
|
close(fd);
|
|
execl("/bin/sh", "/bin/sh", "-c", "osascript", NULL);
|
|
debug(D_ERROR, "exec of \"/bin/sh -c osascript\" failed");
|
|
exit(1);
|
|
}
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void *psync_mmap_anon(size_t size){
|
|
#if defined(PSYNC_MAP_ANONYMOUS)
|
|
return mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|PSYNC_MAP_ANONYMOUS, -1, 0);
|
|
#elif defined(P_OS_WINDOWS)
|
|
return VirtualAlloc(NULL, size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
|
|
#else
|
|
return malloc(size);
|
|
#endif
|
|
}
|
|
|
|
PSYNC_NOINLINE static void *psync_mmap_anon_emergency(size_t size){
|
|
void *ret;
|
|
debug(D_WARNING, "could not allocate %lu bytes", (unsigned long)size);
|
|
psync_try_free_memory();
|
|
ret=psync_mmap_anon(size);
|
|
if (likely(ret))
|
|
return ret;
|
|
else{
|
|
debug(D_CRITICAL, "could not allocate %lu bytes even after freeing some memory, aborting", (unsigned long)size);
|
|
abort();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void *psync_mmap_anon_safe(size_t size){
|
|
void *ret;
|
|
ret=psync_mmap_anon(size);
|
|
if (likely(ret))
|
|
return ret;
|
|
else
|
|
return psync_mmap_anon_emergency(size);
|
|
}
|
|
|
|
int psync_munmap_anon(void *ptr, size_t size){
|
|
#if defined(PSYNC_MAP_ANONYMOUS)
|
|
return munmap(ptr, size);
|
|
#elif defined(P_OS_WINDOWS)
|
|
return psync_bool_to_zero(VirtualFree(ptr, 0, MEM_RELEASE));
|
|
#else
|
|
free(ptr);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void psync_anon_reset(void *ptr, size_t size){
|
|
#if defined(PSYNC_MAP_ANONYMOUS) && defined(MADV_DONTNEED)
|
|
madvise(ptr, size, MADV_DONTNEED);
|
|
#elif defined(P_OS_WINDOWS)
|
|
VirtualAlloc(ptr, size, MEM_RESET, PAGE_READWRITE);
|
|
#endif
|
|
}
|
|
|
|
int psync_mlock(void *ptr, size_t size){
|
|
#if defined(_POSIX_MEMLOCK_RANGE)
|
|
return mlock(ptr, size);
|
|
#elif defined(P_OS_WINDOWS)
|
|
return psync_bool_to_zero(VirtualLock(ptr, size));
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int psync_munlock(void *ptr, size_t size){
|
|
#if defined(_POSIX_MEMLOCK_RANGE)
|
|
return munlock(ptr, size);
|
|
#elif defined(P_OS_WINDOWS)
|
|
return psync_bool_to_zero(VirtualUnlock(ptr, size));
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int psync_get_page_size(){
|
|
return psync_page_size;
|
|
}
|