/* 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 #include #include #include #include #include "pcompat.h" #include "psynclib.h" #include "plibs.h" #include "psettings.h" #include "pssl.h" #include "ptimer.h" #if defined(P_OS_LINUX) #include #endif #if defined(P_OS_POSIX) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern char **environ; #if defined(MAP_ANONYMOUS) #define PSYNC_MAP_ANONYMOUS MAP_ANONYMOUS #elif defined(MAP_ANON) #define PSYNC_MAP_ANONYMOUS MAP_ANON #endif #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]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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 }; int psync_user_is_admin(){ return 0; } 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 #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; ist_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(){ 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); } static char * psync_get_pcloud_path_nc(){ 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); } 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(){ 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); } void psync_yield_cpu(){ sched_yield(); } 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){ struct timespec tm; tm.tv_sec=millisec/1000; tm.tv_nsec=(millisec%1000)*1000000; nanosleep(&tm, NULL); } void psync_milisleep(uint64_t millisec){ psync_check_no_sql_lock(millisec); psync_milisleep_nosqlcheck(millisec); } time_t psync_time(){ #if 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){ clock_gettime(CLOCK_REALTIME, tm); } 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; icolumn_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; i0 && 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 #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=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 (likely_log(sock!=INVALID_SOCKET)){ #if defined(PSOCK_NEED_NOBLOCK) fcntl(sock, F_SETFD, FD_CLOEXEC); fcntl(sock, F_SETFL, fcntl(sock, F_GETFL)|O_NONBLOCK); #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); 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; iai_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 (unlikely(rc!=0)) res=NULL; psync_task_complete(h, res); } #if defined(PSYNC_HAS_PROXY_CODE) static int recent_detect(){ static time_t lastdetect=0; if (psync_timer_time()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; #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 (brssl, (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 (brpending) 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 (brssl, (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 (brbuffer) 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 (brssl, (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 (brpending) 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 (brbuffer) 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 (brbuffer) 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; 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; 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){ 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){ return read(pfd, buff, num); } int psync_pipe_write(psync_socket_t pfd, const void *buff, int num){ return write(pfd, buff, num); } 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=max) max=sockets[i]+1; } i=select(max, &rfds, NULL, NULL, ptv); if (i>0){ for (i=0; id_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))){ 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; } int psync_list_dir_fast(const char *path, psync_list_dir_callback_fast callback, void *ptr){ 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 (namelend_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; } int64_t psync_get_free_space_by_path(const char *path){ struct statvfs buf; if (unlikely_log(statvfs(path, &buf))) return -1; else return (int64_t)buf.f_bavail*(int64_t)buf.f_frsize; } int psync_mkdir(const char *path){ return mkdir(path, PSYNC_DEFAULT_POSIX_FOLDER_MODE); } int psync_rmdir(const char *path){ return rmdir(path); } int psync_file_rename(const char *oldpath, const char *newpath){ return rename(oldpath, newpath); } int psync_file_rename_overwrite(const char *oldpath, const char *newpath){ if (!psync_filename_cmp(oldpath, newpath)) return 0; return rename(oldpath, newpath); } int psync_file_delete(const char *path){ return unlink(path); } psync_file_t psync_file_open(const char *path, int access, int flags){ 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; } int psync_file_close(psync_file_t fd){ return close(fd); } 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; #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; #else return 0; #endif } int psync_folder_sync(const char *path){ 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; } psync_file_t psync_file_dup(psync_file_t fd){ return dup(fd); } int psync_file_set_creation(psync_file_t fd, time_t ctime){ return -1; } int psync_set_crtime_mtime(const char *path, time_t crtime, time_t mtime){ #if 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(_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){ 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; } ssize_t psync_file_pread(psync_file_t fd, void *buf, size_t count, uint64_t offset){ 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; } ssize_t psync_file_write(psync_file_t fd, const void *buf, size_t count){ 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; } ssize_t psync_file_pwrite(psync_file_t fd, const void *buf, size_t count, uint64_t offset){ 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; } int64_t psync_file_seek(psync_file_t fd, uint64_t offset, int whence){ return lseek(fd, offset, whence); } int psync_file_truncate(psync_file_t fd){ 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; } int64_t psync_file_size(psync_file_t fd){ struct stat st; if (unlikely_log(fstat(fd, &st))) return -1; else return st.st_size; } 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_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; } #define PSYNC_RUN_CMD "xdg-open" // XXX: verify this... int psync_run_update_file(const char *path){ #if defined(P_OS_LINUX) 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); } #else return -1; #endif } int psync_invalidate_os_cache_needed(){ return 0; } #define REBUILD_ICON_BUFFER_SIZE 1024 extern int overlays_running; void psync_rebuild_icons(){ if (!overlays_running) return; return; } int psync_invalidate_os_cache(const char *path){ return 0; } 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); #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); #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); #endif } int psync_mlock(void *ptr, size_t size){ #if defined(_POSIX_MEMLOCK_RANGE) return mlock(ptr, size); #else return -1; #endif } int psync_munlock(void *ptr, size_t size){ #if defined(_POSIX_MEMLOCK_RANGE) return munlock(ptr, size); #else return -1; #endif } int psync_get_page_size(){ return psync_page_size; }