├── COPYRIGHT ├── libpcs_io ├── pcs_eventfd.h ├── pcs_upipe.h ├── crc64.h ├── libpcs_io.pc ├── adler32.h ├── pcs_cpuid.h ├── user.h ├── pcs_random.h ├── pcs_iocp.h ├── pcs_fd_gc.h ├── pcs_signal.h ├── pcs_errno.h ├── pcs_rdma_conn.h ├── pcs_rdma_listen.h ├── pcs_net_utils.h ├── lz4_estimate.h ├── parse_diskspace.h ├── pcs_atexit.h ├── pcs_event_ioconn.h ├── pcs_sock_conn.h ├── std_list.c ├── pcs_poll.h ├── libpcs_io.supp ├── pcs_sock_listen.h ├── qselect.h ├── pcs_ioconn.h ├── pcs_watchdog.h ├── md5.h ├── pcs_ucontext.h ├── pcs_sock_ssl_priv.h ├── pcs_align.h ├── parse_diskspace.c ├── regdump.h ├── pcs_atexit.c ├── pcs_numeric.h ├── pcs_shaper.c ├── pcs_sock.h ├── pcs_shaper.h ├── ssl_helpers.h ├── pcs_file_job.h ├── crc32.h ├── pcs_signalfd.h ├── logrotate.h ├── pcs_exec.h ├── pcs_dir.h ├── jhash.h ├── minheap.h ├── bug.h ├── pcs_profiler.h ├── crc32_arm_wrapper.c ├── pcs_rdma_int.h ├── pcs_thread.h ├── timer.h ├── pcs_ioconn.c ├── pool_allocator.h ├── bug.c ├── qselect.c ├── adler32.c ├── pcs_iocp.c ├── pcs_config_file.h ├── pcs_config.h ├── pcs_cpuid.c ├── pcs_aio.h ├── pcs_rdma_prot.h ├── pcs_net_addr.h ├── pcs_types.h ├── pcs_compat.c ├── pcs_endian.h ├── Makefile ├── pcs_sun_port.c ├── pcs_atomic.h ├── pcs_sync_io.h ├── pcs_epoll.c ├── pcs_compat.h ├── pcs_sync_ioreq.h ├── pcs_mr_malloc.h ├── pcs_kqueue.c ├── pcs_splice.h ├── .gdbinit ├── pcs_rdma_io.h ├── pcs_winapi.c ├── pcs_watchdog.c ├── pcs_random.c ├── pcs_event_ioconn.c ├── pcs_sock_conn.c ├── user.c └── pcs_sock_io.h ├── LICENSE ├── README.md ├── Makefile └── Makefile.inc /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright © 2003-2018 Acronis International GmbH. 2 | -------------------------------------------------------------------------------- /libpcs_io/pcs_eventfd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_EVENT_FD_H_INCLUDED 6 | #define _PCS_EVENT_FD_H_INCLUDED 7 | 8 | #include "pcs_config.h" 9 | 10 | #ifdef HAVE_EVENTFD 11 | #include 12 | #endif 13 | 14 | #endif /* _PCS_EVENT_FD_H_INCLUDED */ 15 | -------------------------------------------------------------------------------- /libpcs_io/pcs_upipe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #pragma once 6 | 7 | #include "pcs_types.h" 8 | 9 | struct pcs_co_file; 10 | 11 | PCS_API void pcs_co_upipe(struct pcs_co_file **in_file, struct pcs_co_file **out_file, u32 buf_sz); 12 | PCS_API void pcs_co_upipe_duplex(struct pcs_co_file **file1, struct pcs_co_file **file2, u32 buf_sz); 13 | -------------------------------------------------------------------------------- /libpcs_io/crc64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __CRC64_H__ 6 | #define __CRC64_H__ 7 | 8 | #include "pcs_types.h" 9 | 10 | /* 11 | * Performance: 12 | * CRC64 (software only): 2000 MB/sec 13 | * CRC64 (Intel ISA-L library): 9200 MB/sec 14 | */ 15 | PCS_API uint64_t pcs_crc64(uint64_t crc, const void *buf, unsigned int len); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /libpcs_io/libpcs_io.pc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | prefix=/usr 6 | exec_prefix=/usr 7 | libdir=/usr/lib64 8 | includedir=${prefix}/include 9 | 10 | Name: libpcs_io 11 | Version: __VERSION 12 | Description: Vstorage core library 13 | Libs: -L${libdir} -lpcs_io -lz _PKG_CFG_LDFLAGS 14 | Cflags: -I${includedir}/pcs-core -I${includedir}/pcs-core/proto _PKG_CFG_CFLAGS 15 | -------------------------------------------------------------------------------- /libpcs_io/adler32.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __ADLER32_H__ 6 | #define __ADLER32_H__ 7 | 8 | /* 64bit version. ~6GB/sec on i7 2.3Ghz */ 9 | unsigned int fast_adler32(unsigned int initial, const unsigned char* buf, unsigned int size); 10 | 11 | /* zlib version. just for testing. ~2GB/sec on i7 2.3Ghz */ 12 | unsigned int zlib_adler32(unsigned int adler, const unsigned char *buf, unsigned int len); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /libpcs_io/pcs_cpuid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_CPUID_H__ 6 | #define __PCS_CPUID_H__ 7 | 8 | #include "pcs_types.h" 9 | 10 | PCS_API int pcs_is_crc32_sse_supported(void); 11 | PCS_API int pcs_is_avx2_supported(void); 12 | PCS_API int pcs_is_aesni_supported(void); 13 | 14 | PCS_API int pcs_cpu_is_neon_supported(void); 15 | PCS_API int pcs_cpu_is_arm_crc_supported(void); 16 | PCS_API int pcs_cpu_is_arm_pmull_supported(void); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /libpcs_io/user.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _USER_H 6 | #define _USER_H 7 | 8 | #include "pcs_types.h" 9 | 10 | #ifndef __WINDOWS__ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #endif /* __WINDOWS__ */ 16 | 17 | int is_dir_user_valid(const char *dir); 18 | int is_root(void); 19 | struct passwd *get_user(const char *user); 20 | struct group *get_group(const char *group); 21 | int set_user_if_root(const char *user, const char *group); 22 | 23 | #endif /* _USER_H */ 24 | -------------------------------------------------------------------------------- /libpcs_io/pcs_random.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _RANDOM_H_ 6 | #define _RANDOM_H_ 1 7 | 8 | #include "pcs_types.h" 9 | 10 | struct pcs_rng 11 | { 12 | u64 data[312]; 13 | unsigned pos; 14 | }; 15 | 16 | /* Fill buffer with pseudo random content. Returns 0 on success and -1 otherwise */ 17 | PCS_API int pcs_get_urandom(void *buf, int sz); 18 | PCS_API u64 pcs_random(struct pcs_rng *rng); 19 | PCS_API u64 pcs_rand_range(struct pcs_rng *rng, u64 min, u64 max); 20 | PCS_API void pcs_srandom(struct pcs_rng *rng, u64 seed); 21 | PCS_API void pcs_srandomdev(struct pcs_rng *rng); 22 | 23 | #endif /* _RANDOM_H_ */ 24 | -------------------------------------------------------------------------------- /libpcs_io/pcs_iocp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_IOCP_H_ 6 | #define _PCS_IOCP_H_ 1 7 | 8 | #include "pcs_types.h" 9 | 10 | #ifdef __WINDOWS__ 11 | 12 | struct pcs_iocp 13 | { 14 | OVERLAPPED overlapped; 15 | void (*done)(struct pcs_iocp *iocp); 16 | }; 17 | 18 | struct pcs_process; 19 | 20 | void pcs_iocp_attach(struct pcs_process *proc, HANDLE handle, void *key); 21 | void pcs_iocp_cancel(HANDLE handle, struct pcs_iocp *iocp); 22 | void pcs_iocp_send(struct pcs_process *proc, struct pcs_iocp *iocp); 23 | int pcs_iocp_result(struct pcs_iocp *iocp); 24 | 25 | #endif /* __WINDOWS__ */ 26 | 27 | #endif /* _PCS_IOCP_H_ */ 28 | -------------------------------------------------------------------------------- /libpcs_io/pcs_fd_gc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_FD_GC_H_ 6 | #define _PCS_FD_GC_H_ 1 7 | 8 | #include "std_list.h" 9 | 10 | #define PCS_GC_FD_ON_ACCEPT 16 11 | 12 | struct pcs_process; 13 | 14 | struct pcs_fd_user 15 | { 16 | struct cd_list list; 17 | void *data; 18 | 19 | int (*gc)(void *); 20 | }; 21 | 22 | PCS_API void pcs_init_fd_user(struct pcs_process *, struct pcs_fd_user *, void *, int (*gc)(void *)); 23 | 24 | int pcs_fd_gc(struct pcs_process *); 25 | 26 | /* If err is ENFILE/EMFILE try to collect garbage up to "times" times. 27 | Return number of collected file descriptors or -err if err is any other error */ 28 | int pcs_fd_gc_on_error(struct pcs_process *, int err, int times); 29 | 30 | #endif /* _PCS_FD_GC_H_ */ 31 | -------------------------------------------------------------------------------- /libpcs_io/pcs_signal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_SIGNAL_H 6 | #define _PCS_SIGNAL_H 7 | 8 | #include 9 | #include "pcs_types.h" 10 | #include "pcs_process.h" 11 | 12 | typedef void (*pcs_sighandler_t)(struct pcs_process *proc, int signal, void *priv); 13 | 14 | PCS_API void pcs_signal_block(sigset_t *mask); 15 | PCS_API int pcs_signal_add_handler(struct pcs_process *proc, sigset_t *mask, pcs_sighandler_t handler, void *priv); 16 | PCS_API int pcs_signal_set_defaults(struct pcs_process *proc); 17 | PCS_API int pcs_signal_set_fatal_handlers(void); 18 | 19 | /* For internal use */ 20 | void pcs_signal_fini(struct pcs_process *proc); 21 | void pcs_signal_call_handler(struct pcs_ioconn *conn, int signal); 22 | 23 | #endif /* _PCS_SIGNAL_H */ 24 | -------------------------------------------------------------------------------- /libpcs_io/pcs_errno.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_ERRNO_H__ 6 | #define __PCS_ERRNO_H__ 7 | 8 | #include "pcs_types.h" 9 | #include "pcs_error.h" 10 | 11 | #include 12 | 13 | #ifdef __WINDOWS__ 14 | /* on windows all error codes are in different places, e.g. WSAXXX */ 15 | #include "pcs_sock.h" 16 | #endif 17 | 18 | __must_check static inline int errno_eagain(int err) 19 | { 20 | #ifndef __WINDOWS__ 21 | return err == EAGAIN || err == EWOULDBLOCK; 22 | #else 23 | return err == WSATRY_AGAIN || err == WSAEWOULDBLOCK; 24 | #endif 25 | } 26 | 27 | __must_check static inline int errno_enospc(int err) 28 | { 29 | #ifndef __WINDOWS__ 30 | return err == ENOSPC || err == EDQUOT; 31 | #else 32 | return pcs_errno_to_err(err) == PCS_ERR_NOSPACE; 33 | #endif 34 | } 35 | 36 | #endif /* __PCS_ERRNO_H__ */ 37 | -------------------------------------------------------------------------------- /libpcs_io/pcs_rdma_conn.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_RDMA_CONN_H_ 6 | #define _PCS_RDMA_CONN_H_ 1 7 | 8 | #include 9 | 10 | #include "pcs_types.h" 11 | #include "pcs_sock.h" 12 | #include "pcs_process.h" 13 | #include "pcs_net_addr.h" 14 | #include "pcs_net.h" 15 | 16 | struct pcs_rdmaconnect 17 | { 18 | struct pcs_netconnect netconn; 19 | 20 | struct rdma_cm_id *cmid; 21 | struct pcs_rdmaio *rio; 22 | int sa_len; 23 | struct sockaddr sa[0]; 24 | }; 25 | 26 | #define rdmaconn_from_netconn(conn) container_of(conn, struct pcs_rdmaconnect, netconn) 27 | #define rdmaconn_from_ioconn(conn) container_of(conn, struct pcs_rdmaconnect, netconn.ioconn) 28 | 29 | struct pcs_rdmaconnect * pcs_rdmaconnect_init(struct pcs_process * proc, PCS_NET_ADDR_T *addr); 30 | 31 | #endif /* _PCS_RDMA_CONN_H_ */ 32 | -------------------------------------------------------------------------------- /libpcs_io/pcs_rdma_listen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_RDMA_LISTEN_H_ 6 | #define _PCS_RDMA_LISTEN_H_ 1 7 | 8 | #include 9 | 10 | #include "pcs_types.h" 11 | #include "pcs_sock.h" 12 | #include "pcs_process.h" 13 | #include "pcs_net_addr.h" 14 | #include "pcs_net.h" 15 | 16 | struct pcs_rdmalisten 17 | { 18 | struct pcs_netlisten netlisten; 19 | 20 | struct rdma_cm_id *listen_cmid; 21 | 22 | int sa_len; 23 | struct sockaddr sa[0]; 24 | }; 25 | 26 | #define rdmalisten_from_netlisten(nl) container_of(nl, struct pcs_rdmalisten, netlisten) 27 | #define rdmalisten_from_ioconn(conn) container_of(conn, struct pcs_rdmalisten, netlisten.ioconn) 28 | 29 | struct pcs_rdmalisten * pcs_rdmalisten_alloc(struct pcs_process * proc, const PCS_NET_ADDR_T * addr); 30 | 31 | #endif /* _PCS_RDMA_LISTEN_H_ */ 32 | -------------------------------------------------------------------------------- /libpcs_io/pcs_net_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #pragma once 6 | 7 | #include "pcs_types.h" 8 | #include "pcs_net_addr.h" 9 | 10 | /* return IP addresses for specified hostname */ 11 | int pcs_resolve_hostname(const char *host, const char *svc, PCS_NET_ADDR_T addr[], int *nr, int passive); 12 | 13 | /* return <= nr existing network addresses */ 14 | int pcs_get_local_addrs(PCS_NET_ADDR_T addrs[], int *nr); 15 | 16 | /* return name of network interface for specified address */ 17 | int pcs_get_ifname_by_addr(PCS_NET_ADDR_T *addr, char *buf, int buf_sz); 18 | 19 | int pcs_get_addr_by_ifname(PCS_NET_ADDR_T *addr, const char *ifname); 20 | 21 | /* return IPv4 address for default routing */ 22 | int pcs_get_default_addr(PCS_NET_ADDR_T *addr); 23 | 24 | /* try to bind to the given ip address */ 25 | int pcs_is_net_addr_avail(PCS_NET_ADDR_T *addr); 26 | -------------------------------------------------------------------------------- /libpcs_io/lz4_estimate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __LZ4_ESTIMATE_H__ 6 | #define __LZ4_ESTIMATE_H__ 7 | 8 | #include 9 | #include "pcs_types.h" 10 | 11 | /* Estimates compressed data size. */ 12 | /* NOTE: it's only a rough estimate, do not use it for accurate output size predictions. 13 | * The less compressible is data the more precise estimation is, for example */ 14 | PCS_API size_t lz4_compressed_size_estimate(const char *buf, size_t buf_len); 15 | 16 | /* Check whether data compressible or not, and ratio sets a threshold value. 17 | * Returns 1 if estimated compressed size <= buf_len * (1 - ratio), otherwise returns 0. 18 | * So ratio should be a real value in range [0.0; 1.0). The closer ratio to 1.0 the more 19 | * compressible data should be for the function to return 1. */ 20 | PCS_API int lz4_data_compressible(const char *buf, size_t buf_len, double ratio); 21 | 22 | #endif /* __LZ4_ESTIMATE_H__ */ 23 | -------------------------------------------------------------------------------- /libpcs_io/parse_diskspace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PARSE_SPACE_H 6 | #define _PARSE_SPACE_H 7 | 8 | #include "pcs_types.h" 9 | 10 | #define MAGN_B 0 11 | #define MAGN_KB 1 12 | #define MAGN_MB 2 13 | #define MAGN_GB 3 14 | #define MAGN_TB 4 15 | 16 | /* Parse disk space specifications with possible usage of suffixes 17 | * (K, M, G, T in upper and lower case). Magnitude tells what "1" means, i.e.: 18 | * magnitude = 0 => "1" means 1 byte, all suffixes are allowed 19 | * magnitude = 1 => "1" means 1 kilobyte, only M, G and T suffixes are allowed 20 | * magnitude = 2 => "1" means 1 megabyte, only G and T suffixes are allowed 21 | * magnitude = 3 => "1" means 1 gigabyte, only T suffix is allowed 22 | */ 23 | PCS_API unsigned long long parse_diskspace(const char *arg, unsigned magnitude); 24 | PCS_API int parse_logrotate_diskspace(const char *arg, unsigned long *rotate_num, unsigned long long *rotate_size); 25 | 26 | #endif /* _PARSE_SPACE_H */ 27 | -------------------------------------------------------------------------------- /libpcs_io/pcs_atexit.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_ATEXIT_H_INCLUDED 6 | #define _PCS_ATEXIT_H_INCLUDED 7 | 8 | #include "pcs_types.h" 9 | 10 | typedef enum { 11 | exit_none = 0, /* the place-holder for not yet fired hook */ 12 | exit_normal = 1, /* normal termination */ 13 | exit_abort = 2, /* pcs_abort called */ 14 | exit_fatal = 4, /* fatal signal received */ 15 | } exit_type_t; 16 | 17 | struct pcs_atexit_hook { 18 | void (*cb)(struct pcs_atexit_hook const*); 19 | void* priv; 20 | exit_type_t exit_type; 21 | struct pcs_atexit_hook* next; 22 | }; 23 | 24 | /* Register hook to called on exit */ 25 | PCS_API void pcs_atexit(struct pcs_atexit_hook* hook); 26 | 27 | /* Un-register hook */ 28 | PCS_API void pcs_atexit_unreg(struct pcs_atexit_hook const* hook); 29 | 30 | /* Call atexit hooks (used internally) */ 31 | void pcs_call_atexit(exit_type_t exit_type); 32 | 33 | #endif 34 | 35 | -------------------------------------------------------------------------------- /libpcs_io/pcs_event_ioconn.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_EVENT_H__ 6 | #define __PCS_EVENT_H__ 7 | 8 | #include "pcs_ioconn.h" 9 | #include "pcs_iocp.h" 10 | #include "pcs_process.h" 11 | 12 | /* 13 | * PCS event ioconn is a method to send any kind of manual notifications to eventloop from other threads, 14 | * e.g. sync_io threads, file_jobs etc. 15 | */ 16 | 17 | struct pcs_event_ioconn 18 | { 19 | #ifndef __WINDOWS__ 20 | struct pcs_ioconn ioconn; 21 | int send_event_fd; 22 | #else 23 | struct pcs_iocp iocp; 24 | struct pcs_process *proc; 25 | #endif 26 | void (*data_ready)(void *priv); 27 | void *priv; 28 | }; 29 | 30 | int pcs_event_ioconn_init(struct pcs_process *proc, struct pcs_event_ioconn **event, void (*data_ready)(void *priv), void *priv); 31 | void pcs_event_ioconn_close(struct pcs_event_ioconn * event); 32 | 33 | /* wakeup eventloop on event */ 34 | void pcs_event_ioconn_wakeup(struct pcs_event_ioconn * event); 35 | 36 | #endif /* __PCS_EVENT_H__ */ 37 | -------------------------------------------------------------------------------- /libpcs_io/pcs_sock_conn.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_SOCK_CONN_H_ 6 | #define _PCS_SOCK_CONN_H_ 1 7 | 8 | #include "pcs_types.h" 9 | #include "pcs_sock.h" 10 | #include "pcs_process.h" 11 | #include "pcs_net_addr.h" 12 | #include "pcs_net.h" 13 | 14 | struct pcs_sockconnect 15 | { 16 | struct pcs_netconnect netconn; 17 | int error; 18 | void *private; 19 | void (*complete)(struct pcs_sockconnect *); 20 | 21 | int sa_len; 22 | struct sockaddr sa[0]; 23 | }; 24 | 25 | #define sockconn_from_netconn(conn) container_of(conn, struct pcs_sockconnect, netconn) 26 | #define sockconn_from_ioconn(conn) container_of(conn, struct pcs_sockconnect, netconn.ioconn) 27 | 28 | struct pcs_sockconnect * pcs_sockconnect_init(struct pcs_process * proc, PCS_NET_ADDR_T *addr); 29 | struct pcs_sockconnect * pcs_sockconnect_init_sa(struct pcs_process * proc, struct sockaddr *sa, int len); 30 | void pcs_sockconnect_start(struct pcs_process * proc, struct pcs_sockconnect * sh); 31 | void pcs_sockconnect_abort(struct pcs_sockconnect * conn, int error); 32 | 33 | #endif /* _PCS_SOCK_CONN_H_ */ 34 | 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | libpcs_io is available for use under the MIT license: 2 | 3 | ==== 4 | Copyright © 2003-2018 Acronis International GmbH. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to 8 | deal in the Software without restriction, including without limitation the 9 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | sell copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | IN THE SOFTWARE. 23 | ==== 24 | -------------------------------------------------------------------------------- /libpcs_io/std_list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "std_list.h" 6 | 7 | static void cd_list_merge(struct cd_list *list1, struct cd_list *list2, int (*cmp_fn)(struct cd_list *, struct cd_list *)) 8 | { 9 | struct cd_list *i1 = list1->next, *i2 = list2->next; 10 | 11 | while (i1 != list1 && i2 != list2) { 12 | if (cmp_fn(i1, i2) < 0) { 13 | struct cd_list *tmp = i1; 14 | i1 = i1->next; 15 | cd_list_move_tail(tmp, i2); 16 | } else { 17 | i2 = i2->next; 18 | } 19 | } 20 | 21 | cd_list_splice(list2, list1); 22 | } 23 | 24 | void cd_list_sort(struct cd_list *head, int (*cmp_fn)(struct cd_list *, struct cd_list *)) 25 | { 26 | struct cd_list binlist[64]; 27 | unsigned maxbin = 0, bin; 28 | 29 | while (!cd_list_empty(head)) { 30 | CD_LIST_HEAD(tmp); 31 | cd_list_move_tail(head->next, &tmp); 32 | 33 | for (bin = 0; bin < maxbin && !cd_list_empty(&binlist[bin]); bin++) 34 | cd_list_merge(&tmp, &binlist[bin], cmp_fn); 35 | 36 | if (bin == maxbin) 37 | maxbin++; 38 | 39 | cd_list_init(&binlist[bin]); 40 | cd_list_splice_tail(&tmp, &binlist[bin]); 41 | } 42 | 43 | for (bin = 0; bin < maxbin; bin++) 44 | cd_list_merge(head, &binlist[bin], cmp_fn); 45 | } 46 | -------------------------------------------------------------------------------- /libpcs_io/pcs_poll.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_POLL_H__ 6 | #define __PCS_POLL_H__ 7 | 8 | #include "pcs_types.h" 9 | 10 | #ifdef __LINUX__ 11 | #include 12 | #include 13 | #define HAVE_EPOLL 1 14 | 15 | #ifndef POLLRDHUP 16 | #define POLLRDHUP 0x2000 17 | #endif 18 | #endif 19 | 20 | #ifdef __MAC__ 21 | #include 22 | #include 23 | #define HAVE_KQUEUE 1 24 | 25 | #define POLLRDHUP 0 26 | #define EPOLLET (1 << 31) 27 | #endif 28 | 29 | #ifdef __SUN__ 30 | #include 31 | 32 | #define POLLRDHUP 0 33 | #define EPOLLET 0 34 | #endif 35 | 36 | struct pcs_process; 37 | struct pcs_evloop; 38 | struct pcs_ioconn; 39 | struct pcs_co_file; 40 | 41 | int pcs_poll_init(struct pcs_process *proc); 42 | void pcs_poll_fini(struct pcs_process *proc); 43 | 44 | int pcs_poll_ctl(struct pcs_process *proc, struct pcs_ioconn *conn); 45 | void pcs_poll_wait(struct pcs_evloop *evloop, int timeout); 46 | void pcs_poll_process_events(struct pcs_evloop *evloop); 47 | 48 | void pcs_poll_file_init(struct pcs_co_file *file); 49 | void pcs_poll_file_fini(struct pcs_co_file *file); 50 | void pcs_poll_file_begin(struct pcs_co_file *file, int mask); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /libpcs_io/libpcs_io.supp: -------------------------------------------------------------------------------- 1 | { 2 | pcs_malloc tracepoints info 3 | Memcheck:Leak 4 | match-leak-kinds: reachable 5 | ... 6 | fun:pcs_malloc_item_init_ 7 | } 8 | 9 | { 10 | pcs_process static initialisation 11 | Memcheck:Leak 12 | ... 13 | fun:pcs_process_init 14 | } 15 | 16 | { 17 | pcs_co_filejob static initialisation 18 | Memcheck:Leak 19 | ... 20 | fun:pcs_co_filejob_init 21 | } 22 | 23 | { 24 | A stack for signal handlers allocated by pcs_signal_set_fatal_handlers 25 | Memcheck:Leak 26 | ... 27 | fun:pcs_signal_set_fatal_handlers 28 | } 29 | 30 | { 31 | A context of pcs logger exists as long as process is running 32 | Memcheck:Leak 33 | ... 34 | fun:pcs_set_logfile 35 | } 36 | 37 | { 38 | OpenSSL static initialisation 39 | Memcheck:Leak 40 | ... 41 | fun:pcs_ssl_init 42 | } 43 | 44 | { 45 | OpenSSL per-thread error state allocation 46 | Memcheck:Leak 47 | ... 48 | fun:ERR_get_state 49 | } 50 | 51 | { 52 | OpenSSL per-thread initialisation of ex_data for bio and X509 (what's that?) 53 | Memcheck:Leak 54 | ... 55 | fun:def_get_class 56 | } 57 | 58 | { 59 | OpenSSL per-thread initialisation of ex_data indices for bio and X509 (what's that?) 60 | Memcheck:Leak 61 | ... 62 | fun:int_get_new_index 63 | } 64 | 65 | { 66 | OpenSSL BIO_METHOD for bufqueue 67 | Memcheck:Leak 68 | ... 69 | fun:sslsock_bio_method 70 | } 71 | -------------------------------------------------------------------------------- /libpcs_io/pcs_sock_listen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_SOCK_LISTEN_H_ 6 | #define _PCS_SOCK_LISTEN_H_ 1 7 | 8 | #include "pcs_types.h" 9 | #include "pcs_sock.h" 10 | #include "pcs_poll.h" 11 | #include "pcs_process.h" 12 | #include "pcs_net_addr.h" 13 | #include "pcs_net.h" 14 | 15 | #define PCS_SOCKLISTEN_THROTTLE_DELAY 100 16 | 17 | struct pcs_socklisten 18 | { 19 | struct pcs_netlisten netlisten; 20 | 21 | void * private; 22 | void (*accepted)(struct pcs_socklisten *, pcs_sock_t fd); 23 | 24 | struct pcs_timer throttle_timer; 25 | 26 | int sa_len; 27 | struct sockaddr sa[0]; 28 | }; 29 | 30 | #define PCS_SK_FREEBIND 1 31 | 32 | #define socklisten_from_netlisten(nl) container_of(nl, struct pcs_socklisten, netlisten) 33 | #define socklisten_from_ioconn(conn) container_of(conn, struct pcs_socklisten, netlisten.ioconn) 34 | 35 | struct pcs_socklisten * pcs_socklisten_alloc(struct pcs_process * proc, const PCS_NET_ADDR_T * addr); 36 | struct pcs_socklisten * pcs_socklisten_alloc_sa(struct pcs_process * proc, struct sockaddr *sa, int len); 37 | int pcs_socklisten_start(struct pcs_process * proc, struct pcs_socklisten * sh, int flags); 38 | /* Stop accepting connections on @sh, and destroy it. */ 39 | void pcs_socklisten_stop(struct pcs_socklisten * sh); 40 | 41 | #endif /* _PCS_SOCK_LISTEN_H_ */ 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PCS I/O (Parallel Cloud Storage library) 2 | 3 | ## Description 4 | pcs_io is a C cross platform library implementing async event loop with coroutines, context and other features. 5 | 6 | ## Features 7 | 8 | 1. Basic event loop APIs: timers, jobs, thread pool jobs etc. 9 | 1. Usage of native OS asynchronous APIs support: epoll, Windows IOCP, SUN ports, BSD kqueue, RDMA 10 | 1. Full memory usage accounting and wrapped malloc APIs 11 | 1. coroutines support with: 12 | * I/O blocking APIs for files, sockets, pipes, SSL sockets. 13 | * timeouts and I/O cancellation (via context) 14 | * contexts support similar to Golang 15 | * multi-threading support with Coroutines scheduler 16 | * coroutines waitqueues, mutexes etc. 17 | 1. simple config files API support 18 | 1. built-in cross platform getopt_long support 19 | 1. basic containers (RB tree, lists, heap) 20 | 1. Network address abstraction layer 21 | 1. internal scatter gather bufqueue and user pipes 22 | 23 | ## Built-in debug features and capabilities 24 | 25 | * Async compressed on the fly logging with GZIP/ZSTD support 26 | * Address and thread sanitizer support 27 | * Built-in profiler and watchdog monitoring event loop is really non-blocking (and catching kernel issues, swap out etc.) 28 | * Memory accounting with detailed per-line reporting 29 | * BUG_ON() assertions and call trace dumps on errors / crashes 30 | -------------------------------------------------------------------------------- /libpcs_io/qselect.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #pragma once 6 | 7 | #include "pcs_types.h" 8 | 9 | /* 10 | * Quick selection algorithm with O(n) running time. 11 | */ 12 | 13 | /* Rearrange elements of data array of size N so that all items with indexes < k are less than data[k] while 14 | * all items with indexes > k are greater or equal to data[k]. So this means that on output data[k] is also the 15 | * k-th element of the data array in the sorted order. The routine uses the caller-provided comparison and swap 16 | * functions. Both takes element index as the arguments. The comparison function must return an integer less than, 17 | * equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or 18 | * greater than the second. The callbacks may operate on the data array directly or deal with index so the 19 | * original data will be intact. 20 | */ 21 | void quick_partition( 22 | void* data, long N, long k, 23 | int (*compar)(void*, long, long), void (*swap)(void*, long, long) 24 | ); 25 | 26 | /* Returns k-th element in sorted order from the given (unsorted array) */ 27 | unsigned select_uint(unsigned* arr, long N, long k); 28 | 29 | /* Returns median element given the unsorted array of values */ 30 | static inline unsigned median_uint(unsigned* arr, long N) { 31 | return select_uint(arr, N, N/2); 32 | } 33 | -------------------------------------------------------------------------------- /libpcs_io/pcs_ioconn.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_IOCONN_H_ 6 | #define _PCS_IOCONN_H_ 1 7 | 8 | #include "pcs_types.h" 9 | #include "pcs_sock.h" 10 | #include "std_list.h" 11 | 12 | #ifndef __WINDOWS__ 13 | 14 | struct pcs_ioconn 15 | { 16 | struct cd_list list; 17 | 18 | int fd; 19 | char dead; 20 | int actual_mask; /* Actual mask loaded to kernel epoll */ 21 | int next_mask; /* Mask planned for loading to epoll */ 22 | 23 | struct pcs_process * proc; 24 | 25 | void(*data_ready)(struct pcs_ioconn *); 26 | void(*write_space)(struct pcs_ioconn *); 27 | void(*error_report)(struct pcs_ioconn *); 28 | void(*destruct)(struct pcs_ioconn *); 29 | }; 30 | 31 | void pcs_ioconn_init(struct pcs_process *, struct pcs_ioconn * conn); 32 | void pcs_ioconn_close(struct pcs_ioconn * conn); 33 | void pcs_ioconn_destruct(struct pcs_ioconn * conn); 34 | 35 | void pcs_ioconn_register(struct pcs_ioconn * conn); 36 | void pcs_ioconn_unregister(struct pcs_ioconn * conn); 37 | void pcs_ioconn_schedule(struct pcs_ioconn * conn); 38 | 39 | /* internal API for eventloop */ 40 | void ioconn_kill_all(void * arg); 41 | 42 | static inline void pcs_ioconn_reset(struct pcs_ioconn * conn) 43 | { 44 | conn->fd = -1; 45 | } 46 | 47 | #else /* __WINDOWS__ */ 48 | 49 | #define ioconn_kill_all NULL 50 | 51 | #endif /* __WINDOWS__ */ 52 | 53 | #endif /* _PCS_IOCONN_H_ */ 54 | -------------------------------------------------------------------------------- /libpcs_io/pcs_watchdog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_WATCHDOG_H_ 6 | #define _PCS_WATCHDOG_H_ 1 7 | 8 | #include "pcs_config.h" 9 | 10 | #if defined(__linux__) && !defined(PCS_WATCHDOG_DISABLE) && !defined(PCS_ADDR_SANIT) 11 | #define PCS_USE_WATCHDOG 1 12 | #endif 13 | 14 | #ifdef PCS_USE_WATCHDOG 15 | #include 16 | #include "pcs_thread.h" 17 | #endif 18 | 19 | #define PCS_LOOP_WATCHDOG_TIME (15*1000) 20 | 21 | struct pcs_evloop; 22 | struct pcs_watchdog; 23 | 24 | #ifdef PCS_USE_WATCHDOG 25 | 26 | struct pcs_watchdog 27 | { 28 | /* For pcs_process */ 29 | pcs_thread_t wd_thr; 30 | pthread_mutex_t wd_mutex; 31 | pthread_cond_t wd_wake; 32 | int wd_run; 33 | 34 | /* For pcs_evloop */ 35 | u32 wd_poll_checked; 36 | abs_time_t wd_last_activity; 37 | abs_time_t wd_accounted; 38 | abs_time_t wd_inactive_total; 39 | }; 40 | 41 | void pcs_watchdog_start(struct pcs_process *proc); 42 | void pcs_watchdog_stop(struct pcs_process *proc); 43 | void pcs_watchdog_init_evloop(struct pcs_evloop *evloop); 44 | 45 | #else /* PCS_USE_WATCHDOG */ 46 | 47 | static inline void pcs_watchdog_start(struct pcs_process *proc) {} 48 | static inline void pcs_watchdog_stop(struct pcs_process *proc) {} 49 | static inline void pcs_watchdog_init_evloop(struct pcs_evloop *evloop) {} 50 | 51 | #endif /* PCS_USE_WATCHDOG */ 52 | 53 | #endif /* _PCS_WATCHDOG_H_ */ 54 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include Makefile.inc 2 | 3 | SUBDIRS=libpcs_io 4 | SUBDIRS_COV=libpcs_io 5 | SUBDIRS_INSTALL=libpcs_io 6 | 7 | all: 8 | @set -e; \ 9 | for d in $(SUBDIRS); do $(MAKE) -C $$d all ; done 10 | 11 | install_coverage: all 12 | install -d $(INSTALL_PREFIX)/$(PCS_GCOV_DIR) 13 | install -d $(INSTALL_PREFIX)/$(PCS_GCOV_DIR)/$(PCS_BUILD_VERSION) 14 | find . -name *.gcno | tar cfz ../coverage-$(PCS_BUILD_VERSION).tar.gz -T - 15 | install ../coverage-$(PCS_BUILD_VERSION).tar.gz $(INSTALL_PREFIX)/$(PCS_GCOV_DIR) 16 | rm -f ../coverage-$(PCS_BUILD_VERSION).tar.gz 17 | 18 | install-deps: 19 | yum install -y gcc gcc-c++ fuse-devel openssl-devel avahi-devel zlib-devel ncurses-devel libaio-devel openssl-static asciidoc libxml2-devel json-c-devel json-c-devel avahi-devel libzstd-devel libunwind-devel libcap-devel libibverbs-devel 20 | 21 | install: 22 | @set -e; \ 23 | install -d $(INSTALL_PREFIX)/$(PCS_LIBEXEC_DIR) 24 | install -d $(INSTALL_PREFIX)/$(PCS_VAR_LIB_DIR) 25 | install -d $(INSTALL_PREFIX)/$(PCS_VAR_LOG_DIR) 26 | install -d $(INSTALL_PREFIX)/$(PCS_VAR_RUN_DIR) 27 | for d in $(SUBDIRS_INSTALL); do $(MAKE) -C $$d install ; done 28 | 29 | depend dep: 30 | @set -e; \ 31 | for d in $(SUBDIRS); do $(MAKE) -C $$d depend ; done 32 | 33 | clean: 34 | @for d in $(SUBDIRS) man; do $(MAKE) -C $$d clean ; done 35 | 36 | tags: FORCE 37 | ctags -R 38 | 39 | cscope: FORCE 40 | find . -name '*.c' -print -o -name '*.h' -print | cscope -q -R -b -i - 41 | 42 | FORCE: 43 | -------------------------------------------------------------------------------- /libpcs_io/md5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is the header file for the MD5 message-digest algorithm. 3 | * The algorithm is due to Ron Rivest. This code was 4 | * written by Colin Plumb in 1993, no copyright is claimed. 5 | * This code is in the public domain; do with it what you wish. 6 | * 7 | * Equivalent code is available from RSA Data Security, Inc. 8 | * This code has been tested against that, and is equivalent, 9 | * except that you don't need to include two pages of legalese 10 | * with every copy. 11 | * 12 | * To compute the message digest of a chunk of bytes, declare an 13 | * pcs_md5_context structure, pass it to pcs_md5_init, call pcs_md5_update as 14 | * needed on buffers full of bytes, and then call pcs_md5_final, which 15 | * will fill a supplied 16-byte array with the digest. 16 | * 17 | */ 18 | 19 | #ifndef MD5_H 20 | #define MD5_H 21 | 22 | #include "pcs_types.h" 23 | 24 | #define MD5_LEN 16 25 | 26 | struct pcs_md5_context { 27 | u32 buf[4]; 28 | u32 bytes[2]; 29 | u32 in[MD5_LEN]; 30 | }; 31 | 32 | void pcs_md5_init(struct pcs_md5_context *context); 33 | void pcs_md5_update(struct pcs_md5_context *context, const void *buf, unsigned len); 34 | void pcs_md5_final(unsigned char digest[MD5_LEN], struct pcs_md5_context *context); 35 | 36 | static inline void pcs_md5_hash(unsigned char digest[MD5_LEN], const void *buf, unsigned len) 37 | { 38 | struct pcs_md5_context c; 39 | 40 | pcs_md5_init(&c); 41 | pcs_md5_update(&c, buf, len); 42 | pcs_md5_final(digest, &c); 43 | } 44 | 45 | #endif /* !MD5_H */ 46 | -------------------------------------------------------------------------------- /libpcs_io/pcs_ucontext.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #pragma once 6 | 7 | #include "pcs_types.h" 8 | 9 | #if defined(__WINDOWS__) 10 | 11 | struct pcs_ucontext { 12 | void *fiber; 13 | }; 14 | 15 | void pcs_ucontext_switch(struct pcs_ucontext *save, struct pcs_ucontext *load); 16 | 17 | #define PCS_UCONTEXT_FUNC CALLBACK 18 | #define PCS_UCONTEXT_TOPMOST 19 | 20 | #else /* __WINDOWS__ */ 21 | 22 | #if (defined(__LINUX__) || defined(__MAC__) || defined(__SUN__)) && (defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)) 23 | 24 | struct pcs_ucontext { 25 | void *sp; 26 | }; 27 | 28 | void pcs_ucontext_switch(struct pcs_ucontext *save, struct pcs_ucontext *load); 29 | 30 | #ifdef __x86_64__ 31 | #define PCS_UCONTEXT_TOPMOST __asm__ __volatile__(".cfi_undefined rip") 32 | #elif defined(__i386__) 33 | #define PCS_UCONTEXT_TOPMOST __asm__ __volatile__(".cfi_undefined eip") 34 | #elif defined(__aarch64__) 35 | #define PCS_UCONTEXT_TOPMOST __asm__ __volatile__(".cfi_undefined x30") 36 | #endif 37 | 38 | #else 39 | 40 | #include 41 | 42 | struct pcs_ucontext { 43 | sigjmp_buf jmpbuf; 44 | }; 45 | 46 | static inline void pcs_ucontext_switch(struct pcs_ucontext *save, struct pcs_ucontext *load) 47 | { 48 | if (!sigsetjmp(save->jmpbuf, 0)) 49 | siglongjmp(load->jmpbuf, 1); 50 | } 51 | 52 | #define PCS_UCONTEXT_TOPMOST 53 | 54 | #endif 55 | 56 | void pcs_ucontext_init(struct pcs_ucontext *context, void *stack, u32 stack_sz, void (*func)(void*), void *arg); 57 | 58 | #define PCS_UCONTEXT_FUNC 59 | 60 | #endif /* __WINDOWS__ */ 61 | -------------------------------------------------------------------------------- /libpcs_io/pcs_sock_ssl_priv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "pcs_co_io.h" 10 | 11 | struct pcs_ssl_socket { 12 | struct pcs_co_file file; 13 | 14 | /* the underlying transport */ 15 | struct pcs_co_file *raw_sock; 16 | 17 | SSL_CTX *ctx; 18 | SSL *ssl; 19 | char *hostname; 20 | 21 | struct { 22 | int (*cb)(void *arg, SSL *ssl, int *alert); 23 | void *arg; 24 | } client_hello; 25 | 26 | struct { 27 | unsigned int depth; 28 | int (*get_certs)(void *arg, X509 ***certs, X509_CRL ***crls); 29 | void *arg; 30 | 31 | int (*cb)(struct pcs_co_file *sock, X509_STORE_CTX *ctx, int verify_cert_result); 32 | } verify_peer; 33 | 34 | int state; 35 | #define SOCKSTATE_INIT (-1) 36 | #define SOCKSTATE_CONNECTING (0) 37 | #define SOCKSTATE_ACCEPTING (1) 38 | #define SOCKSTATE_NORMAL (2) /* doing IO with SSL_{read,write}() */ 39 | #define SOCKSTATE_DEAD (4) 40 | 41 | int (*handshake)(struct pcs_ssl_socket *sock); 42 | 43 | /* An error message describing the last SSL failure, as returned by ERR_error_string(). */ 44 | char err_msg[128]; 45 | }; 46 | 47 | void save_last_ssl_error_string(struct pcs_ssl_socket *sock, unsigned long err); 48 | void save_last_os_error_string(struct pcs_ssl_socket *sock, int err); 49 | struct pcs_co_file* pcs_ssl_socket_st(struct pcs_co_file *raw_socket); 50 | struct pcs_co_file* pcs_ssl_socket_mt(struct pcs_co_file *raw_socket); 51 | void pcs_ssl_socket_init(struct pcs_ssl_socket *sock); 52 | void pcs_ssl_socket_fini(struct pcs_ssl_socket *sock); 53 | -------------------------------------------------------------------------------- /libpcs_io/pcs_align.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_ALIGN_H__ 6 | #define __PCS_ALIGN_H__ 7 | 8 | #include "pcs_types.h" 9 | 10 | /* ----- helpers ----- */ 11 | 12 | #if defined(__GNUC__) || defined(__clang__) 13 | 14 | #define __pre_aligned(x) 15 | #define __aligned(x) __attribute__((__aligned__(x))) 16 | #define __pre_packed 17 | #define __packed __attribute__((packed)) 18 | #define __unaligned __attribute__((packed, may_alias)) 19 | 20 | #elif defined(_MSC_VER) 21 | 22 | #define __pre_aligned(x) __declspec(align(x)) 23 | #define __aligned(x) 24 | #define __pre_packed __pragma(pack(push,1)) 25 | #define __packed ; __pragma(pack(pop)) 26 | /* MSVC can't target platforms with unaligned access problems, so we can ignore this attribute */ 27 | #define __unaligned 28 | 29 | #endif 30 | 31 | #define PCS_ALIGN_TO(sz, align) (((sz)+(align)-1)&~((align)-1)) 32 | #define PCS_ALIGN(sz) PCS_ALIGN_TO(sz, 8) 33 | 34 | #define __PCS_UNALIGNED2(type, suff) \ 35 | typedef union { type v; } __unaligned pcs_unaligned_ ## suff ## _t; \ 36 | static inline type pcs_get_unaligned_ ## suff (const void *p) { return ((const pcs_unaligned_ ## suff ## _t *)p)->v; } 37 | #define __PCS_UNALIGNED(size) __PCS_UNALIGNED2(u ## size, size) 38 | 39 | __PCS_UNALIGNED(16) /* pcs_get_unaligned_16() */ 40 | __PCS_UNALIGNED(32) /* pcs_get_unaligned_32() */ 41 | __PCS_UNALIGNED(64) /* pcs_get_unaligned_64() */ 42 | __PCS_UNALIGNED2(ULONG_PTR, ptr) /* pcs_get_unaligned_ptr() */ 43 | 44 | /* This macros are used only to generate unaligned getters */ 45 | #undef __unaligned 46 | #undef __PCS_UNALIGNED 47 | #undef __PCS_UNALIGNED2 48 | 49 | #endif /* __PCS_ALIGN_H__ */ 50 | -------------------------------------------------------------------------------- /libpcs_io/parse_diskspace.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "parse_diskspace.h" 12 | #include "log.h" 13 | 14 | #ifndef ULLONG_MAX 15 | #define ULLONG_MAX (~0ULL) 16 | #endif 17 | 18 | unsigned long long parse_diskspace(const char *arg, unsigned magnitude) 19 | { 20 | static const char suffixes[] = "KkMmGgTt"; 21 | unsigned long long space; 22 | char *p1, *p2; 23 | 24 | BUG_ON(magnitude > MAGN_TB); 25 | 26 | space = strtoull(arg, &p1, 10); 27 | if (space == ULLONG_MAX) { 28 | fprintf(stderr, "strtoul() failed: %s\n", strerror(errno)); 29 | return 0; 30 | } 31 | if (p1 == arg) { 32 | fprintf(stderr, "Invalid space: %s\n", arg); 33 | return 0; 34 | } 35 | if (p1[0] == '\0') 36 | return space; 37 | 38 | p2 = strchr(suffixes + magnitude * 2, p1[0]); 39 | if ((p1[1] != '\0') || !p2) { 40 | fprintf(stderr, "Invalid space suffix: %s\n", p1); 41 | return 0; 42 | } 43 | 44 | return space << (((p2 - suffixes) / 2 - magnitude + 1) * 10); 45 | } 46 | 47 | int parse_logrotate_diskspace(const char *arg, unsigned long *rotate_num, unsigned long long *rotate_size) 48 | { 49 | char *ptr; 50 | *rotate_num = strtoul(arg, &ptr, 10); 51 | if (*rotate_num == ULONG_MAX) { 52 | fprintf(stderr, "strtoul() failed: %s\n", strerror(errno)); 53 | return -1; 54 | } 55 | if (ptr == arg) { 56 | fprintf(stderr, "Invalid number of rotated files."); 57 | return -1; 58 | } 59 | if (ptr[0] != 'x') { 60 | fprintf(stderr, "Invalid delimeter."); 61 | return -1; 62 | } 63 | ++ptr; 64 | *rotate_size = parse_diskspace(ptr, MAGN_B); 65 | if (!*rotate_size) 66 | return -1; 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /libpcs_io/regdump.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _REGISTER_DUMP 6 | #define _REGISTER_DUMP 7 | 8 | #include "pcs_types.h" 9 | 10 | #if defined(__LINUX__) || defined(__MAC__) || defined(__SUN__) 11 | #include /* for backtraces */ 12 | #include 13 | #include 14 | #elif defined(_WIN32) 15 | typedef EXCEPTION_POINTERS ucontext_t; 16 | #else 17 | typedef void ucontext_t; 18 | #endif 19 | 20 | struct pcs_ucontext; 21 | 22 | static inline void *register_get_pc(ucontext_t *context) 23 | { 24 | #if defined(__LINUX__) && defined(__x86_64__) 25 | return (void *)context->uc_mcontext.gregs[REG_RIP]; 26 | #elif defined(__LINUX__) && defined(__i386__) 27 | return (void *)context->uc_mcontext.gregs[REG_EIP]; 28 | #elif defined(__MAC__) && defined(__x86_64__) 29 | return (void *)context->uc_mcontext->__ss.__rip; 30 | #elif defined(__aarch64__) 31 | return (void *)context->uc_mcontext.pc; 32 | #else 33 | return NULL; 34 | #endif 35 | } 36 | 37 | static inline void register_set_bp(ucontext_t *context, ULONG_PTR bp) 38 | { 39 | #if defined(__LINUX__) && defined(__x86_64__) 40 | context->uc_mcontext.gregs[REG_RBP] = bp; 41 | #elif defined(__LINUX__) && defined(__i386__) 42 | context->uc_mcontext.gregs[REG_EBP] = bp; 43 | #elif defined(__MAC__) && defined(__x86_64__) 44 | context->uc_mcontext->__ss.__rbp = bp; 45 | #elif defined(__aarch64__) 46 | /* X29 - frame pointer */ 47 | context->uc_mcontext.regs[29] = bp; 48 | #endif 49 | } 50 | 51 | typedef void (*log_printf_fn)(void *arg, const char *fmt, ...) __printf(2, 3); 52 | void register_dump(ucontext_t *ctx, log_printf_fn log_printf, void *arg); 53 | void trace_dump(ucontext_t *ctx, log_printf_fn log_printf, void *arg); 54 | void trace_dump_coroutine(struct pcs_ucontext *ctx, log_printf_fn log_printf, void *arg); 55 | 56 | #endif /* _REGISTER_DUMP */ 57 | -------------------------------------------------------------------------------- /libpcs_io/pcs_atexit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_atexit.h" 6 | #include "pcs_malloc.h" 7 | #include "bug.h" 8 | 9 | #include 10 | #include 11 | 12 | /* The hooks chain head pointer */ 13 | static struct pcs_atexit_hook* pcs_atexit_hooks = NULL; 14 | static pthread_mutex_t pcs_atexit_lock = PTHREAD_MUTEX_INITIALIZER; 15 | 16 | static void pcs_atexit_(void) 17 | { 18 | pcs_call_atexit(exit_normal); 19 | } 20 | 21 | /* Register hook to called on exit */ 22 | PCS_API void pcs_atexit(struct pcs_atexit_hook* hook) 23 | { 24 | int first_hook; 25 | BUG_ON(!hook->cb); 26 | 27 | pthread_mutex_lock(&pcs_atexit_lock); 28 | first_hook = !pcs_atexit_hooks; 29 | hook->exit_type = exit_none; 30 | hook->next = pcs_atexit_hooks; 31 | pcs_atexit_hooks = hook; 32 | pthread_mutex_unlock(&pcs_atexit_lock); 33 | 34 | if (first_hook) { 35 | atexit(pcs_atexit_); 36 | } 37 | } 38 | 39 | /* Un-register hook */ 40 | PCS_API void pcs_atexit_unreg(struct pcs_atexit_hook const* hook) 41 | { 42 | struct pcs_atexit_hook* curr; 43 | struct pcs_atexit_hook** link = &pcs_atexit_hooks; 44 | 45 | pthread_mutex_lock(&pcs_atexit_lock); 46 | while ((curr = *link)) { 47 | if (curr == hook) { 48 | *link = curr->next; 49 | break; 50 | } 51 | link = &curr->next; 52 | } 53 | pthread_mutex_unlock(&pcs_atexit_lock); 54 | 55 | if (curr) { 56 | pcs_free(curr); 57 | } 58 | } 59 | 60 | /* Call atexit hooks (used internally) */ 61 | void pcs_call_atexit(exit_type_t exit_type) 62 | { 63 | struct pcs_atexit_hook *head, *hook; 64 | 65 | pthread_mutex_lock(&pcs_atexit_lock); 66 | head = pcs_atexit_hooks; 67 | pcs_atexit_hooks = NULL; 68 | pthread_mutex_unlock(&pcs_atexit_lock); 69 | 70 | while ((hook = head)) { 71 | head = hook->next; 72 | hook->exit_type = exit_type; 73 | hook->cb(hook); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /libpcs_io/pcs_numeric.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #pragma once 6 | 7 | static inline u64 next_pow2(u32 x) 8 | { 9 | if (x <= 2) return x; 10 | return (1ULL << 32) >> __builtin_clz(x - 1); 11 | } 12 | 13 | static inline s32 _log2(u32 x) 14 | { 15 | if (x <= 2) return (s32)x - 1; 16 | return 8 * sizeof(x) - 1 - __builtin_clz(x); 17 | } 18 | 19 | static inline s32 _log2l(u64 x) 20 | { 21 | if (x <= 2) return (s32)x - 1; 22 | return 8 * sizeof(x) - 1 - __builtin_clzl(x); 23 | } 24 | 25 | /* Calculate a * b / c avoiding overflow */ 26 | static inline u64 muldiv_safe(u64 a, u64 b, u64 c) 27 | { 28 | u64 v; 29 | /* The arithmetic width */ 30 | s32 const w = 8 * sizeof(v), hw = w / 2; 31 | /* How many bits are occupied by operands */ 32 | s32 a_ = 1 + _log2l(a), b_ = 1 + _log2l(b), c_, x_; 33 | if (a_ + b_ <= w) 34 | /* No overflow, trivial case */ 35 | return a * b / c; 36 | c_ = 1 + _log2l(c); 37 | if (a_ + b_ - c_ > w) 38 | /* Result overflow, returns max representable value */ 39 | return ~0UL; 40 | /* How many excessive bits the a*b has */ 41 | x_ = a_ + b_ - w; 42 | /* Right shift a, b operands so a*b will occupy exactly w bits. 43 | * Note that we are loosing at most 2^-hw of precision here. 44 | */ 45 | if (a_ > hw) { 46 | if (b_ > hw) { 47 | a >>= (a_ - hw); 48 | b >>= (b_ - hw); 49 | } else { 50 | a >>= x_; 51 | } 52 | } else { 53 | b >>= x_; 54 | } 55 | /* Now we can safely multiply. The result will occupy exactly w bits. */ 56 | v = a * b; 57 | if (c_ > hw) { 58 | /* Right shift c to hw bits so the reminder will occupy hw bits as well. 59 | * So we are loosing at most 2^-hw of precision on deletion. 60 | */ 61 | s32 sht = c_ - hw; 62 | if (sht > x_) 63 | sht = x_; 64 | c >>= sht; 65 | x_ -= sht; 66 | } 67 | /* Calculate the ultimate result */ 68 | return (v / c) << x_; 69 | } 70 | -------------------------------------------------------------------------------- /libpcs_io/pcs_shaper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_shaper.h" 6 | #include "pcs_process.h" 7 | #include "log.h" 8 | 9 | static void tba_shaper_start(struct pcs_tba_shaper *s) 10 | { 11 | s->capacity = s->cbs; 12 | s->last_tick = get_abs_time_ms(); 13 | mod_timer(&s->timer, 0); 14 | } 15 | 16 | static void tba_shaper_stop(struct pcs_tba_shaper *s) 17 | { 18 | s->capacity = 0; 19 | del_timer_sync(&s->timer); 20 | } 21 | 22 | static void tba_shaper_timer(void *arg) 23 | { 24 | struct pcs_tba_shaper *s = arg; 25 | const abs_time_t now = get_abs_time_ms(); 26 | const abs_time_t elapsed = get_elapsed_time(now, s->last_tick); 27 | 28 | s->last_tick = now; 29 | s->capacity += (s->out_rate * elapsed) / 1000; 30 | if (s->capacity > s->cbs) 31 | s->capacity = s->cbs; 32 | 33 | mod_timer(&s->timer, PCS_SHAPER_TICK_RATE); 34 | 35 | if (s->wakeup_cb) 36 | s->wakeup_cb(s); 37 | } 38 | 39 | int pcs_tba_shaper_set_rate(struct pcs_tba_shaper *s, u32 out_rate, u64 cbs) 40 | { 41 | if (out_rate && (out_rate < PCS_SHAPER_MIN_RATE || cbs < out_rate)) 42 | return -1; 43 | 44 | if (s->cbs == cbs && s->out_rate == out_rate) 45 | /* don't restart shaper if params haven't changed */ 46 | return 0; 47 | 48 | s->cbs = cbs; 49 | 50 | if (!out_rate) { /* disable shaper */ 51 | tba_shaper_stop(s); 52 | /* notify watchers */ 53 | if (s->out_rate && s->wakeup_cb) 54 | s->wakeup_cb(s); 55 | } else 56 | tba_shaper_start(s); 57 | 58 | s->out_rate = out_rate; 59 | return 0; 60 | } 61 | 62 | void pcs_tba_shaper_init(struct pcs_process *proc, struct pcs_tba_shaper *s) 63 | { 64 | s->cbs = 0; 65 | s->capacity = 0; 66 | s->out_rate = 0; 67 | s->last_tick = 0; 68 | init_timer(proc, &s->timer, tba_shaper_timer, s); 69 | } 70 | 71 | void pcs_tba_shaper_fini(struct pcs_tba_shaper *s) 72 | { 73 | tba_shaper_stop(s); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /libpcs_io/pcs_sock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_SOCK_H__ 6 | #define __PCS_SOCK_H__ 7 | 8 | #include "pcs_types.h" 9 | 10 | #ifndef __WINDOWS__ 11 | 12 | #include 13 | typedef int pcs_sock_t; 14 | 15 | #else /* __WINDOWS__ */ 16 | 17 | /* on windows one can adjust max number of fds to be put into fdset for select()... default value is 64... too small... */ 18 | #undef FD_SETSIZE 19 | #define FD_SETSIZE 1024 20 | 21 | /* It's important to have exactly this order of includes (pcs_types.h -> windows.h, then winsock2.h) */ 22 | #include 23 | #include 24 | #include 25 | 26 | typedef SOCKET pcs_sock_t; 27 | extern LPFN_ACCEPTEX pcs_acceptex; 28 | extern LPFN_CONNECTEX pcs_connectex; 29 | extern u8 can_skip_sync_notifications; 30 | 31 | #endif /* __WINDOWS__ */ 32 | 33 | #include "pcs_net_addr.h" 34 | 35 | PCS_API int pcs_network_init(void); 36 | PCS_API int pcs_sock_bind(pcs_sock_t fd, const PCS_NET_ADDR_T * addr); 37 | PCS_API int pcs_sock_connect(pcs_sock_t fd, const PCS_NET_ADDR_T * addr); 38 | PCS_API void pcs_sock_close(pcs_sock_t fd); 39 | PCS_API void pcs_sock_shutdown(pcs_sock_t fd); 40 | PCS_API void pcs_sock_nonblock(pcs_sock_t fd); 41 | PCS_API void pcs_sock_keepalive(pcs_sock_t fd); 42 | PCS_API int pcs_sock_cork(pcs_sock_t fd); 43 | PCS_API void pcs_sock_push(pcs_sock_t fd); 44 | PCS_API void pcs_sock_nodelay(pcs_sock_t fd); 45 | PCS_API int pcs_sock_errno(void); 46 | PCS_API void pcs_sock_setup_buffers(pcs_sock_t fd, int sndbuf, int rcvbuf); 47 | PCS_API int pcs_sock_getpeername(pcs_sock_t fd, PCS_NET_ADDR_T * addr); 48 | PCS_API int pcs_sock_getsockname(pcs_sock_t fd, PCS_NET_ADDR_T * addr); 49 | 50 | static inline int pcs_sock_invalid(pcs_sock_t fd) 51 | { 52 | #ifndef __WINDOWS__ 53 | return fd < 0; 54 | #else 55 | return fd == INVALID_SOCKET; 56 | #endif 57 | } 58 | 59 | #endif /* __PCS_SOCK_H__ */ 60 | -------------------------------------------------------------------------------- /libpcs_io/pcs_shaper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #pragma once 6 | #include "timer.h" 7 | #include "bug.h" 8 | 9 | /* Implementation of Token Bucket Algorithm shaper for case with single bucket */ 10 | 11 | struct pcs_tba_shaper { 12 | u64 cbs; /* committed burst size */ 13 | u64 capacity; /* current capacity in units */ 14 | u32 out_rate; /* outgoing rate in units per seconds */ 15 | abs_time_t last_tick; /* last tick absolute time */ 16 | struct pcs_timer timer; 17 | void* cb_arg; /* argument for wakeup_cb */ 18 | void (*wakeup_cb)(struct pcs_tba_shaper *s); /* called when capacity increased */ 19 | }; 20 | 21 | struct pcs_process; 22 | 23 | #define PCS_SHAPER_TICK_RATE 10 /* milliseconds */ 24 | #define PCS_SHAPER_MIN_RATE (1000 / PCS_SHAPER_TICK_RATE) 25 | 26 | /* initialize shaper instance */ 27 | PCS_API void pcs_tba_shaper_init(struct pcs_process *proc, struct pcs_tba_shaper *s); 28 | 29 | /* deinitialize shaper instance */ 30 | PCS_API void pcs_tba_shaper_fini(struct pcs_tba_shaper *s); 31 | 32 | /* set shaper parameters: 33 | * out_rate - outgoing rate in units per second, out_rate must be greater than 34 | * or equal to PCS_SHAPER_MIN_RATE, 35 | * cbs - committed burst size, must be greater than (out_rate * 1 sec) and greater than 36 | * largest possible request size in units. */ 37 | PCS_API int pcs_tba_shaper_set_rate(struct pcs_tba_shaper *s, u32 out_rate, u64 cbs); 38 | 39 | /* pass units through shaper, return non zero value if units are allowed to processing */ 40 | static inline int pcs_tba_shaper_pass(struct pcs_tba_shaper *s, u64 units) 41 | { 42 | if (!s->out_rate) /* no limit to apply */ 43 | return 1; 44 | 45 | BUG_ON(units > s->cbs); 46 | 47 | if (s->capacity < units) 48 | return 0; 49 | 50 | s->capacity -= units; 51 | return 1; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /libpcs_io/ssl_helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | 6 | #ifndef _SSL_HELPERS_INCLUDED_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "pcs_types.h" 12 | 13 | #define PCS_SSL_PRIVATE_KEY_LEN 2048 14 | 15 | /* generate pair (private and public) of RSA keys */ 16 | PCS_API EVP_PKEY* pcs_gen_rsa_pkey(int key_len); 17 | /* print X509 certificate */ 18 | PCS_API void pcs_print_cert(int log_level, X509* c); 19 | /* convert X509 certificate to PEM format */ 20 | PCS_API char* pcs_cert_to_pem(X509 *c); 21 | /* convert private key to PEM format */ 22 | PCS_API char * pcs_pkey_to_pem(EVP_PKEY *key); 23 | /* read X509 certificate from PEM */ 24 | PCS_API X509* pcs_cert_from_pem(const char *pem, int *out_sz); 25 | PCS_API int pcs_cert_from_pem_file(const char *path, X509 **res); 26 | /* read private key from PEM */ 27 | PCS_API EVP_PKEY *pcs_pkey_from_pem(const char *pem, int *out_sz); 28 | PCS_API int pcs_pkey_from_pem_file(const char *path, EVP_PKEY **res); 29 | /* read a CRL from PEM */ 30 | PCS_API X509_CRL* pcs_crl_from_pem(const char *pem, int *out_sz); 31 | PCS_API int pcs_crl_from_pem_file(const char *path, X509_CRL **res); 32 | /* create X509 certificate and sign it by pkey */ 33 | X509* pcs_create_cert(EVP_PKEY *pkey, const unsigned char *cn); 34 | /* free resources used by EVP_PKEY private key*/ 35 | PCS_API void pcs_free_pkey(EVP_PKEY *pkey); 36 | /* free resources used by X509 certificate */ 37 | PCS_API void pcs_free_cert(X509 *cert); 38 | /* free resources used by a CRL */ 39 | PCS_API void pcs_free_crl(X509_CRL *crl); 40 | 41 | PCS_API void pcs_ssl_print_error(int log_lvl, const char *msg); 42 | 43 | /* returns len of bytes are writen for @out */ 44 | PCS_API int base64_encode(const char *input, int input_len, char *out, int outl); 45 | PCS_API int base64_decode(const char *input, int len, unsigned char *out); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /libpcs_io/pcs_file_job.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_FILE_JOB_H_ 6 | #define _PCS_FILE_JOB_H_ 1 7 | 8 | #include "pcs_process.h" 9 | 10 | #define PCS_FILE_JOB_MAX_THREADS 256 11 | 12 | struct pcs_file_job_conn; 13 | 14 | struct pcs_file_job 15 | { 16 | struct cd_list list; 17 | int retval; 18 | 19 | int (*function)(void * data); 20 | void *data; 21 | 22 | struct pcs_job done; 23 | }; 24 | 25 | struct pcs_file_job_queue 26 | { 27 | struct pcs_file_job_conn *conn; 28 | 29 | pthread_mutex_t lock; 30 | pthread_cond_t wake; 31 | struct cd_list queue; 32 | int free_threads; 33 | int nr_tasks; 34 | int shutdown; 35 | }; 36 | 37 | struct pcs_file_job_conn 38 | { 39 | struct pcs_process *proc; 40 | 41 | int nr_queues; 42 | int nr_threads; /* max total number of threads for all queues */ 43 | int cur_threads; 44 | unsigned int seq; 45 | 46 | struct pcs_file_job_queue queues[PCS_FILE_JOB_MAX_THREADS]; 47 | pcs_thread_t threads[PCS_FILE_JOB_MAX_THREADS]; 48 | 49 | char name[16]; 50 | }; 51 | 52 | PCS_API struct pcs_file_job * pcs_file_job_alloc(int (*fn)(void*), void * arg); 53 | PCS_API void pcs_file_job_free(struct pcs_file_job *j); 54 | PCS_API void pcs_file_job_init(struct pcs_file_job * j, int (*fn)(void*), void * arg); 55 | 56 | PCS_API void pcs_file_job_submit(struct pcs_file_job_conn * io, struct pcs_file_job * j); 57 | PCS_API void pcs_file_job_submit_hash(struct pcs_file_job_conn * io, struct pcs_file_job * j, unsigned int hash); 58 | 59 | int pcs_file_job_conn_start(struct pcs_process * proc, const char *name, struct pcs_file_job_conn ** new_io); 60 | void pcs_file_job_conn_stop(struct pcs_file_job_conn * io); 61 | PCS_API void pcs_file_job_set_queues_threads(struct pcs_file_job_conn * io, int queues, int threads); 62 | PCS_API int pcs_file_job_set_threads(struct pcs_file_job_conn * io, int new_threads); 63 | 64 | #endif /* _PCS_FILE_JOB_H_ */ 65 | -------------------------------------------------------------------------------- /libpcs_io/crc32.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __CRC32_H__ 6 | #define __CRC32_H__ 7 | 8 | #include "pcs_types.h" 9 | #include "pcs_config.h" /* for GCC_VERSION */ 10 | 11 | PCS_API unsigned int pcs_crc32(const unsigned char *s, unsigned int len); 12 | PCS_API unsigned int pcs_crc32up(unsigned int crc, const unsigned char *s, unsigned int len); 13 | 14 | /* ----------------------------------- internal API -------------------------------- */ 15 | 16 | /* 17 | * Performance: 18 | * 64bit: about 1.5GB/sec, i7 2.3Ghz, close to 1.5GB/sec for crc64 19 | * 32bit: about 1.1GB/sec, i7 2.3Ghz 20 | * SSE (below): 22GB/sec 21 | * ARMv8 (below) 1.2GB/sec 22 | * NOTE: performance significantly depends on compiler and almost doubled for me with clang 2014 -> 2016 23 | */ 24 | unsigned int crc32up_generic(unsigned int crc, const unsigned char *s, unsigned int len); 25 | 26 | extern uint32_t crc32_table[4][256]; 27 | 28 | /* not sure about exact gcc version, so put 4.4 from RHEL6 */ 29 | #if (defined(__x86_64__) && (defined(__clang__) || GCC_VERSION >= 40400)) || defined(_WIN64) 30 | 31 | #define HAVE_CRC32_SSE 32 | 33 | PCS_API extern int crc32_use_sse; 34 | 35 | /* about 22GB/sec, i7 2.3Ghz */ 36 | unsigned int crc32up_sse(unsigned int crc, const unsigned char *s, unsigned int len); 37 | unsigned int crc32_sse(const unsigned char *s, unsigned int len); 38 | 39 | #elif defined(__aarch64__) || defined(__ARM_ARCH_8A__) 40 | 41 | #define HAVE_CRC32_ARM 42 | 43 | #define USE_NEON 1 44 | #define USE_ARM_CRC 2 45 | 46 | extern int crc32_use_arm; 47 | 48 | /* 49 | * ARMv8: 50 | * linux 32bit ~1250 MB/sec 51 | */ 52 | unsigned int crc32up_arm_crc(unsigned int crc, const unsigned char *s, unsigned int len); 53 | unsigned int crc32_arm_crc(const unsigned char *s, unsigned int len); 54 | 55 | unsigned int crc32up_neon_pmull_le(unsigned int crc, const unsigned char *s, unsigned int len); 56 | unsigned int crc32_neon_pmull_le(const unsigned char *s, unsigned int len); 57 | 58 | #endif 59 | 60 | #endif /* __CRC32_H__ */ 61 | -------------------------------------------------------------------------------- /libpcs_io/pcs_signalfd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #if (__GLIBC__ == 2 && __GLIBC_MINOR__ < 9) 9 | 10 | #ifndef __linux__ 11 | #error This file may be compiled on Linux only. 12 | #endif 13 | 14 | #ifndef __NR_signalfd 15 | #if defined (__i386__) 16 | #define __NR_signalfd 321 17 | #elif defined (__x86_64__) 18 | #define __NR_signalfd 282 19 | #endif 20 | #endif 21 | 22 | #ifndef __NR_signalfd4 23 | #if defined (__i386__) 24 | #define __NR_signalfd4 327 25 | #elif defined (__x86_64__) 26 | #define __NR_signalfd4 289 27 | #endif 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | struct signalfd_siginfo 35 | { 36 | uint32_t ssi_signo; 37 | int32_t ssi_errno; 38 | int32_t ssi_code; 39 | uint32_t ssi_pid; 40 | uint32_t ssi_uid; 41 | int32_t ssi_fd; 42 | uint32_t ssi_tid; 43 | uint32_t ssi_band; 44 | uint32_t ssi_overrun; 45 | uint32_t ssi_trapno; 46 | int32_t ssi_status; 47 | int32_t ssi_int; 48 | uint64_t ssi_ptr; 49 | uint64_t ssi_utime; 50 | uint64_t ssi_stime; 51 | uint64_t ssi_addr; 52 | uint8_t __pad[48]; 53 | }; 54 | 55 | /* Flags for signalfd. */ 56 | enum 57 | { 58 | SFD_CLOEXEC = 02000000, 59 | #define SFD_CLOEXEC SFD_CLOEXEC 60 | SFD_NONBLOCK = 04000 61 | #define SFD_NONBLOCK SFD_NONBLOCK 62 | }; 63 | 64 | static inline int pcs_signalfd(int fd, const sigset_t *mask, int flags) 65 | { 66 | #if defined __NR_signalfd4 67 | int rv = syscall(__NR_signalfd4, fd, mask, (size_t)8, flags); 68 | if (rv < 0) { 69 | if (flags != 0) { 70 | errno = EINVAL; 71 | return -1; 72 | } 73 | rv = syscall(__NR_signalfd, fd, mask, (size_t)8); 74 | } 75 | return rv; 76 | #elif defined __NR_signalfd 77 | return syscall(__NR_signalfd, fd, mask, (size_t)8); 78 | #else 79 | errno = ENOSYS; 80 | return -1; 81 | #endif 82 | } 83 | #else 84 | #include 85 | static inline int pcs_signalfd (int fd, const sigset_t *mask, int flags) 86 | { 87 | return signalfd(fd, mask, flags); 88 | } 89 | 90 | #endif 91 | 92 | -------------------------------------------------------------------------------- /libpcs_io/logrotate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCSLOGROTATE_H__ 6 | #define __PCSLOGROTATE_H__ 7 | 8 | /* Rotate scheme based on enumeration. The same as in logrotate. */ 9 | #define PCS_LOG_ROTATE_ENUM 0x0 10 | /* Naming scheme which includes timestamps. Log files' names are 11 | * -..log[.gz|.zst]. 12 | * 13 | * NOTE: libpcs doesn't remove old log files in this mode. */ 14 | #define PCS_LOG_ROTATE_MULTIPROC 0x1 15 | 16 | /* 17 | * Log rotation support (buffered writing only). 18 | */ 19 | #define DEF_LOG_ROTATE_FILENUM 5 20 | #define MAX_LOG_ROTATE_FILENUM 100 21 | 22 | struct logrotate { 23 | int rflags; 24 | char* basename; 25 | 26 | /* Extension. For text logs, if compact timestamps are not used it can be 27 | * .log, .gz and .zst, otherwise - .log, .log.gz and .log.zst. For blogs 28 | * The extension must always be .blog. 29 | */ 30 | char ext[9]; 31 | 32 | /* If mode is TS, then the filename itself is changed, existing log files 33 | * are kept intact */ 34 | char** fname_p; 35 | /* limits for multiproc rotation mode */ 36 | int max_nfiles; 37 | unsigned long long max_age_sec; 38 | long long max_total_size; 39 | unsigned long id; 40 | 41 | unsigned filenum; 42 | int request; 43 | long long threshold; 44 | }; 45 | 46 | int logrotate_unlink_unused(const char* base_name, int min_part_idx, int max_part_idx); 47 | void logrotate_apply_limits(char *basename, int nfiles, unsigned long long total_size, abs_time_t age_sec); 48 | int logrotate_run(struct logrotate *l); 49 | 50 | /* 51 | * Checks whether current log file should be rotated and run 52 | * logrotate_run() if so. Returns 1 if rotation skipped, 0 if 53 | * it went OK and <0 on any error. 54 | */ 55 | int logrotate_maybe_run(struct logrotate *l, long long size); 56 | 57 | /* 58 | * Checks whether log files with different exts are present 59 | * and runs logrotate_run() if so. Returns 1 if rotation skipped, 60 | * 0 if it went OK and <0 on any error. 61 | */ 62 | int logrotate_run_alt(struct logrotate *l, const char *fname); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /libpcs_io/pcs_exec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_EXEC_H_ 6 | #define _PCS_EXEC_H_ 1 7 | 8 | #include "pcs_co_locks.h" 9 | 10 | #ifndef __WINDOWS__ 11 | #include 12 | #endif 13 | 14 | struct pcs_co_file; 15 | 16 | struct pcs_exec { 17 | /* Options */ 18 | u32 stdin_from_pipe : 1; /* redirect stdin from pipe */ 19 | u32 stdout_to_pipe : 1; /* redirect stdout to pipe */ 20 | u32 stderr_to_pipe : 1; /* redirect stderr to pipe */ 21 | u32 stdout_to_log : 1; /* read out stdout and dump into pcs_log() */ 22 | u32 stderr_to_log : 1; /* read out stderr and dump into pcs_log() */ 23 | u32 set_sigmask : 1; /* set signal mask to speicified in sigmask field */ 24 | u32 wait_completion : 1; /* wait for completion, no need to call pcs_execute_wait() explicitly */ 25 | 26 | const char *stdout_log_prefix; /* prefix for log messages if stdout_to_log is used */ 27 | const char *stderr_log_prefix; /* prefix for log messages if stderr_to_log is used */ 28 | int stdout_log_level; /* log level for log messages if stdout_to_log is used */ 29 | int stderr_log_level; /* log level for log messages if stderr_to_log is used */ 30 | 31 | /* Output parameters */ 32 | struct pcs_co_file *stdin_pipe; /* local pipe end if stdin_from_pipe is used */ 33 | struct pcs_co_file *stdout_pipe; /* local pipe end if stdout_to_pipe is used */ 34 | struct pcs_co_file *stderr_pipe; /* local pipe end if stderr_to_pipe is used */ 35 | 36 | struct pcs_co_waitgroup wg; /* waitgroup to track process completion */ 37 | int exit_code; /* process exit code filled by pcs_execute_wait() */ 38 | 39 | #ifdef __WINDOWS__ 40 | DWORD pid; /* process ID */ 41 | #else 42 | sigset_t sigmask; /* signal maask if set_sigmask is used */ 43 | pid_t pid; /* PID */ 44 | int exit_status; /* process status information filled by pcs_execute_wait() */ 45 | #endif 46 | }; 47 | 48 | PCS_API int pcs_execute(const char *const argv[], struct pcs_exec *e); 49 | PCS_API void pcs_execute_wait(struct pcs_exec *e); 50 | 51 | int pcs_co_file_to_log(int log_level, const char *prefix, struct pcs_co_file *file); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /libpcs_io/pcs_dir.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_DIR_H_ 6 | #define _PCS_DIR_H_ 1 7 | 8 | #include "pcs_types.h" 9 | #include "timer.h" 10 | 11 | #ifdef _WINDOWS_ 12 | #define PCS_PATH_SEP '\\' 13 | #else 14 | #define PCS_PATH_SEP '/' 15 | #endif 16 | 17 | struct pcs_stat { 18 | u32 mode; 19 | u32 nlink; 20 | u32 uid; 21 | u32 gid; 22 | u64 dev; /* device on which file resides */ 23 | u64 rdev; /* device for CHRDEV or BLKDEV, tag for REPARSE POINT */ 24 | u64 ino; 25 | u64 size; /* in bytes */ 26 | u64 allocated; /* in bytes */ 27 | u32 flags; /* user flags on Mac, DOS attributes on Windows */ 28 | abs_time_t mtime_ns; 29 | abs_time_t ctime_ns; /* status change time */ 30 | }; 31 | 32 | typedef struct pcs_dirent { 33 | char *name; 34 | char *short_name; 35 | struct pcs_stat stat; 36 | } pcs_dirent_t; 37 | 38 | #define PCS_DIRENT_STAT 1 39 | #define PCS_DIRENT_SHORT_NAMES 2 40 | 41 | /* Open given directory and find first entry. 42 | * If PCS_DIRENT_STAT flag is specified, query file stat on POSIX systems, 43 | * otherwise only file type in stat.mode and stat.ino are filled. 44 | * On Windows PCS_DIRENT_STAT flag is ignored. 45 | * Returns 1 if entry is found, 0 if there is no more entries and sets *out_dir pointer on success. 46 | * On error negative code is returned and *out_dir is left unchanged. */ 47 | PCS_API __must_check int pcs_dirent_first(const char *path, u32 flags, pcs_dirent_t **out_dir); 48 | PCS_API __must_check int pcs_dirent_firstat(pcs_fd_t dirfd, const char *pathname, u32 flags, pcs_dirent_t **out_dir); 49 | PCS_API __must_check int pcs_dirent_next(pcs_dirent_t *dir); 50 | 51 | /* Close dir entry and free resources */ 52 | PCS_API void pcs_dirent_close(pcs_dirent_t *dir); 53 | 54 | /* Coroutine-based versions. Look above for explanation. */ 55 | PCS_API __must_check int pcs_co_dirent_first(const char *path, u32 flags, pcs_dirent_t **out_dir); 56 | PCS_API __must_check int pcs_co_dirent_firstat(pcs_fd_t dirfd, const char *pathname, u32 flags, pcs_dirent_t **out_dir); 57 | PCS_API __must_check int pcs_co_dirent_next(pcs_dirent_t *dir); 58 | PCS_API void pcs_co_dirent_close(pcs_dirent_t *dir); 59 | 60 | #endif /* _PCS_DIR_H_ */ 61 | -------------------------------------------------------------------------------- /libpcs_io/jhash.h: -------------------------------------------------------------------------------- 1 | #ifndef _PCS_JHASH_H_ 2 | #define _PCS_JHASH_H_ 1 3 | 4 | #include "pcs_types.h" 5 | 6 | /* 7 | http://www.burtleburtle.net/bob/c/lookup2.c 8 | -------------------------------------------------------------------- 9 | lookup2.c, by Bob Jenkins, December 1996, Public Domain. 10 | -------------------------------------------------------------------- 11 | */ 12 | 13 | #define jmix(a,b,c) \ 14 | { \ 15 | a -= b; a -= c; a ^= (c>>13); \ 16 | b -= c; b -= a; b ^= (a<<8); \ 17 | c -= a; c -= b; c ^= (b>>13); \ 18 | a -= b; a -= c; a ^= (c>>12); \ 19 | b -= c; b -= a; b ^= (a<<16); \ 20 | c -= a; c -= b; c ^= (b>>5); \ 21 | a -= b; a -= c; a ^= (c>>3); \ 22 | b -= c; b -= a; b ^= (a<<10); \ 23 | c -= a; c -= b; c ^= (b>>15); \ 24 | } 25 | 26 | /* the golden ratio; an arbitrary value */ 27 | #define JHASH_MAGIC 0x9e3779b9 28 | 29 | static inline u32 jhash2( 30 | u32 const* k, /* the key */ 31 | u32 length, /* the length of the key, in u32s */ 32 | u32 initval /* the previous hash, or an arbitrary value */ 33 | ) { 34 | register u32 a,b,c,len; 35 | 36 | /* Set up the internal state */ 37 | len = length; 38 | a = b = JHASH_MAGIC; /* the golden ratio; an arbitrary value */ 39 | c = initval; /* the previous hash value */ 40 | 41 | /*---------------------------------------- handle most of the key */ 42 | while (len >= 3) 43 | { 44 | a += k[0]; 45 | b += k[1]; 46 | c += k[2]; 47 | jmix(a,b,c); 48 | k += 3; len -= 3; 49 | } 50 | 51 | /*-------------------------------------- handle the last 2 ub4's */ 52 | c += (length<<2); 53 | switch(len) /* all the case statements fall through */ 54 | { 55 | /* c is reserved for the length */ 56 | case 2 : b+=k[1]; /* FALLTHROUGH */ 57 | case 1 : a+=k[0]; 58 | /* case 0: nothing left to add */ 59 | } 60 | jmix(a,b,c); 61 | /*-------------------------------------------- report the result */ 62 | return c; 63 | } 64 | 65 | /* This is special version of lookup2, handling hashing of 3 u32 words only. */ 66 | static inline u32 jhash3(u32 a, u32 b, u32 c, u32 initval) 67 | { 68 | a += JHASH_MAGIC; 69 | b += JHASH_MAGIC; 70 | c += initval; 71 | jmix(a, b, c); 72 | return c; 73 | } 74 | 75 | #endif /* _PCS_JHASH_H_ */ 76 | -------------------------------------------------------------------------------- /libpcs_io/minheap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #pragma once 6 | 7 | /* 8 | This file implement a min-heap. 9 | 10 | A quick recap (see https://algs4.cs.princeton.edu/24pq/ for more detail): 11 | 12 | Min-heap is a perfectly balanced binary tree of items that have weights (or priorities) 13 | associated to them. This tree satisfies the "min-heap property": the weight of a node 14 | is less than or equal to weights of the children of it. Perfect balancing means that 15 | all leaf nodes are located at the same depth. 16 | 17 | Min-heap supports the following operations with complexity logarithmic in the size of a heap: 18 | * insert an item, 19 | * remove an item of the minimal weight, 20 | * associate a new weight to an item. 21 | 22 | A classical implementation of a min-heap uses an array of pointers to items of a heap, 23 | where indices of children of an item k are 2*k+1 and 2*k+2. 24 | 25 | This implementation does not allocate additional memory. Instead, it requires users 26 | to embed struct mh_node into items that are added to a heap. It also allows to remove 27 | any item, not just the minimal-weight one (also in logarithmic time). 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | struct mh_node 35 | { 36 | struct cd_list siblings; /* each level of a heap is organised as a circular list */ 37 | struct mh_node *parent; 38 | struct mh_node *left, *right; 39 | }; 40 | 41 | struct mh_root 42 | { 43 | struct mh_node *root; 44 | 45 | /* private to minheap */ 46 | u32 count; 47 | struct mh_node *bottom_right; 48 | 49 | int (*cmp)(struct mh_node *x, struct mh_node *y); 50 | }; 51 | 52 | 53 | #define mh_entry(ptr, type, member) container_of(ptr, type, member) 54 | 55 | static inline void mh_init(struct mh_root *h, int (*cmp)(struct mh_node *x, struct mh_node *y)) 56 | { 57 | h->root = NULL; 58 | h->count = 0; 59 | h->bottom_right = NULL; 60 | h->cmp = cmp; 61 | } 62 | 63 | static inline int mh_empty(const struct mh_root *h) 64 | { 65 | return h->count == 0; 66 | } 67 | 68 | PCS_API void mh_insert(struct mh_root *h, struct mh_node *n); 69 | PCS_API void mh_delete(struct mh_root *h, struct mh_node *n); 70 | /* Correct the location of a node @n in a minheap @h after the weight of it is changed. */ 71 | PCS_API void mh_reweigh_node(struct mh_root *h, struct mh_node *n); 72 | -------------------------------------------------------------------------------- /libpcs_io/bug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_BUG_H_INCLUDED 6 | #define _PCS_BUG_H_INCLUDED 7 | 8 | #include "pcs_types.h" 9 | 10 | #define BUG_ON_ENABLE 11 | 12 | #ifndef BUG_ON_ENABLE 13 | #include 14 | #endif 15 | 16 | /* 17 | * Bug checking related stuff 18 | */ 19 | 20 | /* Bug check context structure */ 21 | struct bug_point { 22 | const char* file; 23 | const char* func; 24 | int line; 25 | int active; 26 | unsigned long long hits; 27 | }; 28 | 29 | #define BUG_POINT_INITIALIZER {__FILE__, __FUNCTION__, __LINE__, 0, 0} 30 | 31 | #ifdef BUG_ON_ENABLE 32 | #define BUG() do {pcs_bug(__FILE__, __LINE__, __FUNCTION__);} while(0) 33 | #define BUG_() do {static struct bug_point bp = BUG_POINT_INITIALIZER; pcs_bug_at(&bp);} while(0) 34 | #else 35 | #define BUG() do {abort();} while(0) 36 | #define BUG_() do {abort();} while(0) 37 | #endif 38 | 39 | extern int pcs_dbg_bugon_enable; 40 | 41 | #define BUG_ON(cond) do {if (unlikely(cond)) BUG_();} while(0) 42 | #define DBG_BUG_ON(cond) do {if (pcs_dbg_bugon_enable && (cond)) BUG();} while(0) 43 | #define DBG_BUG() do {if (pcs_dbg_bugon_enable) BUG();} while(0) 44 | #ifndef _MSC_VER 45 | #define BUILD_BUG_ON(cond) extern void __build_bug_on_dummy(char a[1 - 2*!!(cond)]) 46 | #else 47 | #define BUILD_BUG_ON(cond) static_assert(!(cond), #cond) 48 | #endif 49 | #define CHECK_ALLOC(ptr) do {if (unlikely(!(ptr))) pcs_err("allocation failed", __FILE__, __LINE__, __FUNCTION__);} while(0) 50 | 51 | /* Terminate execution */ 52 | PCS_API void __noreturn pcs_bug(const char *file, int line, const char *func); 53 | 54 | /* Same as above but may be optionally ignored */ 55 | PCS_API void pcs_bug_at(struct bug_point* bp); 56 | 57 | /* Add bugon exception. Expected to be called at the application initialization stage only. 58 | * May return PCS_ERR_NOMEM on allocation failure. 59 | */ 60 | PCS_API int pcs_bug_ignore(const char *file, int line); 61 | 62 | /* Accept file:line specification. May return PCS_ERR_NOMEM or PCS_ERR_INV_PARAMS errors. */ 63 | PCS_API int pcs_bug_ignore_spec(const char *spec); 64 | 65 | /* Read PSTORAGE_DBG_BUGON environment variable and set pcs_dbg_bugon_enable accordingly */ 66 | PCS_API void pcs_dbg_bugon_init(void); 67 | 68 | /* Abort execution gracefully terminating log and calling registerd handlers (see below) */ 69 | PCS_API void __noreturn pcs_abort(void); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /libpcs_io/pcs_profiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_PROFILER_H_ 6 | #define _PCS_PROFILER_H_ 1 7 | 8 | #include "pcs_config.h" 9 | #include "pcs_types.h" 10 | 11 | #if defined(HAVE_POSIX_TIMER) && !defined(PCS_PROFILER_DISABLE) && !defined(PCS_THREAD_SANIT) 12 | #define PCS_USE_PROFILER 1 13 | #include 14 | #include 15 | #endif 16 | 17 | 18 | #define PROFILER_SIGNO 35 19 | 20 | struct pcs_evloop; 21 | typedef void (*pcs_profiler_cb)(void* ctx, void* uc, int overrun); 22 | 23 | struct pcs_profiler 24 | { 25 | #ifdef PCS_USE_PROFILER 26 | timer_t ptimer; 27 | u64 *pbuffer; 28 | int pptr; 29 | int ptotal; 30 | int pactive; 31 | volatile int ptimer_active; 32 | volatile pcs_profiler_cb priv_cb; 33 | void* volatile priv_ctx; 34 | #else 35 | int to_make_compiler_happy; 36 | #endif 37 | }; 38 | 39 | #ifdef PCS_USE_PROFILER 40 | 41 | extern int __pcs_profiler_enabled; 42 | 43 | static inline void pcs_profile_enable(int enable) 44 | { 45 | __pcs_profiler_enabled = enable; 46 | } 47 | 48 | void pcs_profiler_start(struct pcs_evloop * evloop); 49 | void pcs_profiler_stop(struct pcs_evloop * evloop); 50 | void pcs_profiler_enter(struct pcs_evloop * evloop); 51 | void pcs_profiler_leave(struct pcs_evloop * evloop, int dump); 52 | void pcs_profiler_block(struct pcs_evloop * evloop, sigset_t * oldmask); 53 | void pcs_profiler_unblock(struct pcs_evloop * evloop, sigset_t * oldmask); 54 | void * pcs_profiler_last_pc(struct pcs_evloop * evloop); 55 | void pcs_profiler_set_callback(struct pcs_evloop * evloop, pcs_profiler_cb cb, void* ctx); 56 | 57 | #else 58 | static inline void pcs_profile_enable(int enable) {}; 59 | static inline void pcs_profiler_start(struct pcs_evloop * evloop) {}; 60 | static inline void pcs_profiler_stop(struct pcs_evloop * evloop) {}; 61 | static inline void pcs_profiler_enter(struct pcs_evloop * evloop) {}; 62 | static inline void pcs_profiler_leave(struct pcs_evloop * evloop, int dump) {}; 63 | /* avoid definition of sigset_t */ 64 | #define pcs_profiler_block(evloop, oldmask) do {(void)oldmask;} while (0) 65 | #define pcs_profiler_unblock(evloop, oldmask) do {(void)oldmask;} while (0) 66 | static inline void *pcs_profiler_last_pc(struct pcs_evloop * evloop) { return NULL; }; 67 | static inline void pcs_profiler_set_callback(struct pcs_evloop * evloop, pcs_profiler_cb cb, void* ctx) {} 68 | #endif 69 | 70 | 71 | #endif /* _PCS_PROFILER_H_ */ 72 | -------------------------------------------------------------------------------- /libpcs_io/crc32_arm_wrapper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include 6 | #include "crc32.h" 7 | #include "pcs_endian.h" 8 | #include "pcs_align.h" 9 | #include "bug.h" 10 | 11 | #ifdef HAVE_CRC32_ARM 12 | 13 | int crc32_use_arm = USE_NEON | USE_ARM_CRC; 14 | 15 | /* 16 | * len - sizeof buffer (multiple of 16 bytes), len should be > 63 17 | */ 18 | unsigned int crc32c_pmull_le(const unsigned char* buf, unsigned int len, unsigned crc); 19 | 20 | /* 21 | * len - sizeof buffer (any) 22 | */ 23 | unsigned int crc32c_armv8_le(unsigned int init_crc, const unsigned char* buf, unsigned int len); 24 | unsigned int crc32c_armv8_be(unsigned int init_crc, const unsigned char* buf, unsigned int len); 25 | 26 | 27 | inline unsigned int crc32up_neon_pmull_le(unsigned int crc, const unsigned char *s, unsigned int len) 28 | { 29 | unsigned char tmp[68]; 30 | unsigned int tail = len % 64; 31 | unsigned int len64 = len - tail; 32 | unsigned int tcrc; 33 | 34 | crc ^= 0xffffffff; 35 | 36 | if (len64) 37 | crc = crc32c_pmull_le(s, len64, crc); 38 | 39 | if (tail) { 40 | tcrc = crc; 41 | memset(tmp, 0, sizeof(tmp)); 42 | memcpy(tmp + 64 - tail, s + len64, tail); 43 | *(unsigned int*) (tmp + 64 - tail) ^= crc; 44 | crc = crc32c_pmull_le(tmp, 64, 0); 45 | 46 | if (tail < 4) 47 | crc ^= tcrc >> (8 * tail); 48 | } 49 | 50 | crc ^= 0xffffffff; 51 | 52 | return crc; 53 | } 54 | 55 | inline unsigned int crc32_neon_pmull_le(const unsigned char *s, unsigned int len) 56 | { 57 | return crc32up_neon_pmull_le(0, s, len); 58 | } 59 | 60 | inline unsigned int crc32up_arm_crc(unsigned int crc, const unsigned char *s, unsigned int len) 61 | { 62 | crc ^= 0xffffffff; 63 | 64 | #ifdef __ARM_ARCH_8A__ 65 | unsigned char begin[4] __aligned(4) = {0}; 66 | unsigned int shift = ((unsigned long) s) & 3; 67 | unsigned int pref; 68 | 69 | if (shift) { 70 | pref = 4 - shift; 71 | memcpy(begin, s, sizeof(begin)); 72 | len -= pref; 73 | 74 | crc = pcs_cpu_little_endian() 75 | ? crc32c_armv8_le(crc, begin, pref) 76 | : crc32c_armv8_be(crc, begin, pref); 77 | } 78 | 79 | if (len) 80 | #endif /* __ARM_ARCH_8A__ */ 81 | crc = pcs_cpu_little_endian() 82 | ? crc32c_armv8_le(crc, s, len) 83 | : crc32c_armv8_be(crc, s, len); 84 | 85 | crc ^= 0xffffffff; 86 | return crc; 87 | } 88 | 89 | inline unsigned int crc32_arm_crc(const unsigned char *s, unsigned int len) 90 | { 91 | return crc32up_arm_crc(0, s, len); 92 | } 93 | 94 | #endif /* HAVE_CRC32_ARM */ 95 | -------------------------------------------------------------------------------- /libpcs_io/pcs_rdma_int.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_RDMA_INT_H_ 6 | #define _PCS_RDMA_INT_H_ 1 7 | 8 | #include 9 | 10 | /* Internals shared among pcs_rdma_*.[ch] */ 11 | 12 | struct pcs_rdmaio *rio_create(struct pcs_process * proc, int hdr_size, 13 | struct rdma_cm_id *cmid, int queue_depth); 14 | void rio_ioconn_init(struct pcs_rdmaio *rio); 15 | void rio_destroy(struct pcs_rdmaio *rio); 16 | 17 | static inline void 18 | conn_param_init(struct rdma_conn_param *cp, struct pcs_rdmaio_conn_req *cr) 19 | { 20 | memset(cp, 0, sizeof(*cp)); 21 | 22 | if (cr) { 23 | cp->private_data = cr; 24 | cp->private_data_len = sizeof(*cr); 25 | } 26 | 27 | /* these two guys are about RDMA reads: see man rdma_connect(3) */ 28 | cp->responder_resources = RDMA_MAX_RESP_RES; 29 | cp->initiator_depth = RDMA_MAX_INIT_DEPTH; 30 | 31 | cp->flow_control = 1; /* does not matter */ 32 | cp->retry_count = 5; /* # retransmissions when no ACK received */ 33 | cp->rnr_retry_count = 5; /* # RNR retransmissions */ 34 | } 35 | 36 | /* From rdma-core/libibcm/cm.h */ 37 | enum ib_cm_rej_reason { 38 | IB_CM_REJ_INVALID_SERVICE_ID = 8, 39 | IB_CM_REJ_CONSUMER_DEFINED = 28, 40 | }; 41 | 42 | /* From rdma/ib_verbs.h */ 43 | enum ib_qp_state { 44 | IB_QPS_RESET, 45 | IB_QPS_INIT, 46 | IB_QPS_RTR, 47 | IB_QPS_RTS, 48 | IB_QPS_SQD, 49 | IB_QPS_SQE, 50 | IB_QPS_ERR 51 | }; 52 | 53 | static inline void pcs_log_errno(char *where, char *who) 54 | { 55 | pcs_log(LOG_ERR, "%s(): %s failed: %s", 56 | where, who, strerror(errno)); 57 | } 58 | 59 | static inline void pcs_log_event(char *where, int event, int status) 60 | { 61 | char *s; 62 | 63 | switch (event) { 64 | case RDMA_CM_EVENT_CONNECT_REQUEST: 65 | s = "RDMA_CM_EVENT_CONNECT_REQUEST"; 66 | break; 67 | case RDMA_CM_EVENT_ESTABLISHED: 68 | s = "RDMA_CM_EVENT_ESTABLISHED"; 69 | break; 70 | case RDMA_CM_EVENT_REJECTED: 71 | s = "RDMA_CM_EVENT_REJECTED"; 72 | break; 73 | case RDMA_CM_EVENT_ADDR_RESOLVED: 74 | s = "RDMA_CM_EVENT_ADDR_RESOLVED"; 75 | break; 76 | case RDMA_CM_EVENT_ROUTE_RESOLVED: 77 | s = "RDMA_CM_EVENT_ROUTE_RESOLVED"; 78 | break; 79 | case RDMA_CM_EVENT_DISCONNECTED: 80 | s = "RDMA_CM_EVENT_DISCONNECTED"; 81 | break; 82 | case RDMA_CM_EVENT_DEVICE_REMOVAL: 83 | s = "RDMA_CM_EVENT_DEVICE_REMOVAL"; 84 | break; 85 | default: 86 | s = "UNKNOWN"; 87 | } 88 | 89 | pcs_log(LOG_ERR, "%s(): RDMA event %s/%d with status %d", 90 | where, s, event, status); 91 | } 92 | 93 | #endif /* _PCS_RDMA_INT_H_ */ 94 | -------------------------------------------------------------------------------- /libpcs_io/pcs_thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_THREAD_H__ 6 | #define __PCS_THREAD_H__ 7 | 8 | #include "pcs_types.h" 9 | 10 | #include 11 | #define pcs_thread_t pthread_t 12 | #define pcs_thread_ret_t void* 13 | 14 | typedef struct pcs_thread_attr { 15 | unsigned long stack_size; 16 | } pcs_thread_attr_t; 17 | 18 | int pcs_thread_create(pcs_thread_t * thread, const pcs_thread_attr_t * attr, pcs_thread_ret_t (*start_routine)(void *), void * arg); 19 | int pcs_thread_join(pcs_thread_t thread); 20 | pcs_thread_t pcs_thread_self(void); 21 | int pcs_thread_equal(pcs_thread_t t1, pcs_thread_t t2); 22 | 23 | int pcs_thread_timedjoin(pcs_thread_t thread, void **retval, unsigned int timeout_ms); 24 | int pcs_thread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, unsigned int timeout_ms); 25 | void pcs_thread_deadline(struct timespec * ts, unsigned int timeout_ms); 26 | PCS_API void pcs_thread_setname(const char *name); 27 | unsigned long pcs_thread_id(void); 28 | 29 | struct pcs_thread_barrier { 30 | pthread_mutex_t mutex; 31 | pthread_cond_t cond; 32 | unsigned limit; 33 | unsigned count; 34 | unsigned phase; 35 | }; 36 | 37 | PCS_API void pcs_thread_barrier_init(struct pcs_thread_barrier *barrier, unsigned limit); 38 | PCS_API void pcs_thread_barrier_fini(struct pcs_thread_barrier *barrier); 39 | PCS_API void pcs_thread_barrier_wait(struct pcs_thread_barrier *barrier); 40 | PCS_API void pcs_thread_barrier_reset(struct pcs_thread_barrier *barrier, unsigned limit); 41 | 42 | /* ------------------------------------------------------------------------------------------------ */ 43 | /* 44 | * TLS compatibility with Windows delay load, where __thread variables do not work properly. 45 | * Ideally this code should be killed when delay load is removed and all context variables 46 | * should be simply defined as __thread in places they are needed. 47 | */ 48 | 49 | #include "pcs_config.h" 50 | 51 | struct pcs_evloop; 52 | struct pcs_coroutine; 53 | 54 | struct __pcs_current { 55 | struct pcs_process *proc; 56 | struct pcs_evloop *evloop; 57 | struct pcs_coroutine *co; 58 | }; 59 | 60 | PCS_API struct __pcs_current * pcs_thread_tls(void); 61 | #ifdef HAVE_TLS_STATIC 62 | PCS_API extern __thread struct __pcs_current __pcs_thread_tls; 63 | #define pcs_thread_tls() (&__pcs_thread_tls) 64 | #else 65 | void pcs_thread_tls_free(void); 66 | void pcs_process_tls_alloc(void); 67 | void pcs_process_tls_free(void); 68 | #endif 69 | 70 | /* ------------------------------------------------------------------------------------------------ */ 71 | 72 | #endif /* __PCS_THREAD_H__ */ 73 | -------------------------------------------------------------------------------- /libpcs_io/timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __TIMER_H__ 6 | #define __TIMER_H__ 7 | 8 | #include "pcs_types.h" 9 | #include "pcs_config.h" 10 | #include "rbtree.h" 11 | #include "std_list.h" 12 | #include "pcs_atomic.h" 13 | #include "pcs_thread.h" 14 | 15 | typedef unsigned long long abs_time_t; 16 | typedef long long time_diff_t; 17 | 18 | struct pcs_process; 19 | struct pcs_evloop; 20 | 21 | #ifdef __WINDOWS__ 22 | struct timeval; 23 | struct timezone; 24 | PCS_API int gettimeofday(struct timeval * tp, struct timezone * tzp); 25 | PCS_API abs_time_t filetime2ns(const FILETIME *ft); 26 | PCS_API void ns2filetime(abs_time_t ns, FILETIME *filetime); 27 | #endif /* __WINDOWS__ */ 28 | 29 | struct pcs_timer 30 | { 31 | union { 32 | struct rb_node node; /* used when expires != 0 */ 33 | struct cd_list list; /* used when expires == 0 */ 34 | }; 35 | abs_time_t expires; 36 | 37 | struct pcs_process *proc; 38 | pcs_atomic_ptr_t evloop; /* not NULL when timer is armed */ 39 | 40 | void (*function)(void *); 41 | void *data; 42 | }; 43 | 44 | /* 45 | * NOTE: timers can be safely armed: 46 | * 1. before pcs_process started 47 | * 2. in evloop context only (after pcs_process started). 48 | * No remote timer arming from other threads supported 49 | */ 50 | PCS_API void init_timer(struct pcs_process *proc, struct pcs_timer *timer, void (*function)(void *), void *data); 51 | PCS_API void mod_timer(struct pcs_timer *timer, time_diff_t ms); 52 | PCS_API void del_timer_sync(struct pcs_timer *timer); 53 | PCS_API int timer_pending(struct pcs_timer *timer); 54 | 55 | /* The abs_time functions are expected to return monotonic time with arbitrary offset. 56 | * Note that in practice they may return slightly different values on different CPU cores. 57 | * So always use signed result while calculating difference between 2 time values (time_diff_t) 58 | * or call get_elapsed_time(). 59 | */ 60 | PCS_API abs_time_t get_abs_time_ms(void); 61 | PCS_API abs_time_t get_abs_time_us(void); 62 | PCS_API abs_time_t get_real_time_ms(void); 63 | PCS_API abs_time_t get_real_time_us(void); 64 | PCS_API abs_time_t normalize_abs_time_us(abs_time_t t); 65 | 66 | static inline abs_time_t get_elapsed_time(abs_time_t now, abs_time_t old) 67 | { 68 | time_diff_t elapsed = now - old; 69 | return elapsed > 0 ? elapsed : 0; 70 | } 71 | 72 | /* ------------------- Internal API ----------------------- */ 73 | 74 | struct pcs_timer_tree 75 | { 76 | struct rb_root root; 77 | pthread_mutex_t lock; 78 | struct pcs_timer *exec_timer; 79 | pthread_cond_t cond; 80 | int notify_cond; 81 | }; 82 | 83 | void init_timers(struct pcs_timer_tree *); 84 | void fini_timers(struct pcs_timer_tree *); 85 | int check_timers(struct pcs_evloop *); 86 | int get_timers_timeout(struct pcs_timer_tree *); 87 | void abort_timers(struct pcs_timer_tree *); 88 | 89 | #endif /* __TIMER_H__ */ 90 | -------------------------------------------------------------------------------- /libpcs_io/pcs_ioconn.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_types.h" 6 | #include "pcs_malloc.h" 7 | #include "pcs_ioconn.h" 8 | #include "pcs_poll.h" 9 | #include "pcs_process.h" 10 | #include "log.h" 11 | 12 | #include 13 | 14 | static void hup_callback(struct pcs_ioconn * conn) 15 | { 16 | } 17 | 18 | void pcs_ioconn_close(struct pcs_ioconn * conn) 19 | { 20 | if (conn->fd >= 0) { 21 | close(conn->fd); 22 | conn->fd = -1; 23 | } 24 | } 25 | 26 | void pcs_ioconn_destruct(struct pcs_ioconn * conn) 27 | { 28 | pcs_ioconn_close(conn); 29 | pcs_free(conn); 30 | } 31 | 32 | void pcs_ioconn_init(struct pcs_process * proc, struct pcs_ioconn * conn) 33 | { 34 | cd_list_init(&conn->list); 35 | pcs_ioconn_reset(conn); 36 | conn->dead = 0; 37 | conn->actual_mask = 0; 38 | conn->next_mask = 0; 39 | conn->proc = proc; 40 | conn->data_ready = conn->write_space = conn->error_report = hup_callback; 41 | conn->destruct = pcs_ioconn_destruct; 42 | } 43 | 44 | void pcs_ioconn_register(struct pcs_ioconn * conn) 45 | { 46 | cd_list_add_tail(&conn->list, &conn->proc->ioconns); 47 | 48 | conn->actual_mask = 0; 49 | if (conn->next_mask) 50 | pcs_ioconn_schedule(conn); 51 | } 52 | 53 | void pcs_ioconn_unregister(struct pcs_ioconn * conn) 54 | { 55 | if (conn->dead) 56 | return; 57 | 58 | conn->data_ready = conn->write_space = conn->error_report = hup_callback; 59 | 60 | conn->next_mask = 0; 61 | pcs_ioconn_schedule(conn); 62 | 63 | cd_list_del(&conn->list); 64 | conn->dead = 1; 65 | 66 | if (!conn->destruct) 67 | return; 68 | 69 | struct pcs_process *proc = conn->proc; 70 | if (!pcs_process_is_running(proc)) { 71 | conn->destruct(conn); 72 | } else { 73 | cd_list_add_tail(&conn->list, &proc->kill_list); 74 | pcs_job_wakeup(&proc->kill_ioconn_job); 75 | } 76 | } 77 | 78 | void pcs_ioconn_schedule(struct pcs_ioconn * conn) 79 | { 80 | if (conn->actual_mask == conn->next_mask || conn->dead) 81 | return; 82 | 83 | struct pcs_process *proc = conn->proc; 84 | 85 | int err = pcs_poll_ctl(proc, conn); 86 | if (err) { 87 | pcs_log(LOG_ERR, "epoll error=%d %08x->%08x", err, conn->actual_mask, conn->next_mask); 88 | BUG(); 89 | } 90 | 91 | if (conn->actual_mask == 0) 92 | proc->n_ioconns++; 93 | else if (conn->next_mask == 0) 94 | proc->n_ioconns--; 95 | 96 | conn->actual_mask = conn->next_mask; 97 | } 98 | 99 | /* called via pcs_ioconn_unregister -> proc.kill_ioconn_job job */ 100 | void ioconn_kill_all(void * arg) 101 | { 102 | struct pcs_process * proc = (struct pcs_process *)arg; 103 | struct pcs_ioconn * conn; 104 | 105 | while (!cd_list_empty(&proc->kill_list)) { 106 | conn = cd_list_first_entry(&proc->kill_list, struct pcs_ioconn, list); 107 | cd_list_del(&conn->list); 108 | conn->destruct(conn); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /libpcs_io/pool_allocator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #pragma once 6 | 7 | /* This is the almost zero overhead pool allocator best suited for small objects. 8 | * Objects larger than some fraction of the page size would be better served by 9 | * standard malloc. The allocator initialization function just returns -1 for 10 | * such object sizes. 11 | * 12 | * The allocator consists of 2 parts - the central pool of pages and the 13 | * allocator specialized on allocating objects of the particular size. 14 | * 15 | * The allocator does not care about thread safety. It must be ensured 16 | * externally if necessary. 17 | */ 18 | 19 | #include "std_list.h" 20 | 21 | /* Define to enforce page faults on accessing freed chunks */ 22 | //#define MEM_POOL_GUARD 23 | 24 | /* Define for testing */ 25 | //#define MEM_POOL_DEBUG 26 | 27 | #ifdef MEM_POOL_GUARD 28 | #define MEM_POOL_DEBUG 29 | #endif 30 | 31 | /* How many pages will be allocated at once */ 32 | #ifdef MEM_POOL_DEBUG 33 | #define MEM_POOL_PREALLOC_PGS 0x1 34 | #else 35 | #define MEM_POOL_PREALLOC_PGS 0x100 36 | #endif 37 | 38 | /* The allocation alignment */ 39 | #define MEM_POOL_ALLOC_ALIGN sizeof(void*) 40 | 41 | /* The minimum number of allocations per page. 42 | * Note that below some threshold using malloc becomes more space efficient. 43 | */ 44 | #define MEM_POOL_MIN_ALLOCS_PER_PAGE 6 45 | 46 | struct pool_allocator { 47 | /* Allocation size */ 48 | unsigned size; 49 | /* The number of chunks allocated on single page */ 50 | unsigned chunks_per_page; 51 | 52 | /* The list of allocated pages */ 53 | struct cd_list pgs_used; /* Partially used pages */ 54 | struct cd_list pgs_full; /* Full pages */ 55 | 56 | /* Statistics */ 57 | unsigned long long pgs_cnt; /* Total pages in both lists */ 58 | }; 59 | 60 | struct mem_pool { 61 | /* Free page lists */ 62 | struct cd_list pgs_free; 63 | struct cd_list pgs_standby; 64 | 65 | /* Standby links allocator */ 66 | struct pool_allocator standby_allocator; 67 | 68 | /* Statistics */ 69 | unsigned long long pgs_free_cnt; /* The current number of free pages */ 70 | unsigned long long pgs_standby_cnt;/* The current number of standby pages */ 71 | unsigned long long pgs_allocated; /* Total number of allocated pages */ 72 | }; 73 | 74 | /* Initialize pool */ 75 | PCS_API void pool_init(struct mem_pool* p); 76 | 77 | /* Initialize pool allocator, may return -1 if the size is not suitable for the pool allocation. */ 78 | PCS_API int pool_allocator_init(struct pool_allocator* a, unsigned size); 79 | 80 | /* Check if pool allocator was initialized successfully */ 81 | static inline int pool_allocator_valid(struct pool_allocator* a) 82 | { 83 | return a->chunks_per_page > 0; 84 | } 85 | 86 | /* Allocate chunk */ 87 | PCS_API void* pool_alloc(struct mem_pool* p, struct pool_allocator* a); 88 | 89 | /* Release chunk */ 90 | PCS_API void pool_free(struct mem_pool* p, struct pool_allocator* a, void* ptr); 91 | -------------------------------------------------------------------------------- /libpcs_io/bug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "log.h" 6 | 7 | #include 8 | 9 | #include "pcs_malloc.h" 10 | #include "pcs_profiler.h" 11 | #include "pcs_atexit.h" 12 | 13 | struct bugon_exception { 14 | struct bugon_exception* next; 15 | const char* file; 16 | int line; 17 | }; 18 | 19 | /* Bugon exceptions */ 20 | struct bugon_exception* bugon_except_list; 21 | 22 | /* Enable debug bugons */ 23 | int pcs_dbg_bugon_enable; 24 | 25 | void __noreturn pcs_abort(void) 26 | { 27 | pcs_log_terminate(); 28 | 29 | /* block profiler signals to avoid truncated core dumps */ 30 | pcs_profiler_block(NULL, NULL); 31 | 32 | pcs_call_atexit(exit_abort); 33 | abort(); 34 | } 35 | 36 | /* Terminate execution */ 37 | void pcs_bug(const char *file, int line, const char *func) 38 | { 39 | pcs_err("BUG", file, line, func); 40 | } 41 | 42 | /* Same as above but may be optionally ignored */ 43 | void pcs_bug_at(struct bug_point* bp) 44 | { 45 | if (!bp->active) { 46 | struct bugon_exception* x; 47 | bp->active = 1; 48 | for (x = bugon_except_list; x; x = x->next) { 49 | if (x->line == bp->line && !strcmp(x->file, bp->file)) { 50 | bp->active = -1; 51 | break; 52 | } 53 | } 54 | } 55 | ++bp->hits; 56 | if (bp->active < 0) { 57 | pcs_log(LOG_ERR, "IGNORED BUG at %s:%d/%s() [hit number %llu]", bp->file, bp->line, bp->func, bp->hits); 58 | return; 59 | } 60 | pcs_bug(bp->file, bp->line, bp->func); 61 | } 62 | 63 | /* Add bugon exception. Expected to be called at the application initialization stage only. 64 | * May return PCS_ERR_NOMEM on allocation failure. 65 | */ 66 | int pcs_bug_ignore(const char *file, int line) 67 | { 68 | struct bugon_exception* x = pcs_malloc(sizeof(*x)); 69 | if (!x) 70 | return PCS_ERR_NOMEM; 71 | if (!(x->file = pcs_strdup(file))) { 72 | pcs_free(x); 73 | return PCS_ERR_NOMEM; 74 | } 75 | x->line = line; 76 | x->next = bugon_except_list; 77 | bugon_except_list = x; 78 | return 0; 79 | } 80 | 81 | /* Accept file:line specification. May return PCS_ERR_NOMEM or PCS_ERR_INV_PARAMS errors. */ 82 | int pcs_bug_ignore_spec(const char *spec) 83 | { 84 | int res = 0; 85 | char *spec_copy, *sep, *eptr = 0; 86 | long line; 87 | if (!(spec_copy = pcs_strdup(spec))) { 88 | return PCS_ERR_NOMEM; 89 | } 90 | if (!(sep = strchr(spec_copy, ':'))) { 91 | res = PCS_ERR_INV_PARAMS; 92 | goto cleanup; 93 | } 94 | *sep++ = 0; 95 | line = strtol(sep, &eptr, 10); 96 | if (!*sep || *eptr || line <= 0) { 97 | res = PCS_ERR_INV_PARAMS; 98 | goto cleanup; 99 | } 100 | res = pcs_bug_ignore(spec_copy, line); 101 | cleanup: 102 | pcs_free(spec_copy); 103 | return res; 104 | } 105 | 106 | /* Read PSTORAGE_DBG_BUGON environment variable and set pcs_dbg_bugon_enable accordingly */ 107 | void pcs_dbg_bugon_init(void) 108 | { 109 | if (getenv("PSTORAGE_DBG_BUGON")) 110 | pcs_dbg_bugon_enable = 1; 111 | } 112 | -------------------------------------------------------------------------------- /libpcs_io/qselect.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "qselect.h" 6 | #include "pcs_types.h" 7 | 8 | static inline long median3(void* data, long l, long r, long c, int (*compar)(void*, long, long)) 9 | { 10 | if (compar(data, l, r) < 0) { 11 | if (compar(data, c, l) < 0) return l; 12 | if (compar(data, c, r) > 0) return r; 13 | return c; 14 | } else { 15 | if (compar(data, c, l) > 0) return l; 16 | if (compar(data, c, r) < 0) return r; 17 | return c; 18 | } 19 | } 20 | 21 | static long find_pivot(void* data, long l, long r, int (*compar)(void*, long, long)) 22 | { 23 | return median3(data, l, r, (l + r) / 2, compar); 24 | } 25 | 26 | #define LESS(a, b) compar(data, a, b) < 0 27 | #define SWAP(a, b) do { if (a != b) swap(data, a, b); } while(0) 28 | 29 | static long partition(void* data, long l, long r, int (*compar)(void*, long, long), void (*swap)(void*, long, long)) 30 | { 31 | if (r < l + 2) { 32 | if (r != l && LESS(r, l)) 33 | SWAP(r, l); 34 | return l; 35 | } 36 | long i, p = find_pivot(data, l, r, compar); 37 | SWAP(p, r); /* Move pivot to the [r] */ 38 | for (i = p = l; i < r; ++i) { 39 | if (LESS(i, r)) { /* [i] < pivot */ 40 | SWAP(i, p); 41 | ++p; 42 | } 43 | /* [l..p-1] < pivot, [p..i] >= pivot */ 44 | } 45 | SWAP(r, p); 46 | return p; 47 | } 48 | 49 | /* Rearrange elements of data array of size N so that all items with indexes < k are less than data[k] while 50 | * all items with indexes > k are greater or equal to data[k]. So this means that on output data[k] is also the 51 | * k-th element of the data array in the sorted order. The routine uses the caller-provided comparison and swap 52 | * functions. Both takes element index as the arguments. The comparison function must return an integer less than, 53 | * equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or 54 | * greater than the second. The callbacks may operate on the data array directly or deal with index so the 55 | * original data will be intact. 56 | */ 57 | void quick_partition( 58 | void* data, long N, long k, 59 | int (*compar)(void*, long, long), void (*swap)(void*, long, long) 60 | ) 61 | { 62 | long l = 0, r = N - 1; 63 | for (;;) { 64 | long p = partition(data, l, r, compar, swap); 65 | if (p == k) return; 66 | if (p < k) 67 | l = p + 1; 68 | else 69 | r = p - 1; 70 | } 71 | } 72 | 73 | static int compar_uint(void* data, long i, long j) 74 | { 75 | unsigned* arr = data; 76 | if (arr[i] < arr[j]) 77 | return -1; 78 | if (arr[i] > arr[j]) 79 | return 1; 80 | return 0; 81 | } 82 | 83 | static void swap_uint(void* data, long i, long j) 84 | { 85 | unsigned* arr = data; 86 | unsigned tmp = arr[i]; 87 | arr[i] = arr[j]; 88 | arr[j] = tmp; 89 | } 90 | 91 | /* Returns k-th element in sorted order from the given (unsorted array) */ 92 | unsigned select_uint(unsigned* arr, long N, long k) 93 | { 94 | quick_partition(arr, N, k, compar_uint, swap_uint); 95 | return arr[k]; 96 | } 97 | 98 | -------------------------------------------------------------------------------- /libpcs_io/adler32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include 6 | #include 7 | #include "adler32.h" 8 | 9 | #define BASE 65521L /* largest prime smaller than 65536 */ 10 | #define NMAX 5552 11 | /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ 12 | 13 | #define DO1(buf,i) {s1 += buf[i]; s2 += s1;} 14 | #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); 15 | #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); 16 | #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); 17 | #define DO16(buf) DO8(buf,0); DO8(buf,8); 18 | 19 | unsigned int zlib_adler32(unsigned int adler, const unsigned char *buf, unsigned int len) 20 | { 21 | unsigned long s1 = adler & 0xffff; 22 | unsigned long s2 = (adler >> 16) & 0xffff; 23 | int k; 24 | 25 | if (buf == NULL) 26 | return 1; 27 | 28 | while (len > 0) { 29 | k = len < NMAX ? len : NMAX; 30 | len -= k; 31 | while (k >= 16) { 32 | DO16(buf); 33 | buf += 16; 34 | k -= 16; 35 | } 36 | if (k != 0) do { 37 | s1 += *buf++; 38 | s2 += s1; 39 | } while (--k); 40 | s1 %= BASE; 41 | s2 %= BASE; 42 | } 43 | return (s2 << 16) | s1; 44 | } 45 | 46 | unsigned int fast_adler32(unsigned int initial, const unsigned char* buf, unsigned int size) 47 | { 48 | const uint32_t n = 1; 49 | size_t len = size; 50 | size_t s1 = initial & 0xffff; 51 | size_t s2 = initial >> 16; 52 | unsigned i; 53 | 54 | if (sizeof(void *) == 8) { 55 | while (((size_t)buf & 7) && len) 56 | { 57 | s1 += *(buf++); 58 | s2 += s1; 59 | len--; 60 | } 61 | 62 | while (len >= 23 * 8) { 63 | len -= 23 * 8; 64 | s2 += s1 * 23 * 8; 65 | 66 | uint64_t a1 = 0; 67 | uint64_t a2 = 0; 68 | uint64_t b1 = 0; 69 | uint64_t b2 = 0; 70 | 71 | for (i = 0; i < 23; ++i) { 72 | uint64_t v = *(uint64_t*)buf; 73 | a2 += a1; 74 | b2 += b1; 75 | a1 += v & 0x00FF00FF00FF00FF; 76 | b1 += (v >> 8) & 0x00FF00FF00FF00FF; 77 | buf += 8; 78 | } 79 | 80 | s1 += (((a1 + b1) * 0x1000100010001) >> 48); 81 | s2 += ((((a2 & 0xFFFF0000FFFF) + (b2 & 0xFFFF0000FFFF) + ((a2 >> 16) & 0xFFFF0000FFFF) + ((b2 >> 16) & 0xFFFF0000FFFF)) * 0x800000008) >> 32); 82 | if (*(char *)&n) /* little endian */ 83 | s2 += 2 * ((a1 * 0x4000300020001) >> 48) + ((b1 * 0x1000100010001) >> 48) + 2 * ((b1 * 0x3000200010000) >> 48); 84 | else /* big endian */ 85 | s2 += 2 * ((b1 * 0x1000200030004) >> 48) + ((a1 * 0x1000100010001) >> 48) + 2 * ((a1 * 0x0000100020003) >> 48); 86 | 87 | s1 %= BASE; 88 | s2 %= BASE; 89 | } 90 | } else { 91 | /* non-64bit system */ 92 | while (len >= NMAX) { 93 | len -= NMAX; 94 | 95 | for (i = 0; i < NMAX; ++i) { 96 | s1 += *(buf++); 97 | s2 += s1; 98 | } 99 | 100 | s1 %= BASE; 101 | s2 %= BASE; 102 | } 103 | } 104 | 105 | while (len) { 106 | s1 += *(buf++); 107 | s2 += s1; 108 | len--; 109 | } 110 | 111 | s1 %= BASE; 112 | s2 %= BASE; 113 | 114 | return (unsigned int)((s2 << 16) | s1); 115 | } 116 | -------------------------------------------------------------------------------- /libpcs_io/pcs_iocp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_iocp.h" 6 | #include "pcs_poll.h" 7 | #include "pcs_process.h" 8 | #include "pcs_winapi.h" 9 | #include "log.h" 10 | 11 | void pcs_iocp_attach(struct pcs_process *proc, HANDLE handle, void *key) 12 | { 13 | if (!CreateIoCompletionPort(handle, proc->iocp, (ULONG_PTR)key, 0)) { 14 | pcs_log_syserror(LOG_ERR, GetLastError(), "CreateIoCompletionPort failed"); 15 | BUG(); 16 | } 17 | } 18 | 19 | void pcs_iocp_cancel(HANDLE handle, struct pcs_iocp *iocp) 20 | { 21 | if (CancelIoExPtr) { 22 | if (!CancelIoExPtr(handle, &iocp->overlapped) && GetLastError() != ERROR_NOT_FOUND) 23 | pcs_log_syserror(LOG_WARN, GetLastError(), "CancelIoEx failed"); 24 | } else { 25 | if (!CancelIo(handle)) 26 | pcs_log_syserror(LOG_WARN, GetLastError(), "CancelIo failed"); 27 | } 28 | } 29 | 30 | void pcs_iocp_send(struct pcs_process *proc, struct pcs_iocp *iocp) 31 | { 32 | if (!PostQueuedCompletionStatus(proc->iocp, 0, 0, &iocp->overlapped)) { 33 | pcs_log_syserror(LOG_ERR, GetLastError(), "PostQueuedCompletionStatus failed"); 34 | BUG(); 35 | } 36 | } 37 | 38 | int pcs_iocp_result(struct pcs_iocp *iocp) 39 | { 40 | if (SUCCEEDED(iocp->overlapped.Internal)) 41 | return (int)iocp->overlapped.InternalHigh; 42 | 43 | return -(int)RtlNtStatusToDosErrorPtr((NTSTATUS)iocp->overlapped.Internal); 44 | } 45 | 46 | int pcs_poll_init(struct pcs_process *proc) 47 | { 48 | proc->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); 49 | if (!proc->iocp) { 50 | int err = GetLastError(); 51 | pcs_log_syserror(LOG_ERR, err, "CreateIoCompletionPort failed"); 52 | return -err; 53 | } 54 | return 0; 55 | } 56 | 57 | void pcs_poll_fini(struct pcs_process *proc) 58 | { 59 | if (proc->iocp) { 60 | CloseHandle(proc->iocp); 61 | proc->iocp = NULL; 62 | } 63 | } 64 | 65 | void pcs_poll_wait(struct pcs_evloop *evloop, int timeout) 66 | { 67 | HANDLE iocp = evloop->proc->iocp; 68 | OVERLAPPED_ENTRY *ev = evloop->events; 69 | 70 | BUG_ON(evloop->nr_events); 71 | 72 | if (GetQueuedCompletionStatusExPtr) { 73 | ULONG n; 74 | if (GetQueuedCompletionStatusExPtr(iocp, ev, PCS_MAX_EVENTS_NR, &n, timeout, FALSE)) { 75 | evloop->nr_events = n; 76 | return; 77 | } 78 | } else { 79 | ev->lpOverlapped = NULL; 80 | GetQueuedCompletionStatus(iocp, &ev->dwNumberOfBytesTransferred, &ev->lpCompletionKey, &ev->lpOverlapped, timeout); 81 | if (ev->lpOverlapped) { 82 | ev->Internal = ev->lpOverlapped->Internal; 83 | evloop->nr_events = 1; 84 | return; 85 | } 86 | } 87 | 88 | int err = GetLastError(); 89 | if (err != WAIT_TIMEOUT) 90 | pcs_log_syserror(LOG_ERR, err, "GetQueuedCompletionStatus failed"); 91 | } 92 | 93 | void pcs_poll_process_events(struct pcs_evloop *evloop) 94 | { 95 | int i; 96 | for (i = 0; i < evloop->nr_events; i++) { 97 | struct pcs_iocp *iocp = container_of(evloop->events[i].lpOverlapped, struct pcs_iocp, overlapped); 98 | iocp->done(iocp); 99 | } 100 | 101 | evloop->nr_events = 0; 102 | } 103 | -------------------------------------------------------------------------------- /libpcs_io/pcs_config_file.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_CONFIG_FILE_H 6 | #define _PCS_CONFIG_FILE_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | struct pcs_config { 15 | struct rb_root root; 16 | }; 17 | 18 | struct pcs_config_node { 19 | struct rb_node rb; 20 | char *key; 21 | char *value; 22 | char *value_expanded; 23 | }; 24 | 25 | /* Create empty configuration */ 26 | PCS_API struct pcs_config* pcs_create_config(void); 27 | 28 | /* Parse configuration file */ 29 | PCS_API int pcs_parse_config_file(struct pcs_config* cfg, FILE *f); 30 | 31 | /* Parse single line */ 32 | PCS_API int pcs_parse_config_line(struct pcs_config* cfg, char* str); 33 | 34 | /* Release config and all associated resources */ 35 | PCS_API void pcs_free_config(struct pcs_config *cfg); 36 | 37 | /* Create config structure and read configuration from the file 38 | * Returns config context on success or NULL on failure. 39 | */ 40 | PCS_API struct pcs_config* pcs_read_config(const char *fname); 41 | 42 | /* Read a config from a string. Returns config context on success, or NULL on failure. 43 | Note: this function modifies @str (but restores it before returning). */ 44 | PCS_API struct pcs_config* pcs_read_config_mem(char *str); 45 | 46 | /* Writes existing config structure into file. 47 | * Does not guarantee atomicity! 48 | * Either writes the whole config or nothing. 49 | */ 50 | PCS_API int pcs_write_config(struct pcs_config *cfg, const char *fname); 51 | 52 | /* Writes a config to a string. The caller is responsible to free the result string. */ 53 | PCS_API char* pcs_write_config_mem(struct pcs_config *cfg); 54 | 55 | /* Various getters. */ 56 | PCS_API int pcs_config_getstr(struct pcs_config *cfg, const char *key, const char **val); 57 | PCS_API int pcs_config_getstr_expand(struct pcs_config *cfg, const char *key, const char **val); 58 | 59 | PCS_API int pcs_config_getint(struct pcs_config *cfg, const char *key, void *buf, size_t size); 60 | PCS_API int pcs_config_getuint(struct pcs_config *cfg, const char *key, void *buf, size_t size); 61 | 62 | PCS_API int pcs_config_getnetaddr(struct pcs_config *cfg, const char *key, const char *def_port, PCS_NET_ADDR_T *val); 63 | 64 | PCS_API int pcs_config_get_view(struct pcs_config *cfg, 65 | void (*cb)(void *userp, const char *key, const char *val), 66 | void *userp); 67 | 68 | PCS_API void pcs_config_setstr(struct pcs_config *cfg, const char *key, const char *val); 69 | PCS_API void pcs_config_setint(struct pcs_config *cfg, const char *key, u64 val); 70 | PCS_API void pcs_config_setint_hex(struct pcs_config *cfg, const char *key, u64 val); 71 | PCS_API void pcs_config_setnetaddr(struct pcs_config *cfg, const char *key, const PCS_NET_ADDR_T *val); 72 | 73 | PCS_API int pcs_config_unset(struct pcs_config *cfg, const char *key); 74 | 75 | /* Helper routines for string to integer conversion */ 76 | int pcs_str_to_int (const char *str, void *buf, size_t size); 77 | int pcs_str_to_uint(const char *str, void *buf, size_t size); 78 | 79 | 80 | #endif /* _PCS_CONFIG_FILE_H */ 81 | -------------------------------------------------------------------------------- /libpcs_io/pcs_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCSIO_CONFIG_H__ 6 | #define __PCSIO_CONFIG_H__ 7 | 8 | #include "pcs_types.h" 9 | #include 10 | 11 | #ifndef __WINDOWS__ 12 | #include 13 | #else 14 | #include 15 | #endif 16 | 17 | /* --------------------------------------------------------------------------------------- */ 18 | 19 | #ifdef __LINUX__ 20 | 21 | #include 22 | #include 23 | 24 | #define HAVE_FORK 1 25 | #define HAVE_POSIX_TIMER 1 26 | #define HAVE_OOM_ADJUST 1 27 | #define HAVE_PTHREAD_TIMEDJOIN 1 28 | #ifndef __aarch64__ 29 | #define HAVE_TLS_STATIC 1 30 | #endif 31 | 32 | /* helpful link to find kernel/glibc version: 33 | * http://distrowatch.com/table.php?distribution=redhat */ 34 | 35 | #if __GLIBC_PREREQ(2, 4) 36 | #define HAVE_FSTATAT 1 37 | #define HAVE_TCP_INFO 1 38 | #endif 39 | 40 | /* RHEL5 doesn't have eventfd in glibc */ 41 | #if __GLIBC_PREREQ(2, 9) 42 | #define HAVE_EVENTFD 1 43 | #define HAVE_PIPE2 1 44 | #define HAVE_AIO 1 /* we need eventfd to use AIO */ 45 | #endif 46 | 47 | #if __GLIBC_PREREQ(2, 10) 48 | #define HAVE_ACCEPT4 1 49 | #endif 50 | 51 | #ifndef O_CLOEXEC 52 | /* It is safe to specify O_CLOEXEC always, older kernels ignore this flag */ 53 | #define O_CLOEXEC 02000000 54 | #endif 55 | 56 | #endif /* __LINUX__ */ 57 | 58 | /* --------------------------------------------------------------------------------------- */ 59 | 60 | #ifdef __MAC__ 61 | #define HAVE_FORK 1 62 | #define HAVE_TLS_STATIC 1 63 | #endif 64 | 65 | /* --------------------------------------------------------------------------------------- */ 66 | 67 | #ifdef __SUN__ 68 | #define HAVE_FORK 1 69 | /* Do not define HAVE_TLS_STATIC - otherwise gcc will cache TLS pointer. 70 | * This results in incorrect behaviour when coroutine is moved to another thread. */ 71 | #endif 72 | 73 | /* --------------------------------------------------------------------------------------- */ 74 | 75 | #ifdef __GNUC__ 76 | #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) 77 | #else 78 | #define GCC_VERSION 0 79 | #endif 80 | 81 | #ifndef __GLIBC__ 82 | #define __GLIBC_PREREQ(mj,mi) 0 83 | #endif 84 | 85 | #ifdef __clang__ 86 | #if __clang_major__ >= 4 87 | #if __has_feature(address_sanitizer) 88 | #define PCS_ADDR_SANIT 89 | #endif 90 | #if __has_feature(thread_sanitizer) 91 | #define PCS_THREAD_SANIT 92 | #define __no_sanitize_thread __attribute__((no_sanitize_thread)) 93 | #endif 94 | #endif 95 | #elif GCC_VERSION >= 70100 96 | #if defined(__SANITIZE_ADDRESS__) 97 | #define PCS_ADDR_SANIT 98 | #endif 99 | #if defined(__SANITIZE_THREAD__) 100 | #define PCS_THREAD_SANIT 101 | #define __no_sanitize_thread __attribute__((no_sanitize_thread, noipa)) 102 | #endif 103 | #endif 104 | #ifdef PCS_THREAD_SANIT 105 | void __tsan_acquire(void *); 106 | void __tsan_release(void *); 107 | #else 108 | #define __no_sanitize_thread 109 | #define __tsan_acquire(addr) 110 | #define __tsan_release(addr) 111 | #endif 112 | 113 | #endif /* __PCSIO_CONFIG_H__ */ 114 | -------------------------------------------------------------------------------- /libpcs_io/pcs_cpuid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_cpuid.h" 6 | #include "pcs_config.h" 7 | #include "bug.h" 8 | 9 | /* not sure about exact gcc version, so put 4.4 from RHEL6 */ 10 | #if defined(__x86_64__) && (defined(__clang__) || GCC_VERSION >= 40400) 11 | 12 | static inline void pcs_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) 13 | { 14 | asm volatile("cpuid" 15 | : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) 16 | : "0" (*eax), "2" (*ecx)); 17 | } 18 | 19 | #elif defined(_WIN64) 20 | 21 | #include 22 | 23 | static inline void pcs_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) 24 | { 25 | int cpuid[4] = {0, 0, 0, 0}; 26 | __cpuid(cpuid, *eax); 27 | 28 | (*eax) = cpuid[0]; 29 | (*ebx) = cpuid[1]; 30 | (*ecx) = cpuid[2]; 31 | (*edx) = cpuid[3]; 32 | } 33 | 34 | #else 35 | 36 | static inline void pcs_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) 37 | { 38 | (*eax) = 0; 39 | (*ebx) = 0; 40 | (*ecx) = 0; 41 | (*edx) = 0; 42 | } 43 | 44 | #endif 45 | 46 | static int pcs_cpuid_max(void) 47 | { 48 | unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; 49 | 50 | pcs_cpuid(&eax, &ebx, &ecx, &edx); 51 | return eax; 52 | } 53 | 54 | int pcs_is_crc32_sse_supported(void) 55 | { 56 | if (pcs_cpuid_max() < 1) 57 | return 0; 58 | 59 | unsigned int eax = 1, ebx, ecx = 0, edx; 60 | 61 | pcs_cpuid(&eax, &ebx, &ecx, &edx); 62 | return !!(ecx & (1 << 20)); /* SSE4.2 support */ 63 | } 64 | 65 | int pcs_is_avx2_supported(void) 66 | { 67 | if (pcs_cpuid_max() < 7) 68 | return 0; 69 | 70 | unsigned int eax = 7, ebx = 0, ecx = 0, edx; 71 | 72 | pcs_cpuid(&eax, &ebx, &ecx, &edx); 73 | return !!(ebx & (1 << 5)); /* AVX2 support */ 74 | } 75 | 76 | int pcs_is_aesni_supported(void) 77 | { 78 | if (pcs_cpuid_max() < 1) 79 | return 0; 80 | 81 | unsigned int eax = 1, ebx, ecx = 0, edx; 82 | 83 | pcs_cpuid(&eax, &ebx, &ecx, &edx); 84 | return !!(ecx & (1 << 25)); /* AES-NI support */ 85 | } 86 | 87 | 88 | #if defined(__aarch64__) || (__ARM_ARCH_7A__) || (__ARM_ARCH_8A__) 89 | #include 90 | #include 91 | #endif 92 | 93 | int pcs_cpu_is_neon_supported() 94 | { 95 | #ifdef __aarch64__ 96 | unsigned int val = getauxval(AT_HWCAP); 97 | return val & HWCAP_ASIMD; 98 | #elif defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) 99 | unsigned int val = getauxval(AT_HWCAP); 100 | return val & HWCAP_NEON; 101 | #else 102 | BUG(); 103 | #endif 104 | } 105 | 106 | int pcs_cpu_is_arm_crc_supported() 107 | { 108 | #ifdef __aarch64__ 109 | unsigned int val = getauxval(AT_HWCAP); 110 | return val & HWCAP_CRC32; 111 | #elif defined(__ARM_ARCH_8A__) 112 | unsigned int val = getauxval(AT_HWCAP2); 113 | return val & HWCAP2_CRC32; 114 | #else 115 | BUG(); 116 | #endif 117 | } 118 | 119 | 120 | int pcs_cpu_is_arm_pmull_supported() 121 | { 122 | #ifdef __aarch64__ 123 | unsigned int val = getauxval(AT_HWCAP); 124 | return val & HWCAP_PMULL; 125 | #elif defined(__ARM_ARCH_8A__) 126 | unsigned int val = getauxval(AT_HWCAP2); 127 | return val & HWCAP2_PMULL; 128 | #else 129 | BUG(); 130 | #endif 131 | } 132 | -------------------------------------------------------------------------------- /libpcs_io/pcs_aio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_AIO_H_ 6 | #define _PCS_AIO_H_ 1 7 | 8 | #include "pcs_config.h" 9 | #include "pcs_process.h" 10 | #include "pcs_thread.h" 11 | #include "pcs_error.h" 12 | 13 | #include 14 | 15 | #ifdef HAVE_AIO 16 | #define _LINUX_MOUNT_H /* workaround incompatiblity with */ 17 | #include 18 | #endif 19 | 20 | #define PCS_AIO_MAXREQS 128 21 | 22 | struct pcs_aioreq 23 | { 24 | struct cd_list list; 25 | #ifdef HAVE_AIO 26 | struct iocb iocb; 27 | #else 28 | int fd; 29 | void *buf; 30 | #endif 31 | u64 pos; 32 | size_t count; 33 | int error; 34 | PCS_NODE_ID_T client; 35 | u32 iocontext; 36 | int flags; 37 | void *priv; 38 | void (*complete)(struct pcs_aioreq *); 39 | }; 40 | 41 | #define PCS_AIO_F_WRITE 1 42 | #define PCS_AIO_F_PAD 2 43 | 44 | struct pcs_aio_worker 45 | { 46 | struct cd_list queue; 47 | short idx; 48 | unsigned short shutdown; 49 | int in_waiting; 50 | pcs_thread_t thr; 51 | pthread_mutex_t lock; 52 | pthread_cond_t wake; 53 | 54 | int served; 55 | }; 56 | 57 | struct pcs_aio 58 | { 59 | struct cd_list queue; 60 | int queued; 61 | 62 | int acquired_reqs; 63 | int shutdown; 64 | int max_threads; 65 | int threads; 66 | #ifdef HAVE_AIO 67 | struct pcs_event_ioconn *ioconn; 68 | aio_context_t ctx; 69 | #else 70 | struct pcs_job job; 71 | #endif 72 | 73 | pthread_mutex_t error_lock; 74 | struct cd_list error_queue; 75 | int error_count; 76 | 77 | int pending; 78 | 79 | u32 salt; 80 | 81 | struct pcs_aio_worker workers[0]; 82 | }; 83 | 84 | static inline void pcs_aio_pread(struct pcs_aioreq *req, int fd, void *buf, 85 | size_t count, long long offset) 86 | { 87 | req->pos = offset; 88 | req->count = count; 89 | req->flags = 0; 90 | #ifdef HAVE_AIO 91 | memset(&req->iocb, 0, sizeof(req->iocb)); 92 | req->iocb.aio_lio_opcode = IOCB_CMD_PREAD; 93 | req->iocb.aio_fildes = fd; 94 | req->iocb.aio_buf = (u64)buf; 95 | req->iocb.aio_nbytes = count; 96 | req->iocb.aio_offset = offset; 97 | #else 98 | req->fd = fd; 99 | req->buf = buf; 100 | #endif 101 | } 102 | 103 | static inline void pcs_aio_pwrite(struct pcs_aioreq *req, int fd, void *buf, 104 | size_t count, long long offset) 105 | { 106 | req->pos = offset; 107 | req->count = count; 108 | req->flags = PCS_AIO_F_WRITE; 109 | #ifdef HAVE_AIO 110 | memset(&req->iocb, 0, sizeof(req->iocb)); 111 | req->iocb.aio_lio_opcode = IOCB_CMD_PWRITE; 112 | req->iocb.aio_fildes = fd; 113 | req->iocb.aio_buf = (u64)buf; 114 | req->iocb.aio_nbytes = count; 115 | req->iocb.aio_offset = offset; 116 | #else 117 | req->fd = fd; 118 | req->buf = buf; 119 | #endif 120 | } 121 | 122 | void pcs_aioreq_init(struct pcs_aioreq *); 123 | void pcs_aioreq_free(struct pcs_aio *, struct pcs_aioreq *); 124 | void pcs_aioreq_submit(struct pcs_aio *, struct pcs_aioreq *); 125 | 126 | struct pcs_aio * pcs_aio_init(struct pcs_process *, int threads); 127 | int pcs_aio_start(struct pcs_process * proc, struct pcs_aio * aio); 128 | int pcs_aio_deinit(struct pcs_aio * aio); 129 | int pcs_aio_set_threads(struct pcs_aio * aio, int threads); 130 | 131 | #endif /* _PCS_AIO_H_ */ 132 | -------------------------------------------------------------------------------- /libpcs_io/pcs_rdma_prot.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_RDMA_PROT_H_ 6 | #define _PCS_RDMA_PROT_H_ 1 7 | 8 | /* PCS RDMA network protocol v1 */ 9 | 10 | #define RIO_MAGIC 0x5078614d 11 | #define RIO_VERSION 1 12 | 13 | #define RIO_MSG_SIZE (2*4096) /* max size of any wire message */ 14 | 15 | #define RIO_QUEUE_DEPTH 8 /* TODO: make it tunable */ 16 | #define RIO_MAX_QUEUE_DEPTH 128 /* for conn_param sanity checks */ 17 | 18 | /* negotiated by rdma_connect/rdma_accept */ 19 | struct pcs_rdmaio_conn_req { 20 | u32 magic; /* RIO_MAGIC */ 21 | u32 version; /* RIO_VERSION */ 22 | u32 queue_depth; /* RIO_QUEUE_DEPTH */ 23 | u32 msg_size; /* RIO_MSG_SIZE */ 24 | } __attribute__((aligned(8))); 25 | 26 | /* negotiated by rdma_connect/rdma_accept */ 27 | struct pcs_rdmaio_rej { 28 | struct pcs_rdmaio_conn_req cr; 29 | u32 error; /* errno */ 30 | } __attribute__((aligned(8))); 31 | 32 | /* "type" field of pcs_rdmaio_msg */ 33 | enum { 34 | RIO_MSG_IMMEDIATE = 0, 35 | RIO_MSG_NOOP, 36 | RIO_MSG_RDMA_READ_REQ, 37 | RIO_MSG_RDMA_READ_ACK, 38 | RIO_MAX_TYPE_VALUE 39 | }; 40 | 41 | /* sent/recieved by rdma_post_send/rdma_post_recv */ 42 | struct pcs_rdmaio_hdr { 43 | u32 magic; /* RIO_MAGIC */ 44 | u16 version; /* RIO_VERSION */ 45 | u16 type; /* RIO_MSG_IMMEDIATE/... */ 46 | u32 size; /* total size of wire message */ 47 | u32 credits; /* # credits to return */ 48 | } __attribute__((aligned(8))); 49 | 50 | struct pcs_remote_buf { 51 | u64 xid; 52 | u64 rbuf; 53 | u32 rkey; 54 | u32 rlen; 55 | } __attribute__((aligned(8))); 56 | 57 | struct pcs_rdma_ack { 58 | u64 xid; 59 | u32 status; 60 | } __attribute__((aligned(8))); 61 | 62 | static inline int rio_parse_hdr(char *buf, char **payload, int *payload_size, int *credits, struct pcs_remote_buf **rb, struct pcs_rdma_ack **rack) 63 | { 64 | struct pcs_rdmaio_hdr *hdr = (struct pcs_rdmaio_hdr *)buf; 65 | 66 | if (hdr->magic != RIO_MAGIC) { 67 | pcs_log(LOG_ERR, "wrong rio msg magic: 0x%x", hdr->magic); 68 | return -1; 69 | } 70 | 71 | if (hdr->version != RIO_VERSION) { 72 | pcs_log(LOG_ERR, "wrong rio msg version: 0x%x", hdr->version); 73 | return -1; 74 | } 75 | 76 | if (hdr->type >= RIO_MAX_TYPE_VALUE) { 77 | pcs_log(LOG_ERR, "wrong rio msg type: 0x%x", hdr->type); 78 | return -1; 79 | } 80 | 81 | if (hdr->size > RIO_MSG_SIZE) { 82 | pcs_log(LOG_ERR, "wrong rio msg size: 0x%x", hdr->size); 83 | return -1; 84 | } 85 | 86 | if (hdr->credits > RIO_QUEUE_DEPTH) { 87 | pcs_log(LOG_ERR, "wrong rio msg credits: 0x%x", hdr->credits); 88 | return -1; 89 | } 90 | 91 | if (hdr->type == RIO_MSG_RDMA_READ_REQ && 92 | hdr->size - sizeof(*hdr) < sizeof(struct pcs_remote_buf)) { 93 | pcs_log(LOG_ERR, "short rdma read req: 0x%x", hdr->size); 94 | return -1; 95 | } 96 | 97 | if (hdr->type == RIO_MSG_RDMA_READ_ACK && 98 | hdr->size != sizeof(*hdr) + sizeof(struct pcs_rdma_ack)) { 99 | pcs_log(LOG_ERR, "wrong size rdma read ack: 0x%x", hdr->size); 100 | return -1; 101 | } 102 | 103 | *payload = buf + sizeof(*hdr); 104 | *payload_size = hdr->size - sizeof(*hdr); 105 | *credits = hdr->credits; 106 | 107 | if (hdr->type == RIO_MSG_RDMA_READ_REQ) { 108 | *rb = (struct pcs_remote_buf *)*payload; 109 | *payload += sizeof(struct pcs_remote_buf); 110 | *payload_size -= sizeof(struct pcs_remote_buf); 111 | } else if (hdr->type == RIO_MSG_RDMA_READ_ACK) { 112 | *rack = (struct pcs_rdma_ack *)*payload; 113 | *payload += sizeof(struct pcs_rdma_ack); 114 | *payload_size -= sizeof(struct pcs_rdma_ack); 115 | } 116 | 117 | return hdr->type; 118 | } 119 | 120 | #endif /* _PCS_RDMA_PROT_H_ */ 121 | -------------------------------------------------------------------------------- /libpcs_io/pcs_net_addr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_NET_ADDR_H__ 6 | #define __PCS_NET_ADDR_H__ 7 | 8 | #include "pcs_types.h" 9 | 10 | struct sockaddr; 11 | struct addrinfo; 12 | 13 | enum 14 | { 15 | PCS_ADDRTYPE_NONE = 0, 16 | PCS_ADDRTYPE_IP = 1, 17 | PCS_ADDRTYPE_IP6 = 2, 18 | PCS_ADDRTYPE_UNIX = 3, 19 | PCS_ADDRTYPE_RDMA = 4, 20 | PCS_ADDRTYPE_NETLINK = 5, 21 | }; 22 | 23 | /* alignment makes it usable in binary protocols */ 24 | typedef struct __pre_aligned(8) _PCS_NET_ADDR_T { 25 | u32 type; 26 | union { 27 | struct { 28 | u32 port; /* network byteorder */ 29 | u8 address[16]; 30 | }; 31 | /* type == PCS_ADDRTYPE_NETLINK */ 32 | struct { 33 | u32 pid; 34 | u32 groups; 35 | }; 36 | }; 37 | } PCS_NET_ADDR_T __aligned(8); 38 | 39 | PCS_API int pcs_getaddrinfo(const char* node, const char* service, const struct addrinfo* hints, struct addrinfo ** new_ai); 40 | PCS_API int pcs_co_getaddrinfo(const char* node, const char* service, const struct addrinfo* hints, struct addrinfo ** new_ai); 41 | PCS_API void pcs_freeaddrinfo(struct addrinfo* ai); 42 | 43 | /** 44 | Parse a string in one of the following formats: 45 | - ipv4addr:port 46 | - ipv6addr:port 47 | - hostname:port 48 | 49 | If @str is hostname:port, this call resolves the DNS name 'hostname' into an address. 50 | 51 | \param @str a string with a host name 52 | \param @addr an address that @str resolves to (a randomly picked one if @str is a DNS name that resolves to multiple addresses) 53 | \returns the same errors that getaddrinfo() does 54 | */ 55 | PCS_API int pcs_parse_netaddr(const char *str, PCS_NET_ADDR_T *addr); 56 | 57 | /* Similar to pcs_parse_netaddr(), but use @def_port as a port number if @str does not specify one. */ 58 | PCS_API int pcs_parse_netaddr_port(const char *str, const char *def_port, PCS_NET_ADDR_T *addr); 59 | /* Similar to pcs_parse_netaddr_port(), but returns all addresses that @str resolves to. */ 60 | PCS_API int pcs_parse_netaddr_port_multi(const char *str, const char *def_port, int * nr_addrs, PCS_NET_ADDR_T ** addrs); 61 | 62 | /* A version of pcs_parse_netaddr() that can be used from a coroutine. */ 63 | PCS_API int pcs_co_parse_netaddr(const char *str, PCS_NET_ADDR_T *addr); 64 | /* A version of pcs_parse_netaddr_port() that can be used from a coroutine. */ 65 | PCS_API int pcs_co_parse_netaddr_port(const char *str, const char *def_port, PCS_NET_ADDR_T *addr); 66 | /* A version of pcs_parse_netaddr_port_multi() that can be used from a coroutine. */ 67 | PCS_API int pcs_co_parse_netaddr_port_multi(const char *str, const char *def_port, int * nr_addrs, PCS_NET_ADDR_T ** addrs); 68 | 69 | /* get human-readable string for error returned by pcs_parse_netaddr */ 70 | PCS_API const char *pcs_parse_netaddr_err(int err_code); 71 | 72 | /* Like gethostname(), but guarantees to null-terminate @buf. */ 73 | PCS_API int pcs_gethostname(char *buf, int size); 74 | 75 | /* allocate sockaddr from PCS_NET_ADDR_T */ 76 | PCS_API int pcs_netaddr2sockaddr(PCS_NET_ADDR_T const* addr, struct sockaddr **_sa, int *salen); 77 | PCS_API int pcs_netaddr2afamily(PCS_NET_ADDR_T const* addr); 78 | PCS_API int pcs_sockaddr2netaddr(PCS_NET_ADDR_T *addr, struct sockaddr *sa); 79 | PCS_API int pcs_format_netaddr(char * str, int len, PCS_NET_ADDR_T const* addr); 80 | PCS_API int pcs_format_netaddr_port(char * str, int len, unsigned *port, PCS_NET_ADDR_T const* addr); 81 | PCS_API int pcs_netaddr_cmp(PCS_NET_ADDR_T const* addr1, PCS_NET_ADDR_T const* addr2); 82 | PCS_API int pcs_netaddr_cmp_ignore_port(PCS_NET_ADDR_T const* addr1, PCS_NET_ADDR_T const* addr2); 83 | PCS_API int pcs_netaddr2hostname(PCS_NET_ADDR_T *addr, char *buf, int size); 84 | PCS_API int pcs_is_zero_netaddr(PCS_NET_ADDR_T *addr); 85 | #endif /* __PCS_NET_ADDR_H__ */ 86 | -------------------------------------------------------------------------------- /libpcs_io/pcs_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_TYPES_H__ 6 | #define __PCS_TYPES_H__ 7 | 8 | #include 9 | 10 | /* ----- platform macros ----- */ 11 | 12 | #ifdef __linux__ 13 | #define __LINUX__ 14 | #endif 15 | 16 | #ifdef __APPLE__ 17 | #define __MAC__ 18 | #endif 19 | 20 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) 21 | #define __WINDOWS__ 22 | #endif 23 | 24 | #ifdef __sparc__ 25 | #define __SPARC__ 26 | #endif 27 | 28 | #ifdef __sun 29 | #define __SUN__ 30 | #endif 31 | 32 | /* ----- platform independant data types ----- */ 33 | 34 | typedef uint64_t u64; 35 | typedef uint32_t u32; 36 | typedef uint16_t u16; 37 | typedef uint8_t u8; 38 | 39 | typedef int64_t s64; 40 | typedef int32_t s32; 41 | typedef int16_t s16; 42 | typedef int8_t s8; 43 | 44 | #if defined(_WIN64) 45 | typedef unsigned __int64 ULONG_PTR; 46 | #else 47 | typedef unsigned long ULONG_PTR; 48 | #endif 49 | 50 | #define llu long long unsigned int 51 | 52 | /* ----- helpers ----- */ 53 | /* NOTE: use '__restrict' instead of C99 'restrict' as it is supported by all of: gcc, clang, msvc */ 54 | #if defined(__GNUC__) || defined(__clang__) 55 | 56 | #define __printf(x,y) __attribute__((format(printf, x, y), nonnull(x))) 57 | #define __noreturn __attribute__((noreturn)) 58 | #define __noinline __attribute__((noinline)) 59 | #define __maybe_unused __attribute__((unused)) 60 | 61 | #if !defined(__MINGW32__) && !defined(__MINGW64__) 62 | #define __forceinline inline __attribute__((always_inline)) 63 | #endif 64 | 65 | #define __must_check __attribute__((warn_unused_result)) 66 | #define likely(x) __builtin_expect(!!(x), 1) 67 | #define unlikely(x) __builtin_expect(!!(x), 0) 68 | 69 | #define container_of(ptr, type, member) ({ \ 70 | const typeof( ((type *)0)->member ) *__ptr = (ptr); \ 71 | (type *)( (char *)__ptr - offsetof(type,member) );}) 72 | 73 | #elif defined(_MSC_VER) 74 | 75 | #define __printf(x,y) 76 | #define __noreturn __declspec(noreturn) 77 | #define __noinline __declspec(noinline) 78 | #define __forceinline __forceinline 79 | #define __must_check _Check_return_ 80 | #define likely(x) (x) 81 | #define unlikely(x) (x) 82 | /* MSVC doesn't compile "static inline", but is ok with "static __inline"... */ 83 | #define inline __inline 84 | #define __thread __declspec(thread) 85 | #define __maybe_unused 86 | 87 | #define container_of(ptr, type, member) ((type*)((char*)(ptr) - offsetof(type, member))) 88 | 89 | #endif 90 | 91 | #ifdef __WINDOWS__ 92 | 93 | /* XXX: probably not the best place for this */ 94 | typedef unsigned long sigset_t; 95 | typedef void * timer_t; 96 | 97 | #ifndef WIN32_LEAN_AND_MEAN 98 | #define WIN32_LEAN_AND_MEAN 99 | #endif 100 | #ifndef NOMINMAX 101 | #define NOMINMAX 102 | #endif 103 | #include 104 | 105 | typedef HANDLE pcs_fd_t; 106 | #define PCS_INVALID_FD INVALID_HANDLE_VALUE 107 | 108 | #ifdef PCS_API_DLLEXPORT 109 | #define PCS_API __declspec(dllexport) 110 | #elif defined(PCS_API_DLLIMPORT) 111 | #define PCS_API __declspec(dllimport) 112 | #else 113 | #define PCS_API 114 | #endif 115 | 116 | #else 117 | typedef int pcs_fd_t; 118 | #define PCS_INVALID_FD (-1) 119 | 120 | #ifdef PCS_API_DLLEXPORT 121 | #define PCS_API __attribute__((visibility("default"))) 122 | #else 123 | #define PCS_API 124 | #endif 125 | 126 | #endif 127 | 128 | #include "pcs_align.h" 129 | 130 | typedef struct __pre_aligned(8) _PCS_NODE_ID_T { 131 | u64 val; 132 | } PCS_NODE_ID_T __aligned(8); 133 | 134 | typedef union { 135 | struct { 136 | u32 major; 137 | u32 minor; 138 | }; 139 | u64 full; 140 | } PCS_FAST_PATH_VERSION_T; 141 | 142 | #endif /* __PCS_TYPES_H__ */ 143 | -------------------------------------------------------------------------------- /libpcs_io/pcs_compat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_compat.h" 6 | #include "pcs_config.h" 7 | #include "pcs_malloc.h" 8 | #include "pcs_types.h" 9 | #include "bug.h" 10 | 11 | #ifndef __WINDOWS__ 12 | #include 13 | #include 14 | #include 15 | #ifdef __MAC__ 16 | #include 17 | #endif 18 | #else 19 | #include /* _get_osfhandle */ 20 | #include 21 | #endif 22 | 23 | #ifdef __WINDOWS__ 24 | int fsync(int fd) 25 | { 26 | HANDLE h = (HANDLE)_get_osfhandle(fd); 27 | if (h == INVALID_HANDLE_VALUE) 28 | return -1; 29 | 30 | if (!FlushFileBuffers(h)) 31 | return -1; 32 | 33 | return 0; 34 | } 35 | 36 | int ftruncate(int fd, u64 len) 37 | { 38 | int err = _chsize_s(fd, len); 39 | if (err) { 40 | errno = err; 41 | return -1; 42 | } 43 | return 0; 44 | } 45 | 46 | char * strndup(const char *str, size_t n) 47 | { 48 | size_t len; 49 | char *copy; 50 | 51 | len = strnlen(str, n); 52 | if ((copy = malloc(len + 1)) == NULL) 53 | return (NULL); 54 | memcpy(copy, str, len); 55 | copy[len] = '\0'; 56 | return (copy); 57 | } 58 | 59 | WCHAR * pcs_utf8_to_utf16(const char * str, int len) 60 | { 61 | int w_len = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0); 62 | if (w_len == 0) 63 | return NULL; 64 | 65 | WCHAR * w_str = pcs_xmalloc(sizeof(WCHAR) * (w_len + 1)); 66 | int res = MultiByteToWideChar(CP_UTF8, 0, str, len, w_str, w_len); 67 | BUG_ON(res != w_len); 68 | w_str[w_len] = L'\0'; 69 | return w_str; 70 | } 71 | 72 | char * pcs_utf16_to_utf8(const WCHAR * wstr, int wlen) 73 | { 74 | int len = WideCharToMultiByte(CP_UTF8, 0, wstr, wlen, NULL, 0, NULL, NULL); 75 | if (len == 0) 76 | return NULL; 77 | 78 | char * str = pcs_xmalloc(len + 1); 79 | int res = WideCharToMultiByte(CP_UTF8, 0, wstr, wlen, str, len, NULL, NULL); 80 | BUG_ON(res != len); 81 | str[len] = '\0'; 82 | return str; 83 | } 84 | 85 | #if (_MSC_VER < 1900) 86 | int pcs_vsnprintf(char *str, size_t size, const char *format, va_list ap) 87 | { 88 | int count = -1; 89 | 90 | if (size != 0) { 91 | count = _vsnprintf(str, size, format, ap); 92 | str[size - 1] = 0; 93 | } 94 | if (count == -1) { 95 | count = _vscprintf(format, ap); 96 | } 97 | 98 | return count; 99 | } 100 | 101 | int pcs_snprintf(char *str, size_t size, const char *format, ...) 102 | { 103 | int count; 104 | va_list ap; 105 | 106 | va_start(ap, format); 107 | count = pcs_vsnprintf(str, size, format, ap); 108 | va_end(ap); 109 | 110 | return count; 111 | } 112 | #endif /* (_MSC_VER < 1900) */ 113 | #endif /* __WINDOWS__ */ 114 | 115 | unsigned int pcs_nr_processors(void) 116 | { 117 | #ifdef __WINDOWS__ 118 | SYSTEM_INFO info; 119 | GetSystemInfo(&info); 120 | return info.dwNumberOfProcessors; 121 | #else 122 | int nr = sysconf(_SC_NPROCESSORS_ONLN); 123 | return (nr <= 0) ? 1 : nr; 124 | #endif 125 | } 126 | 127 | unsigned int pcs_sys_page_size(void) 128 | { 129 | #ifdef __WINDOWS__ 130 | SYSTEM_INFO info; 131 | GetSystemInfo(&info); 132 | return info.dwPageSize; 133 | #else 134 | int nr = sysconf(_SC_PAGESIZE); 135 | return (nr <= 0) ? 4096 : nr; 136 | #endif 137 | } 138 | 139 | u64 pcs_phys_memory_size(void) 140 | { 141 | #if defined(__WINDOWS__) 142 | MEMORYSTATUS mem; 143 | GlobalMemoryStatus(&mem); 144 | return mem.dwTotalPhys; 145 | #elif defined(__MAC__) 146 | int mib[2] = { CTL_HW, HW_MEMSIZE }; 147 | u64 mem_size = 0; 148 | size_t len = sizeof(mem_size); 149 | return sysctl(mib, 2, &mem_size, &len, 0, 0) < 0 ? 0 : mem_size; 150 | #else 151 | long phys_pages = sysconf(_SC_PHYS_PAGES); 152 | long pg_size = sysconf(_SC_PAGESIZE); 153 | if (phys_pages < 0 || pg_size < 0) 154 | return 0; 155 | return (u64)phys_pages * pg_size; 156 | #endif 157 | } 158 | -------------------------------------------------------------------------------- /libpcs_io/pcs_endian.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_ENDIAN_H__ 6 | #define __PCS_ENDIAN_H__ 7 | 8 | #include "pcs_types.h" 9 | 10 | /* ----- little endian / big endian ----- */ 11 | 12 | typedef u64 le64; 13 | typedef u32 le32; 14 | typedef u16 le16; 15 | typedef u8 le8; 16 | 17 | typedef u64 be64; 18 | typedef u32 be32; 19 | typedef u16 be16; 20 | typedef u8 be8; 21 | 22 | #if defined(__GLIBC__) /* Linux, SPARC, etc. */ 23 | #include 24 | #include 25 | #elif defined(__MAC__) || defined(__WINDOWS__) || defined(__x86_64__) 26 | /* define Mac and Windows as litle endian */ 27 | #define __LITTLE_ENDIAN 1234 28 | #define __BIG_ENDIAN 4321 29 | #define __BYTE_ORDER __LITTLE_ENDIAN 30 | #else 31 | #error "unknown endianes" 32 | #endif 33 | 34 | #ifdef _MSC_VER 35 | #include 36 | #define bswap_16 _byteswap_ushort 37 | #define bswap_32 _byteswap_ulong 38 | #define bswap_64 _byteswap_uint64 39 | #endif 40 | 41 | #ifdef __MAC__ 42 | #define bswap_16 __builtin_bswap16 43 | #define bswap_32 __builtin_bswap32 44 | #define bswap_64 __builtin_bswap64 45 | #endif 46 | 47 | #ifdef __SUN__ 48 | #include 49 | #define bswap_16 BSWAP_16 50 | #define bswap_32 BSWAP_32 51 | #define bswap_64 BSWAP_64 52 | #endif 53 | 54 | #define bswap_none(x) (x) 55 | 56 | #if (defined(__GNUC__) || defined(__clang__)) && !defined(__cplusplus) 57 | /* compile-time macro to verify that bswap_XXX() are called with correct argument type and expected bitness */ 58 | #define BSWAP_VERIFY(fn, x, sz) ((sizeof(x) == sz) ? fn(x) : (u8)sizeof(struct {char bswap_arg_size_mismatch[(sizeof(x) == sz) ? 1 : -1]; })) 59 | #else 60 | #define BSWAP_VERIFY(fn, x, sz) fn(x) 61 | #endif 62 | 63 | #if __BYTE_ORDER == __LITTLE_ENDIAN 64 | #define cpu_to_le8(x) BSWAP_VERIFY(bswap_none, x, 1) 65 | #define cpu_to_le16(x) BSWAP_VERIFY(bswap_none, x, 2) 66 | #define cpu_to_le32(x) BSWAP_VERIFY(bswap_none, x, 4) 67 | #define cpu_to_le64(x) BSWAP_VERIFY(bswap_none, x, 8) 68 | #define le8_to_cpu(x) BSWAP_VERIFY(bswap_none, x, 1) 69 | #define le16_to_cpu(x) BSWAP_VERIFY(bswap_none, x, 2) 70 | #define le32_to_cpu(x) BSWAP_VERIFY(bswap_none, x, 4) 71 | #define le64_to_cpu(x) BSWAP_VERIFY(bswap_none, x, 8) 72 | 73 | #define cpu_to_be8(x) BSWAP_VERIFY(bswap_none, x, 1) 74 | #define cpu_to_be16(x) BSWAP_VERIFY(bswap_16, x, 2) 75 | #define cpu_to_be32(x) BSWAP_VERIFY(bswap_32, x, 4) 76 | #define cpu_to_be64(x) BSWAP_VERIFY(bswap_64, x, 8) 77 | #define be8_to_cpu(x) BSWAP_VERIFY(bswap_none, x, 1) 78 | #define be16_to_cpu(x) BSWAP_VERIFY(bswap_16, x, 2) 79 | #define be32_to_cpu(x) BSWAP_VERIFY(bswap_32, x, 4) 80 | #define be64_to_cpu(x) BSWAP_VERIFY(bswap_64, x, 8) 81 | #else 82 | #define cpu_to_le8(x) BSWAP_VERIFY(bswap_none, x, 1) 83 | #define cpu_to_le16(x) BSWAP_VERIFY(bswap_16, x, 2) 84 | #define cpu_to_le32(x) BSWAP_VERIFY(bswap_32, x, 4) 85 | #define cpu_to_le64(x) BSWAP_VERIFY(bswap_64, x, 8) 86 | #define le8_to_cpu(x) BSWAP_VERIFY(bswap_none, x, 1) 87 | #define le16_to_cpu(x) BSWAP_VERIFY(bswap_16, x, 2) 88 | #define le32_to_cpu(x) BSWAP_VERIFY(bswap_32, x, 4) 89 | #define le64_to_cpu(x) BSWAP_VERIFY(bswap_64, x, 8) 90 | 91 | #define cpu_to_be8(x) BSWAP_VERIFY(bswap_none, x, 1) 92 | #define cpu_to_be16(x) BSWAP_VERIFY(bswap_none, x, 2) 93 | #define cpu_to_be32(x) BSWAP_VERIFY(bswap_none, x, 4) 94 | #define cpu_to_be64(x) BSWAP_VERIFY(bswap_none, x, 8) 95 | #define be8_to_cpu(x) BSWAP_VERIFY(bswap_none, x, 1) 96 | #define be16_to_cpu(x) BSWAP_VERIFY(bswap_none, x, 2) 97 | #define be32_to_cpu(x) BSWAP_VERIFY(bswap_none, x, 4) 98 | #define be64_to_cpu(x) BSWAP_VERIFY(bswap_none, x, 8) 99 | #endif 100 | 101 | static inline int pcs_cpu_little_endian(void) 102 | { 103 | const uint64_t n = 1; 104 | return *(char *)&n; 105 | } 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /libpcs_io/Makefile: -------------------------------------------------------------------------------- 1 | include ../Makefile.inc 2 | 3 | LIBPCS_IO=libpcs_io.so 4 | LIBOBJS=pcs_sock.o pcs_sock_conn.o pcs_sock_io.o pcs_sock_listen.o pcs_net_addr.o pcs_net_utils.o \ 5 | pcs_sock_ssl.o pcs_sock_ssl_st.o pcs_sock_ssl_mt.o \ 6 | pcs_process.o pcs_epoll.o pcs_kqueue.o pcs_sun_port.o pcs_event_ioconn.o pcs_ioconn.o \ 7 | pcs_compat.o pcs_aio.o pcs_sync_io.o pcs_sync_ioreq.o pcs_file_job.o pcs_atexit.o \ 8 | rbtree.o minheap.o timer.o log.o bug.o md5.o crc32.o crc32_sse.o crc32_arm_wrapper.o crc64.o adler32.o \ 9 | pcs_profiler.o pcs_watchdog.o logrotate.o pool_allocator.o pcs_config_file.o user.o \ 10 | pcs_signal.o pcs_random.o getopt_long.o \ 11 | pcs_malloc.o pcs_thread.o pcs_coroutine.o pcs_co_locks.o pcs_co_io.o \ 12 | lz4.o lz4_estimate.o parse_diskspace.o \ 13 | qselect.o regdump.o syslog.o pcs_shaper.o bufqueue.o pcs_upipe.o pcs_dir.o \ 14 | pcs_splice.o pcs_error.o ssl_helpers.o pcs_cpuid.o std_list.o pcs_context.o pcs_mr_malloc.o \ 15 | pcs_ucontext.o pcs_exec.o 16 | LIBOBJS += $(if $(PCS_ENABLE_RDMA), pcs_rdma_conn.o pcs_rdma_listen.o pcs_rdma_io.o) 17 | LIBOBJS_ASM=crc32_neon.o 18 | 19 | COREHDRS=$(shell ls *.h | grep -v license) 20 | 21 | CFLAGS += -fPIC -I. 22 | CFLAGS += $(if $(PCS_ENABLE_ZSTD), -D_ENABLE_ZSTD_COMPRESSION,) 23 | CFLAGS += $(if $(PCS_ENABLE_INTEL_ISAL), -DPCS_ENABLE_INTEL_ISAL,) 24 | CFLAGS += $(if $(findstring Linux,$(UNAME)), -DHAVE_LIBUNWIND -DHAVE_LINUX_CAPS) 25 | 26 | lz4.o: CFLAGS += -O3 27 | lz4_estimate.o: CFLAGS += -O3 28 | crc32_sse.o: CFLAGS += -O3 29 | LDFLAGS += -fPIC 30 | LDLIBS += -lssl -lcrypto 31 | LDLIBS += $(if $(PCS_ENABLE_ZSTD), -lzstd,) 32 | LDLIBS += $(if $(PCS_ENABLE_INTEL_ISAL), -lisal,) 33 | LDLIBS += $(if $(PCS_ENABLE_RDMA), -lrdmacm -libverbs,) 34 | LDLIBS += $(if $(findstring Linux,$(UNAME)), -lunwind) 35 | 36 | ifdef PCS_TCMALLOC 37 | LIBTCMALLOC += -Wl,-Bstatic -Wl,-whole-archive -ltcmalloc_minimal -Wl,-no-whole-archive -Wl,-Bdynamic -lstdc++ 38 | endif 39 | LIBRT=$(if $(findstring Linux,$(UNAME)),-lrt) 40 | 41 | all: $(LIBPCS_IO) 42 | 43 | $(LIBPCS_IO): $(LIBOBJS) $(LIBOBJS_ASM) 44 | $(CC) $(LDFLAGS) -shared $(SONAME_FLAGS) -o $@.$(LIB_VER) $^ $(LIBTCMALLOC) $(LIBRT) $(LDLIBS) -lz 45 | ln -sf $@.$(LIB_VER) $@.$(SONAME_MJ_VER) 46 | ln -sf $@.$(LIB_VER) $@ 47 | 48 | depend dep: 49 | $(CC) $(CFLAGS) -M $(LIBOBJS:.o=.c) > .depend 50 | $(CC) $(CFLAGS) -M $(LIBOBJS_ASM:.o=.S) >> .depend 51 | 52 | PKG_CFG_CFLAGS += $(if $(PCS_ADDR_SANIT),-fsanitize=address -fno-omit-frame-pointer,) 53 | PKG_CFG_LDFLAGS += $(if $(PCS_ADDR_SANIT),-lasan,) 54 | 55 | libpcs_io-$(SONAME_MJ_VER).pc: 56 | sed "s/__VERSION/$(PCS_BUILD_VERSION)/;s/_PKG_CFG_CFLAGS/$(PKG_CFG_CFLAGS)/;s/_PKG_CFG_LDFLAGS/$(PKG_CFG_LDFLAGS)/" libpcs_io.pc > $@ 57 | cat $@ 58 | 59 | clean: 60 | rm -rf *.so.* 61 | rm -f *.o *.a *.so *.8 .depend 62 | rm -f *.gcda *.gcno 63 | rm -f libpcs_io-$(SONAME_MJ_VER).pc 64 | 65 | install-core-headers: 66 | install -d $(INSTALL_PREFIX)/usr/include/pcs-core 67 | for hdr in $(COREHDRS); do install -m 644 $$hdr $(INSTALL_PREFIX)/usr/include/pcs-core; done 68 | 69 | install-devel-libs: libpcs_io-$(SONAME_MJ_VER).pc 70 | install -d $(INSTALL_PREFIX)/$(PCS_LIB_DIR) 71 | install -d $(INSTALL_PREFIX)/$(PCS_LIB_DIR)/pkgconfig 72 | install -m 644 libpcs_io-$(SONAME_MJ_VER).pc $(INSTALL_PREFIX)/$(PCS_LIB_DIR)/pkgconfig 73 | 74 | install: $(LIBPCS_IO) install-core-headers install-devel-libs 75 | install -d $(INSTALL_PREFIX)/$(PCS_INCLUDE_DIR) 76 | install -m 644 std_list.h log.h pcs_types.h rbtree.h bug.h pcs_compat.h pcs_config.h pcs_align.h \ 77 | timer.h pcs_error.h $(INSTALL_PREFIX)/$(PCS_INCLUDE_DIR) 78 | install $(LIBPCS_IO).$(LIB_VER) $(INSTALL_PREFIX)/$(PCS_LIB_DIR) 79 | ln -sf $(LIBPCS_IO).$(LIB_VER) $(INSTALL_PREFIX)/$(PCS_LIB_DIR)/$(LIBPCS_IO) 80 | ln -sf $(LIBPCS_IO).$(LIB_VER) $(INSTALL_PREFIX)/$(PCS_LIB_DIR)/$(LIBPCS_IO).$(SONAME_MJ_VER) 81 | 82 | ifeq (.depend,$(wildcard .depend)) 83 | include .depend 84 | else 85 | all: depend 86 | endif 87 | -------------------------------------------------------------------------------- /libpcs_io/pcs_sun_port.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_poll.h" 6 | 7 | #ifdef __SUN__ 8 | 9 | #include "pcs_process.h" 10 | #include "pcs_ioconn.h" 11 | #include "pcs_co_io.h" 12 | #include "pcs_co_locks.h" 13 | #include "bug.h" 14 | #include "log.h" 15 | 16 | #include 17 | #include 18 | 19 | int pcs_poll_ctl(struct pcs_process *proc, struct pcs_ioconn *conn) 20 | { 21 | if (conn->next_mask == 0) 22 | return port_dissociate(proc->port, PORT_SOURCE_FD, conn->fd); 23 | 24 | return port_associate(proc->port, PORT_SOURCE_FD, conn->fd, conn->next_mask, conn); 25 | } 26 | 27 | 28 | void pcs_poll_wait(struct pcs_evloop *evloop, int timeout) 29 | { 30 | BUG_ON(evloop->nr_events); 31 | 32 | evloop->events[0].portev_user = NULL; 33 | 34 | struct timespec ts = {.tv_sec = timeout / 1000, .tv_nsec = (timeout % 1000) * 1000000}; 35 | uint_t nr = 1; 36 | if (port_getn(evloop->proc->port, evloop->events, PCS_MAX_EVENTS_NR, &nr, &ts) >= 0 || errno == ETIME) 37 | evloop->nr_events = nr; 38 | } 39 | 40 | static void process_ioconn_event(port_event_t *ev) 41 | { 42 | struct pcs_ioconn *conn = ev->portev_user; 43 | int events = ev->portev_events; 44 | 45 | switch (ev->portev_source) { 46 | case PORT_SOURCE_FD: 47 | conn->actual_mask = 0; 48 | 49 | if (likely(events & POLLIN)) 50 | conn->data_ready(conn); 51 | 52 | if (unlikely(events & POLLOUT)) 53 | conn->write_space(conn); 54 | 55 | if (unlikely(events & (POLLERR|POLLHUP|POLLNVAL))) 56 | conn->error_report(conn); 57 | 58 | pcs_ioconn_schedule(conn); 59 | break; 60 | 61 | case PORT_SOURCE_USER: 62 | conn->data_ready(conn); 63 | break; 64 | 65 | default: 66 | BUG(); 67 | } 68 | } 69 | 70 | static void update_co_file_poll_mask(struct pcs_co_file *file, int mask_clear, int mask_set) 71 | { 72 | pthread_mutex_lock(&file->mutex); 73 | file->mask = (file->mask & ~mask_clear) | mask_set; 74 | if (file->mask) { 75 | if (port_associate(pcs_current_proc->port, PORT_SOURCE_FD, file->fd, file->mask, (void *)((ULONG_PTR)file | 1))) { 76 | pcs_log_syserror(LOG_ERR, errno, "update_co_file_poll_mask: port_associate failed"); 77 | BUG(); 78 | } 79 | } 80 | pthread_mutex_unlock(&file->mutex); 81 | } 82 | 83 | static void process_co_file_event(port_event_t *ev) 84 | { 85 | BUG_ON(ev->portev_source != PORT_SOURCE_FD); 86 | 87 | unsigned int events = ev->portev_events; 88 | if (events & (POLLERR|POLLHUP|POLLNVAL)) 89 | events |= POLLIN|POLLOUT; 90 | 91 | struct pcs_co_file *file = (struct pcs_co_file *)((ULONG_PTR)ev->portev_user & ~(ULONG_PTR)1); 92 | update_co_file_poll_mask(file, events, 0); 93 | 94 | if (events & POLLIN) 95 | pcs_co_event_signal(&file->reader.ev); 96 | if (events & POLLOUT) 97 | pcs_co_event_signal(&file->writer.ev); 98 | } 99 | 100 | void pcs_poll_process_events(struct pcs_evloop *evloop) 101 | { 102 | int i; 103 | for (i = 0; i < evloop->nr_events; i++) { 104 | port_event_t *ev = &evloop->events[i]; 105 | 106 | if ((ULONG_PTR)ev->portev_user & 1) 107 | process_co_file_event(ev); 108 | else 109 | process_ioconn_event(ev); 110 | } 111 | 112 | evloop->nr_events = 0; 113 | } 114 | 115 | int pcs_poll_init(struct pcs_process *proc) 116 | { 117 | proc->port = port_create(); 118 | if (proc->port < 0) 119 | return -errno; 120 | return 0; 121 | } 122 | 123 | void pcs_poll_fini(struct pcs_process *proc) 124 | { 125 | if (proc->port >= 0) 126 | close(proc->port); 127 | proc->port = -1; 128 | } 129 | 130 | void pcs_poll_file_init(struct pcs_co_file *file) 131 | { 132 | } 133 | 134 | void pcs_poll_file_fini(struct pcs_co_file *file) 135 | { 136 | update_co_file_poll_mask(file, ~0, 0); 137 | } 138 | 139 | void pcs_poll_file_begin(struct pcs_co_file *file, int mask) 140 | { 141 | update_co_file_poll_mask(file, 0, mask); 142 | } 143 | 144 | #endif /* __SUN__ */ 145 | -------------------------------------------------------------------------------- /libpcs_io/pcs_atomic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_ATOMIC_H_ 6 | #define _PCS_ATOMIC_H_ 7 | 8 | #include "pcs_types.h" 9 | 10 | /** 11 | * This is a pcs atomic API 12 | * 13 | * Under GCC and clang we use if it is available 14 | * If it is not, we use gnu built-in atomic API: https://gcc.gnu.org/onlinedocs/gcc-4.5.4/gcc/Atomic-Builtins.html 15 | * Under MSVC we use its atomic intrinsics: https://msdn.microsoft.com/en-us/library/hh977022.aspx 16 | * 17 | * Currently, we have 2 data types -- pcs_atomic32_t and pcs_atomic64_t 18 | * 19 | * List of available functions: 20 | * 21 | * 1. 22 | * void pcs_atomic32_store(pcs_atomic32_t *obj, s32 val) 23 | * sets the value of @obj to @val 24 | * 25 | * 2. 26 | * s32 pcs_atomic_load(pcs_atomic32_t *obj) 27 | * returns the value of @obj 28 | * 29 | * 3. 30 | * s32 pcs_atomic32_fetch_and_add(pcs_atomic32_t *obj, s32 val) 31 | * returns the old value of @obj and then adds @val to it 32 | * 33 | * 4. 34 | * void pcs_atomic32_add(pcs_atomic32_t *obj, s32 val) 35 | * same as the function above but without fetching the old value 36 | * 37 | * 5. 38 | * s32 pcs_atomic32_fetch_and_sub(pcs_atomic32_t *obj, s32 val) 39 | * returns the old value of @obj and then subtracts @val from it 40 | * 41 | * 6. 42 | * void pcs_atomic32_sub(pcs_atomic32_t *obj, s32 val) 43 | * same as the function above but without fetching the old value 44 | * 45 | * 7. 46 | * void pcs_atomic32_fetch_and_inc(pcs_atomic32_t *obj) 47 | * returns the old value of @obj and then increments @obj by 1 48 | * 49 | * 8. 50 | * void pcs_atomic32_inc(pcs_atomic32_t *obj) 51 | * same as the function above but without fetching the old value 52 | * 53 | * 9. 54 | * void pcs_atomic32_fetch_and_dec(pcs_atomic32_t *obj) 55 | * returns the old value of @obj and then decrements @obj by 1 56 | * 57 | * 10. 58 | * void pcs_atomic32_dec(pcs_atomic32_t *obj) 59 | * same as the function above but without fetching the old value 60 | * 61 | * 11. 62 | * s32 pcs_atomic32_cas(pcs_atomic32_t *obj, s32 old_val, s32 new_val) 63 | * if the value of @obj is equal to @old_val, sets it to @new_val 64 | * regardles of its success, returs the value of @obj at the begginning of this operation 65 | * 66 | * 67 | * The same list of fuctions is available with _s64 suffix 68 | * 69 | * 70 | * Implementation of memory barriers for ARM and x86 architectures. 71 | * As x86 has a strong memory model, we mostly need it for ARM architecture. 72 | * 73 | * Hardware memory barriers: 74 | * 75 | * 1. 76 | * void pcs_rmb(void) 77 | * read memory barrier (#LoadLoad semantics) 78 | * 79 | * 2. 80 | * void pcs_wmb(void) 81 | * write memory barrier (#StoreStore semantics) 82 | * 83 | * 3. 84 | * void pcs_mb(void) 85 | * full memory barrier (#LoadLoad + #LoadStore + #StoreStre + #StoreLoad semantics) 86 | * 87 | * Compiler memory barrier: 88 | * 89 | * 1. 90 | * void pcs_compiler_mb() 91 | * full compiler barrier 92 | */ 93 | 94 | #if defined(__GNUC__) || defined(__clang__) 95 | #include "pcs_atomic_gcc.h" 96 | #elif defined(_MSC_VER) 97 | #include "pcs_atomic_msvc.h" 98 | #else 99 | #error "Unknown compiler" 100 | #endif 101 | 102 | static inline void pcs_atomic_ptr_store(pcs_atomic_ptr_t *obj, void *val) 103 | { 104 | pcs_atomic_uptr_store(obj, (ULONG_PTR)val); 105 | } 106 | 107 | static inline void *pcs_atomic_ptr_load(pcs_atomic_ptr_t *obj) 108 | { 109 | return (void *)pcs_atomic_uptr_load(obj); 110 | } 111 | 112 | static inline void *pcs_atomic_ptr_exchange(pcs_atomic_ptr_t *obj, void *val) 113 | { 114 | return (void *)pcs_atomic_uptr_exchange(obj, (ULONG_PTR)val); 115 | } 116 | 117 | static inline void *pcs_atomic_ptr_cas(pcs_atomic_ptr_t *obj, void *old_val, void *new_val) 118 | { 119 | return (void *)pcs_atomic_uptr_cas(obj, (ULONG_PTR)old_val, (ULONG_PTR)new_val); 120 | } 121 | 122 | #endif /* _PCS_ATOMIC_H_ */ 123 | -------------------------------------------------------------------------------- /libpcs_io/pcs_sync_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_SYNC_IO_H_ 6 | #define _PCS_SYNC_IO_H_ 1 7 | 8 | #include "pcs_dir.h" 9 | 10 | /* returns number of bytes or -errno */ 11 | PCS_API int pcs_sync_nwrite(pcs_fd_t fd, u64 offs, void const *buf, int sz); 12 | PCS_API int pcs_sync_nread(pcs_fd_t fd, u64 offs, void *buf, int sz); 13 | PCS_API int pcs_sync_swrite(pcs_fd_t fd, void const *buf, int sz); 14 | PCS_API int pcs_sync_sread(pcs_fd_t fd, void *buf, int sz); 15 | 16 | /* returns 0 or -errno */ 17 | PCS_API int pcs_sync_fallocate(pcs_fd_t fd, u64 offset, u64 len); 18 | /* before punching holes file must be marked as sparse on Windows */ 19 | PCS_API int pcs_sync_make_sparse(pcs_fd_t fd); 20 | PCS_API int pcs_sync_punch_hole(pcs_fd_t fd, u64 offset, u64 len); 21 | PCS_API int pcs_sync_zero_range(pcs_fd_t fd, u64 offset, u64 len); 22 | PCS_API int pcs_sync_ftruncate(pcs_fd_t fd, u64 len); 23 | PCS_API int pcs_sync_fsync(pcs_fd_t fd); 24 | PCS_API int pcs_sync_fdatasync(pcs_fd_t fd); 25 | PCS_API int pcs_sync_getfsize(pcs_fd_t fd, u64 * size); 26 | PCS_API int pcs_sync_open(const char * pathname, int flag, int mode, pcs_fd_t * out_fd); 27 | PCS_API int pcs_sync_openat(pcs_fd_t dirfd, const char * pathname, int flag, int mode, pcs_fd_t * out_fd); 28 | PCS_API int pcs_sync_close(pcs_fd_t fd); 29 | PCS_API int pcs_sync_mkdir(const char *pathname, int mode); 30 | PCS_API int pcs_sync_mkdirat(pcs_fd_t dirfd, const char *filename, int mode); 31 | PCS_API int pcs_sync_rmdir(const char *pathname); 32 | PCS_API int pcs_sync_rmdirat(pcs_fd_t dirfd, const char *filename); 33 | PCS_API int pcs_sync_unlink(const char * pathname); 34 | PCS_API int pcs_sync_unlinkat(pcs_fd_t dirfd, const char * filename, int flags); 35 | PCS_API int pcs_sync_rename(const char * oldpath, const char * newpath); 36 | PCS_API int pcs_sync_renameat(pcs_fd_t olddirfd, const char * oldname, pcs_fd_t newdirfd, const char * newname); 37 | PCS_API int pcs_sync_lseek(pcs_fd_t fd, u64 offs, int origin, u64 *new_offs); 38 | PCS_API int pcs_sync_ioctl(pcs_fd_t fd, unsigned long int cmd, void *data); 39 | /* Try to lock or unlock file range. 40 | * Cmd is F_SETLK|F_SETLKW or F_OFD_SETLK|F_OFD_SETLKW (Linux and Mac only). 41 | * Type is one of F_RDLCK|F_WRLCK|F_UNLCK. 42 | * Note: range locks are mandatory on Windows, advisory on UNIX. 43 | * Returns 0 if lock is acquired, -EACCES or -EAGAIN if lock is held by another process */ 44 | PCS_API int pcs_sync_lock(pcs_fd_t fd, int cmd, short int type, u64 offs, u64 len); 45 | int pcs_sync_create_lock_file(const char *path, pcs_fd_t *out_fd); 46 | int pcs_sync_close_lock_file(const char *path, pcs_fd_t fd); 47 | 48 | struct stat; 49 | struct pcs_stat; 50 | #define PCS_SYNC_NOFOLLOW (1 << 0) 51 | PCS_API int pcs_sync_stat(const char *path, int flags, struct pcs_stat *res); 52 | PCS_API int pcs_sync_fstat(pcs_fd_t fd, struct pcs_stat *res); 53 | PCS_API int pcs_sync_fstatat(pcs_fd_t dirfd, const char *filename, struct pcs_stat *res); 54 | void pcs_stat2pcs(const struct stat *st, struct pcs_stat *res); 55 | 56 | struct statvfs; 57 | 58 | struct pcs_statvfs 59 | { 60 | u64 bsize; /* Filesystem block size */ 61 | u64 frsize; /* Fragment size */ 62 | u64 blocks; /* Size of fs in @frsize units */ 63 | u64 bfree; /* Number of free blocks */ 64 | u64 bavail; /* Number of free blocks for unprivileged users */ 65 | u64 files; /* Number of inodes */ 66 | u64 ffree; /* Number of free inodes */ 67 | u64 favail; /* Number of free inodes for unprivileged users */ 68 | u64 fsid; /* Filesystem ID */ 69 | u64 flag; /* Mount flags */ 70 | u64 namemax; /* Maximum filename length */ 71 | }; 72 | 73 | PCS_API int pcs_sync_statvfs(const char *path, struct pcs_statvfs *res); 74 | PCS_API int pcs_sync_fstatvfs(pcs_fd_t fd, struct pcs_statvfs *res); 75 | void pcs_statvfs2pcs(const struct statvfs *st, struct pcs_statvfs *res); 76 | 77 | PCS_API pcs_fd_t pcs_stdin_fd(void); 78 | PCS_API pcs_fd_t pcs_stdout_fd(void); 79 | PCS_API pcs_fd_t pcs_stderr_fd(void); 80 | 81 | /* Internal function. Not exported */ 82 | char *pcs_pathat(pcs_fd_t dirfd, const char *pathname); 83 | 84 | #endif /* _PCS_SYNC_IO_H_ */ 85 | -------------------------------------------------------------------------------- /libpcs_io/pcs_epoll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_poll.h" 6 | 7 | #ifdef HAVE_EPOLL 8 | 9 | #include "pcs_process.h" 10 | #include "pcs_ioconn.h" 11 | #include "pcs_co_io.h" 12 | #include "pcs_co_locks.h" 13 | #include "bug.h" 14 | #include "log.h" 15 | 16 | #include 17 | #include 18 | 19 | int pcs_poll_ctl(struct pcs_process *proc, struct pcs_ioconn *conn) 20 | { 21 | int op; 22 | if (conn->next_mask == 0) 23 | op = EPOLL_CTL_DEL; 24 | else if (conn->actual_mask == 0) 25 | op = EPOLL_CTL_ADD; 26 | else 27 | op = EPOLL_CTL_MOD; 28 | 29 | struct epoll_event ev = {.events = conn->next_mask, .data = {.ptr = conn}}; 30 | return epoll_ctl(proc->epollfd, op, conn->fd, &ev); 31 | } 32 | 33 | void pcs_poll_wait(struct pcs_evloop *evloop, int timeout) 34 | { 35 | BUG_ON(evloop->nr_events > 0); 36 | 37 | for (;;) { 38 | evloop->nr_events = epoll_wait(evloop->proc->epollfd, evloop->events, PCS_MAX_EVENTS_NR, timeout); 39 | if (evloop->nr_events >= 0 || errno != EINTR) 40 | break; 41 | 42 | /* Optimization for the idle case. 43 | * Profiler timer is not disabled by default as it is quite expensive operation. 44 | * So signal still kicks out us of epoll and disables itself, then we get EINTR and need to restart with new timeout cause no fds are ready. 45 | * if we don't restart, timer will be re-armed in event loop and we will never sleep more then 10ms even when 100% idle */ 46 | timeout = get_timers_timeout(&evloop->timers); 47 | } 48 | } 49 | 50 | static void process_ioconn_event(struct epoll_event *ev) 51 | { 52 | struct pcs_ioconn *conn = ev->data.ptr; 53 | int events = ev->events; 54 | 55 | if (likely(events & POLLIN)) 56 | conn->data_ready(conn); 57 | 58 | if (unlikely(events & POLLOUT)) 59 | conn->write_space(conn); 60 | 61 | if (unlikely(events & (POLLERR|POLLHUP|POLLNVAL|POLLRDHUP))) 62 | conn->error_report(conn); 63 | 64 | pcs_ioconn_schedule(conn); 65 | } 66 | 67 | static void process_co_file_event(struct epoll_event *ev) 68 | { 69 | struct pcs_co_file *file = (struct pcs_co_file *)((ULONG_PTR)ev->data.ptr & ~(ULONG_PTR)1); 70 | int events = ev->events; 71 | 72 | if (events & (POLLERR|POLLHUP|POLLNVAL|POLLRDHUP)) 73 | pcs_atomic32_or(&file->err_mask, events); 74 | 75 | if (events & (POLLIN|POLLERR|POLLHUP|POLLNVAL|POLLRDHUP)) 76 | pcs_co_event_signal(&file->reader.ev); 77 | 78 | if (events & (POLLOUT|POLLERR|POLLHUP|POLLNVAL)) 79 | pcs_co_event_signal(&file->writer.ev); 80 | } 81 | 82 | void pcs_poll_process_events(struct pcs_evloop *evloop) 83 | { 84 | int i; 85 | for (i = 0; i < evloop->nr_events; i++) { 86 | struct epoll_event *ev = &evloop->events[i]; 87 | 88 | if ((ULONG_PTR)ev->data.ptr & 1) 89 | process_co_file_event(ev); 90 | else 91 | process_ioconn_event(ev); 92 | } 93 | 94 | evloop->nr_events = 0; 95 | } 96 | 97 | int pcs_poll_init(struct pcs_process *proc) 98 | { 99 | #ifdef EPOLL_CLOEXEC 100 | proc->epollfd = epoll_create1(EPOLL_CLOEXEC); 101 | if (proc->epollfd < 0) 102 | return -errno; 103 | #else 104 | proc->epollfd = epoll_create(1); /* size is ignored */ 105 | if (proc->epollfd < 0) 106 | return -errno; 107 | 108 | fcntl(proc->epollfd, F_SETFD, FD_CLOEXEC); 109 | #endif 110 | return 0; 111 | } 112 | 113 | void pcs_poll_fini(struct pcs_process *proc) 114 | { 115 | if (proc->epollfd >= 0) 116 | close(proc->epollfd); 117 | proc->epollfd = -1; 118 | } 119 | 120 | void pcs_poll_file_init(struct pcs_co_file *file) 121 | { 122 | pcs_atomic32_store(&file->err_mask, 0); 123 | struct epoll_event ev = {.events = EPOLLET | POLLIN | POLLOUT | POLLRDHUP, .data = {.ptr = (void *)((ULONG_PTR)file | 1)}}; 124 | if (epoll_ctl(pcs_current_proc->epollfd, EPOLL_CTL_ADD, file->fd, &ev)) { 125 | pcs_log_syserror(LOG_ERR, errno, "pcs_poll_file_init: epoll_ctl failed"); 126 | BUG(); 127 | } 128 | } 129 | 130 | void pcs_poll_file_fini(struct pcs_co_file *file) 131 | { 132 | } 133 | 134 | void pcs_poll_file_begin(struct pcs_co_file *file, int mask) 135 | { 136 | } 137 | 138 | #endif /* HAVE_EPOLL */ 139 | -------------------------------------------------------------------------------- /libpcs_io/pcs_compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCSIO_COMPAT_H__ 6 | #define __PCSIO_COMPAT_H__ 7 | 8 | #include "pcs_types.h" 9 | #include "pcs_config.h" 10 | #include 11 | 12 | #if defined(__WINDOWS__) || defined(__SUN__) 13 | #include 14 | 15 | #define DT_UNKNOWN 0 16 | #define DT_FIFO 1 17 | #define DT_CHR 2 18 | #define DT_DIR 4 19 | #define DT_BLK 6 20 | #define DT_REG 8 21 | #define DT_LNK 10 22 | #define DT_SOCK 12 23 | #define DT_WHT 14 24 | 25 | #define IFTODT(m) (((m) & S_IFMT) >> 12) 26 | #define DTTOIF(t) ((t) << 12) 27 | #endif 28 | 29 | #ifdef __WINDOWS__ 30 | #define S_IFIFO DTTOIF(DT_FIFO) 31 | #define S_IFBLK DTTOIF(DT_BLK) 32 | #define S_IFLNK DTTOIF(DT_LNK) 33 | #define S_IFSOCK DTTOIF(DT_SOCK) 34 | 35 | #define S_ISUID 0x800 36 | #define S_ISGID 0x400 37 | #define S_ISVTX 0x200 38 | 39 | #define S_IRUSR S_IREAD 40 | #define S_IWUSR S_IWRITE 41 | #define S_IXUSR S_IEXEC 42 | #define S_IRWXU (S_IREAD | S_IWRITE | S_IEXEC) 43 | 44 | #define S_IRGRP (S_IRUSR >> 3) 45 | #define S_IWGRP (S_IWUSR >> 3) 46 | #define S_IXGRP (S_IXUSR >> 3) 47 | #define S_IRWXG (S_IRWXU >> 3) 48 | 49 | #define S_IROTH (S_IRGRP >> 3) 50 | #define S_IWOTH (S_IWGRP >> 3) 51 | #define S_IXOTH (S_IXGRP >> 3) 52 | #define S_IRWXO (S_IRWXG >> 3) 53 | 54 | #define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) 55 | #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) 56 | #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 57 | #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) 58 | #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 59 | #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) 60 | #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) 61 | 62 | #define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR) 63 | #define O_DIRECT 0x40000000 64 | 65 | #define F_RDLCK 1 66 | #define F_UNLCK 2 67 | #define F_WRLCK 3 68 | 69 | #define F_SETLK 6 70 | #define F_SETLKW 7 71 | 72 | struct iovec { 73 | void *iov_base; 74 | size_t iov_len; 75 | }; 76 | 77 | #else /* __WINDOWS__ */ 78 | 79 | #include 80 | 81 | #if defined(__LINUX__) && __GLIBC_PREREQ(2, 8) 82 | #define HAVE_SYNC_FILE_RANGE 83 | #endif 84 | 85 | #ifndef O_DIRECT 86 | #define O_DIRECT 0 87 | #endif 88 | 89 | #ifndef F_OFD_SETLK 90 | 91 | #if defined(__LINUX__) 92 | #define F_OFD_SETLK 37 93 | #define F_OFD_SETLKW 38 94 | #elif defined(__MAC__) 95 | /* https://github.com/apple/darwin-xnu/blob/xnu-3247.1.106/bsd/sys/fcntl.h#L353 */ 96 | #define F_OFD_SETLK 90 97 | #define F_OFD_SETLKW 91 98 | #endif 99 | 100 | #endif /* F_OFD_SETLK */ 101 | 102 | #endif /* __WINDOWS__ */ 103 | 104 | PCS_API unsigned int pcs_nr_processors(void); 105 | PCS_API unsigned int pcs_sys_page_size(void); 106 | PCS_API u64 pcs_phys_memory_size(void); 107 | 108 | #ifndef __LINUX__ 109 | struct fiemap_extent { 110 | u64 fe_logical; 111 | u64 fe_physical; 112 | u64 fe_length; 113 | u32 fe_flags; 114 | }; 115 | 116 | struct fiemap { 117 | u32 fm_mapped_extents; 118 | struct fiemap_extent fm_extents[0]; 119 | }; 120 | 121 | #define FIEMAP_EXTENT_DELALLOC 0x00000004 122 | #define FIEMAP_EXTENT_UNWRITTEN 0x00000800 123 | #endif /* __LINUX__ */ 124 | 125 | #ifdef __WINDOWS__ 126 | PCS_API int fsync(int fd); 127 | PCS_API int ftruncate(int fd, u64 len); 128 | PCS_API char *strndup(const char *s, size_t size); 129 | 130 | #if (_MSC_VER < 1900) && !defined(snprintf) 131 | #include 132 | 133 | PCS_API int pcs_vsnprintf(char *str, size_t size, const char *format, va_list ap); 134 | PCS_API int pcs_snprintf(char *str, size_t size, const char *format, ...); 135 | 136 | #define vsnprintf pcs_vsnprintf 137 | #define snprintf pcs_snprintf 138 | #endif /* _MSC_VER < 1900 */ 139 | 140 | /* For null-terminated string -1 can be used as len. 141 | Returns (m)allocated widechar string or null. Check GetLastError() for failure reason. */ 142 | PCS_API WCHAR * pcs_utf8_to_utf16(const char * str, int len); 143 | PCS_API char * pcs_utf16_to_utf8(const WCHAR * wstr, int wlen); 144 | 145 | #define strtok_r strtok_s 146 | 147 | #endif /* _MSC_VER */ 148 | 149 | #endif /* __PCSIO_COMPAT_H__ */ 150 | -------------------------------------------------------------------------------- /libpcs_io/pcs_sync_ioreq.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_SYNC_IOREQ_H_ 6 | #define _PCS_SYNC_IOREQ_H_ 1 7 | 8 | #include 9 | #include "pcs_process.h" 10 | #include "pcs_thread.h" 11 | #include "pcs_error.h" 12 | #include "pcs_event_ioconn.h" 13 | 14 | #define FSYNC_IS_BARRIER 1 15 | 16 | struct pcs_sync_ioreq; 17 | typedef void (*pcs_sync_io_cb)(struct pcs_sync_ioreq *); 18 | 19 | struct pcs_sync_ioreq 20 | { 21 | struct cd_list list; 22 | char *buf; 23 | size_t count; 24 | unsigned long long pos; 25 | pcs_fd_t fd; 26 | int flags; 27 | int res; 28 | pcs_err_t error; 29 | void *priv; 30 | pcs_sync_io_cb complete; 31 | }; 32 | 33 | #define PCS_SYNC_IO_READ 1 34 | 35 | #define PCS_SYNC_IO_WRITE 2 /* write completes after FSYNC/FDATASYNC or immedeately if NOSYNCWAIT is set */ 36 | #define PCS_SYNC_IO_NOSYNCWAIT 4 37 | #define PCS_SYNC_IO_FSYNC 8 38 | #define PCS_SYNC_IO_FDATASYNC 16 39 | 40 | #define PCS_SYNC_IO_FLUSH 32 /* flush kernel writeback using sync_file_range, doesn't send device barrier */ 41 | #define PCS_SYNC_IO_TRUNCATE 64 42 | #define PCS_SYNC_IO_FLUSH_ASYNC 128 43 | #define PCS_SYNC_IO_READ_AVAIL 0x10000 /* Read available data and update count accordingly */ 44 | 45 | struct pcs_sync_io 46 | { 47 | struct pcs_process *proc; 48 | struct pcs_event_ioconn *event; 49 | 50 | int shutdown; 51 | int queued; 52 | 53 | pcs_thread_t thr; 54 | 55 | /* Incoming queue */ 56 | struct cd_list in_queue; 57 | pthread_mutex_t in_mutex; 58 | pthread_cond_t in_wake; 59 | int in_waiting; 60 | 61 | struct cd_list fsync_queue; 62 | #ifndef FSYNC_IS_BARRIER 63 | pthread_mutex_t fsync_mutex; 64 | pthread_cond_t fsync_wake; 65 | int fsync_count; 66 | pcs_thread_t fsync_thr; 67 | #endif 68 | 69 | /* Completion queue */ 70 | struct cd_list out_queue; 71 | pthread_mutex_t out_mutex; 72 | }; 73 | 74 | struct pcs_sync_ioreq * pcs_sync_ioreq_alloc(void); 75 | void pcs_sync_ioreq_init(struct pcs_sync_ioreq *); 76 | void pcs_sync_ioreq_free(struct pcs_sync_ioreq *); 77 | 78 | static inline void pcs_sync_ioreq_pread(struct pcs_sync_ioreq *req, pcs_fd_t fd, void *buf, 79 | size_t count, long long offset) 80 | { 81 | req->buf = (char *)buf; 82 | req->count = count; 83 | req->pos = offset; 84 | req->fd = fd; 85 | req->flags = PCS_SYNC_IO_READ; 86 | } 87 | 88 | static inline void pcs_sync_ioreq_pwrite(struct pcs_sync_ioreq *req, pcs_fd_t fd, void const *buf, 89 | size_t count, long long offset, int sync) 90 | { 91 | req->buf = (char *)buf; 92 | req->count = count; 93 | req->pos = offset; 94 | req->fd = fd; 95 | req->flags = PCS_SYNC_IO_WRITE | sync; 96 | } 97 | 98 | static inline void pcs_sync_ioreq_fsync(struct pcs_sync_ioreq *req, pcs_fd_t fd) 99 | { 100 | req->count = 0; 101 | req->fd = fd; 102 | req->flags = PCS_SYNC_IO_WRITE | PCS_SYNC_IO_FSYNC; 103 | } 104 | 105 | static inline void pcs_sync_ioreq_fdatasync(struct pcs_sync_ioreq *req, pcs_fd_t fd) 106 | { 107 | req->count = 0; 108 | req->fd = fd; 109 | req->flags = PCS_SYNC_IO_WRITE | PCS_SYNC_IO_FDATASYNC; 110 | } 111 | 112 | static inline void pcs_sync_ioreq_flush(struct pcs_sync_ioreq *req, pcs_fd_t fd, 113 | size_t count, long long offset) 114 | { 115 | req->count = count; 116 | req->pos = offset; 117 | req->fd = fd; 118 | req->flags = PCS_SYNC_IO_FLUSH; 119 | } 120 | 121 | static inline void pcs_sync_ioreq_async_flush(struct pcs_sync_ioreq *req, pcs_fd_t fd, 122 | size_t count, long long offset) 123 | { 124 | req->count = count; 125 | req->pos = offset; 126 | req->fd = fd; 127 | req->flags = PCS_SYNC_IO_FLUSH_ASYNC; 128 | } 129 | 130 | static inline void pcs_sync_ioreq_truncate(struct pcs_sync_ioreq *req, pcs_fd_t fd, long long offset) 131 | { 132 | req->pos = offset; 133 | req->fd = fd; 134 | req->flags = PCS_SYNC_IO_TRUNCATE; 135 | } 136 | 137 | void pcs_sync_ioreq_submit(struct pcs_sync_io *, struct pcs_sync_ioreq *); 138 | 139 | int pcs_sync_io_start(struct pcs_process * proc, struct pcs_sync_io ** new_io); 140 | void pcs_sync_io_stop(struct pcs_sync_io * io); 141 | 142 | #endif /* _PCS_SYNC_IO_H_ */ 143 | -------------------------------------------------------------------------------- /libpcs_io/pcs_mr_malloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_MR_MALLOC_H__ 6 | #define __PCS_MR_MALLOC_H__ 7 | 8 | #include "pcs_types.h" 9 | #include "pcs_malloc.h" 10 | 11 | /* 12 | * get_chunk method of pcs_msg tells the caller how to 13 | * interpretreturn value by setting *len to -PCS_*_BUF: */ 14 | #define PCS_PLAIN_BUF 0 /* buffer itself */ 15 | #define PCS_SPLICE_BUF 1 /* pcs_splice_buf */ 16 | #define PCS_MR_POOL_BUF 2 /* pcs_mr_buf */ 17 | 18 | /* 19 | * The following structure will be directly exposded to rdma-engine. In the 20 | * other words, rdma-engine will get read/write access to it. 21 | * 22 | * Initially, all its fields are NULL pointers. Handling a buffer first time, 23 | * rdma-engine detects mr_ctx == NULL and proceeds with ibv_reg_mr() 24 | * covering embracing buffer with a MR (Memory Region). The MR is only valid 25 | * for given PD (Protection Domain). Hence, rdma-engine saves in 26 | * of this structure. 27 | * 28 | * Along with mr_ctx/pd_ctx, rdma-engine sets mr_free_cb callback. It's 29 | * needed to release the MR. If we decide to release mr_pool, we'll call 30 | * this callback which, in turns, will call ibv_dereg_mr(). 31 | * 32 | * All subsequent invocation of rdma-engine will observe mr_ctx != NULL. 33 | * So, rdma_engine may use mr_ctx as MR after sanity checking pd_ctx == PD. 34 | */ 35 | struct pcs_mr_ctx { 36 | void *mr_ctx; /* will be set once, by rdma-engine */ 37 | void *pd_ctx; /* same thing as for mr_ctx */ 38 | void (*mr_free_cb)(void *mr_ctx, void *pd_ctx); /* ask rdma-engine to release mr_ctx */ 39 | }; 40 | 41 | /* If get_chunk set copy to -2, rdma-engine must interpret buf as: */ 42 | struct pcs_mr_buf { 43 | char *buf; /* buffer visible to user */ 44 | size_t size; /* its size */ 45 | struct pcs_mr_ctx *ctx; /* points to mr_pool mrc */ 46 | }; 47 | 48 | struct malloc_item; 49 | 50 | void *__pcs_malloc_mmap(struct malloc_item **p_mi, const char *file, int bugon_if_failed, int flags, size_t size); 51 | void __pcs_free_mmap(void *block, size_t size); 52 | 53 | #define pcs_malloc_mmap(size) TRACE_ALLOC(__pcs_malloc_mmap, 0, 0, size) 54 | #define pcs_xmalloc_mmap(size) TRACE_ALLOC(__pcs_malloc_mmap, 1, 0, size) 55 | #define pcs_free_mmap(block, size) __pcs_free_mmap(block, size) 56 | 57 | #ifndef PCS_ENABLE_RDMA 58 | 59 | #define pcs_mr_xmalloc(size) pcs_xmalloc(size) 60 | #define pcs_mr_malloc(size) pcs_malloc(size) 61 | #define pcs_mr_free(block) pcs_free(block) 62 | 63 | #define pcs_mr_malloc_mmap(size) pcs_malloc_mmap(size) 64 | #define pcs_mr_xmalloc_mmap(size) pcs_xmalloc_mmap(size) 65 | #define pcs_mr_free_mmap(block, size) pcs_free_mmap(block, size) 66 | 67 | #else 68 | 69 | enum { 70 | MR_HASH_TYPE_DEFAULT, 71 | MR_HASH_TYPE_MMAP, 72 | MR_HASH_TYPE_MAX 73 | }; 74 | 75 | void *__pcs_mr_malloc(struct malloc_item **p_mi, const char *file, int bugon_if_failed, size_t size, int hash_type); 76 | 77 | #define pcs_mr_malloc(size) TRACE_ALLOC(__pcs_mr_malloc, 0, size, MR_HASH_TYPE_DEFAULT) 78 | #define pcs_mr_xmalloc(size) TRACE_ALLOC(__pcs_mr_malloc, 1, size, MR_HASH_TYPE_DEFAULT) 79 | #define pcs_mr_xmalloc_mmap(size) TRACE_ALLOC(__pcs_mr_malloc, 1, size, MR_HASH_TYPE_MMAP) 80 | 81 | void pcs_mr_free(void *block); 82 | 83 | static inline void pcs_mr_free_mmap(void *block, size_t size) 84 | { 85 | pcs_mr_free(block); 86 | } 87 | 88 | #endif // PCS_ENABLE_RDMA 89 | 90 | /* Dump per-pool memory usage */ 91 | void pcs_mr_memdump(int loglevel); 92 | 93 | /* One who provides get_chunk can use it to set pcs_mr_buf ctx pointer */ 94 | struct pcs_mr_ctx *pcs_mr_get_ctx(void *block); 95 | 96 | /* rdma-engine will use this helper to get args for ibv_reg_mr() */ 97 | void *pcs_mrc2buf(struct pcs_mr_ctx *ctx, size_t *length); 98 | 99 | static inline void unwind_mr_buf(void **buf, int *copy) 100 | { 101 | if (*copy == -PCS_MR_POOL_BUF) { 102 | struct pcs_mr_buf *b = *buf; 103 | *buf = b->buf; 104 | *copy = b->size; 105 | } 106 | } 107 | 108 | #endif /* __PCS_MR_MALLOC_H__ */ 109 | -------------------------------------------------------------------------------- /libpcs_io/pcs_kqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_poll.h" 6 | 7 | #ifdef HAVE_KQUEUE 8 | 9 | #include "pcs_process.h" 10 | #include "pcs_ioconn.h" 11 | #include "pcs_signal.h" 12 | #include "pcs_co_io.h" 13 | #include "pcs_co_locks.h" 14 | #include "bug.h" 15 | #include "log.h" 16 | 17 | #include 18 | #include 19 | 20 | int pcs_poll_ctl(struct pcs_process *proc, struct pcs_ioconn *conn) 21 | { 22 | struct kevent ev[2]; 23 | int nr = 0; 24 | 25 | if ((conn->actual_mask ^ conn->next_mask) & POLLIN) { 26 | ev[nr].ident = conn->fd; 27 | ev[nr].filter = EVFILT_READ; 28 | ev[nr].flags = (conn->next_mask & EPOLLET ? EV_CLEAR : 0) | (conn->next_mask & POLLIN ? EV_ADD : EV_DELETE); 29 | ev[nr].fflags = 0; 30 | ev[nr].data = 0; 31 | ev[nr].udata = conn; 32 | nr++; 33 | } 34 | if ((conn->actual_mask ^ conn->next_mask) & POLLOUT) { 35 | ev[nr].ident = conn->fd; 36 | ev[nr].filter = EVFILT_WRITE; 37 | ev[nr].flags = (conn->next_mask & EPOLLET ? EV_CLEAR : 0) | (conn->next_mask & POLLOUT ? EV_ADD : EV_DELETE); 38 | ev[nr].fflags = 0; 39 | ev[nr].data = 0; 40 | ev[nr].udata = conn; 41 | nr++; 42 | } 43 | return kevent(proc->kqueue, ev, nr, NULL, 0, NULL); 44 | } 45 | 46 | void pcs_poll_wait(struct pcs_evloop *evloop, int timeout) 47 | { 48 | BUG_ON(evloop->nr_events > 0); 49 | 50 | struct timespec ts = {.tv_sec = timeout / 1000, .tv_nsec = (timeout % 1000) * 1000000}; 51 | evloop->nr_events = kevent(evloop->proc->kqueue, NULL, 0, evloop->events, PCS_MAX_EVENTS_NR, &ts); 52 | } 53 | 54 | static void process_ioconn_event(struct kevent *ev) 55 | { 56 | struct pcs_ioconn *conn = ev->udata; 57 | 58 | switch (ev->filter) { 59 | case EVFILT_READ: 60 | conn->data_ready(conn); 61 | if (ev->flags & EV_EOF) 62 | conn->error_report(conn); 63 | pcs_ioconn_schedule(conn); 64 | break; 65 | 66 | case EVFILT_WRITE: 67 | conn->write_space(conn); 68 | if (ev->flags & EV_EOF) 69 | conn->error_report(conn); 70 | pcs_ioconn_schedule(conn); 71 | break; 72 | 73 | case EVFILT_USER: 74 | conn->data_ready(conn); 75 | break; 76 | 77 | case EVFILT_SIGNAL: 78 | pcs_signal_call_handler(conn, ev->ident); 79 | break; 80 | 81 | default: 82 | BUG(); 83 | } 84 | } 85 | 86 | static void process_co_file_event(struct kevent *ev) 87 | { 88 | struct pcs_co_file *file = (struct pcs_co_file *)((ULONG_PTR)ev->udata & ~(ULONG_PTR)1); 89 | 90 | switch (ev->filter) { 91 | case EVFILT_READ: 92 | pcs_co_event_signal(&file->reader.ev); 93 | break; 94 | 95 | case EVFILT_WRITE: 96 | pcs_co_event_signal(&file->writer.ev); 97 | break; 98 | 99 | default: 100 | BUG(); 101 | } 102 | } 103 | 104 | void pcs_poll_process_events(struct pcs_evloop *evloop) 105 | { 106 | int i; 107 | for (i = 0; i < evloop->nr_events; i++) { 108 | struct kevent *ev = &evloop->events[i]; 109 | 110 | if ((ULONG_PTR)ev->udata & 1) 111 | process_co_file_event(ev); 112 | else 113 | process_ioconn_event(ev); 114 | } 115 | 116 | evloop->nr_events = 0; 117 | } 118 | 119 | int pcs_poll_init(struct pcs_process *proc) 120 | { 121 | proc->kqueue = kqueue(); 122 | if (proc->kqueue < 0) 123 | return -errno; 124 | return 0; 125 | } 126 | 127 | void pcs_poll_fini(struct pcs_process *proc) 128 | { 129 | if (proc->kqueue >= 0) 130 | close(proc->kqueue); 131 | proc->kqueue = -1; 132 | } 133 | 134 | void pcs_poll_file_init(struct pcs_co_file *file) 135 | { 136 | } 137 | 138 | void pcs_poll_file_fini(struct pcs_co_file *file) 139 | { 140 | } 141 | 142 | void pcs_poll_file_begin(struct pcs_co_file *file, int mask) 143 | { 144 | struct kevent ev = {.ident = file->fd, .flags = EV_ADD|EV_ONESHOT, .udata = (void *)((ULONG_PTR)file | 1)}; 145 | 146 | switch (mask) { 147 | case POLLIN: 148 | ev.filter = EVFILT_READ; 149 | break; 150 | 151 | case POLLOUT: 152 | ev.filter = EVFILT_WRITE; 153 | break; 154 | 155 | default: 156 | BUG(); 157 | } 158 | 159 | if ((kevent(pcs_current_proc->kqueue, &ev, 1, NULL, 0, NULL))) { 160 | pcs_log_syserror(LOG_ERR, errno, "pcs_poll_file_begin: kevent failed"); 161 | BUG(); 162 | } 163 | } 164 | 165 | #endif /* HAVE_KQUEUE */ 166 | -------------------------------------------------------------------------------- /libpcs_io/pcs_splice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef __PCS_SPLICE_H__ 6 | #define __PCS_SPLICE_H__ 1 7 | 8 | #include "pcs_fd_gc.h" 9 | 10 | #include 11 | 12 | #ifdef __LINUX__ 13 | #if __GLIBC_PREREQ(2, 5) 14 | #define HAS_LINUX_SPLICE 15 | #endif 16 | #endif 17 | 18 | struct pcs_splice_buf; 19 | struct pcs_splice_pool; 20 | 21 | struct pcs_splice_buf * pcs_splice_buf_alloc(struct pcs_splice_pool * pool); 22 | void pcs_splice_bufs_destroy(struct cd_list * bufs); 23 | struct pcs_splice_buf * pcs_splice_buf_clone(struct pcs_splice_buf * b); 24 | struct pcs_splice_buf * pcs_splice_buf_cut(struct pcs_splice_buf * b, int offset, int size); 25 | struct pcs_splice_buf * pcs_splice_buf_split(struct pcs_splice_buf * b, int size); 26 | int pcs_splice_buf_concat(struct pcs_splice_buf * b, struct pcs_splice_buf * b1); 27 | int pcs_splice_buf_drain(struct pcs_splice_buf * b); 28 | void pcs_splice_buf_free(struct pcs_splice_buf * b); 29 | int pcs_splice_buf_recv(struct pcs_splice_buf * b, int fd, int size); 30 | int pcs_splice_buf_recv_packet(struct pcs_splice_buf * b, int fd, int size); 31 | int pcs_splice_buf_send(int fd, struct pcs_splice_buf * b, int size); 32 | int pcs_splice_buf_send_packet(int fd, struct pcs_splice_buf * b); 33 | int pcs_splice_buf_pwrite(int fd, off_t pos, struct pcs_splice_buf * b); 34 | int pcs_splice_buf_pread(struct pcs_splice_buf * b, int fd, off_t pos, int size); 35 | int pcs_splice_buf_getbytes(struct pcs_splice_buf * b, char * buf, int size); 36 | int pcs_splice_buf_peekbytes(struct pcs_splice_buf * b, char * buf, int size, int offset); 37 | int pcs_splice_buf_putbytes(struct pcs_splice_buf * b, char * buf, int size); 38 | int pcs_splice_buf_vm(struct pcs_splice_buf * b, void * addr, int len); 39 | 40 | void pcs_splice_bufs_add(struct cd_list * bufs, struct pcs_splice_buf * sb); 41 | void pcs_splice_bufs_splice(struct cd_list * bufs, struct cd_list * sbufs); 42 | void pcs_splice_bufs_desplice(struct cd_list * bufs); 43 | void pcs_splice_bufs_desplice_mt(struct cd_list * bufs); 44 | void pcs_splice_bufs_range(struct cd_list * bufs, struct cd_list * range, void * p, unsigned int size); 45 | 46 | void pcs_splice_pool_init(struct pcs_process * proc, struct pcs_splice_pool * pool, int enable); 47 | void pcs_splice_pool_fini(struct pcs_splice_pool * pool); 48 | void pcs_splice_pool_disable(struct pcs_splice_pool * pool); 49 | void pcs_splice_pool_permanently_disable(struct pcs_splice_pool * pool); 50 | void pcs_splice_pool_enable(struct pcs_splice_pool * pool); 51 | 52 | #ifdef HAS_LINUX_SPLICE 53 | 54 | #define PCS_SPLICE_FD_LIMIT 4096 55 | 56 | struct pcs_splice_buf 57 | { 58 | struct cd_list list; 59 | struct pcs_splice_pool * pool; 60 | ULONG_PTR tag; 61 | int refcnt; 62 | unsigned int bytes; 63 | int desc; 64 | }; 65 | 66 | struct pcs_splice_pool 67 | { 68 | struct pcs_process * proc; 69 | struct pcs_fd_user fd_user; 70 | 71 | struct cd_list free_list; 72 | int free_count; 73 | int total_count; 74 | 75 | int drain_fd; 76 | int permanently_disabled; 77 | }; 78 | 79 | static inline struct pcs_splice_buf * pcs_splice_buf_get(struct pcs_splice_buf * b) 80 | { 81 | b->refcnt++; 82 | return b; 83 | } 84 | 85 | static inline void pcs_splice_buf_put(struct pcs_splice_buf * b) 86 | { 87 | if (--b->refcnt == 0) 88 | pcs_splice_buf_free(b); 89 | } 90 | 91 | static inline unsigned int pcs_splice_buf_bytes(struct pcs_splice_buf * b) 92 | { 93 | return b->bytes; 94 | } 95 | 96 | static inline int pcs_splice_buf_enabled(struct pcs_splice_pool * pool) 97 | { 98 | return pool->drain_fd >= 0; 99 | } 100 | 101 | #else /* HAS_LINUX_SPLICE */ 102 | 103 | struct pcs_splice_buf 104 | { 105 | struct cd_list list; 106 | ULONG_PTR tag; 107 | }; 108 | 109 | struct pcs_splice_pool 110 | { 111 | }; 112 | 113 | #define PCS_SPLICE_FD_LIMIT 0 114 | 115 | static inline int pcs_splice_buf_enabled(struct pcs_splice_pool * pool) 116 | { 117 | return 0; 118 | } 119 | 120 | struct pcs_splice_buf * pcs_splice_buf_get(struct pcs_splice_buf * b); 121 | void pcs_splice_buf_put(struct pcs_splice_buf * b); 122 | unsigned int pcs_splice_buf_bytes(struct pcs_splice_buf * b); 123 | 124 | #endif /* HAS_LINUX_SPLICE */ 125 | 126 | #endif /* __PCS_SPLICE_H__ */ 127 | -------------------------------------------------------------------------------- /libpcs_io/.gdbinit: -------------------------------------------------------------------------------- 1 | handle SIG35 pass nostop noprint 2 | 3 | set $CO_ZOMBIE=0 4 | set $CO_IDLE=1 5 | set $CO_READY=2 6 | set $CO_RUNNING=3 7 | set $CO_WAITING=4 8 | set $CO_MIGRATED=5 9 | set $CO_BACKTRACE=0x10 10 | 11 | define co_list 12 | if $argc == 0 13 | set $END=&proc.co_list.list 14 | else 15 | set $END=&((struct pcs_process*)$arg0).co_list.list 16 | end 17 | set $CO=(struct pcs_coroutine*)$END.next 18 | while $CO != $END 19 | set $STATE=$CO.state.val & ~$CO_BACKTRACE 20 | if $STATE != $CO_ZOMBIE && $STATE != $CO_IDLE 21 | set $NAME=$CO.name 22 | if $NAME == 0 23 | printf "%p %#x\n", $CO, $STATE 24 | else 25 | printf "%p %#x %s\n", $CO, $STATE, $NAME 26 | end 27 | end 28 | set $CO=(struct pcs_coroutine*)$CO.list.next 29 | end 30 | end 31 | 32 | document co_list 33 | Argument: [ "proc" ] 34 | If argument is absent variable "proc" from current frame is used. 35 | 36 | Print list of coroutines 37 | end 38 | 39 | define co_set 40 | set $CO=(struct pcs_coroutine*)$arg0 41 | set $STATE=$CO.state.val & ~$CO_BACKTRACE 42 | if $STATE == $CO_READY || $STATE == $CO_WAITING 43 | select-frame 0 44 | set $CO_SET_SAVED_RBX=$rbx 45 | set $CO_SET_SAVED_R12=$r12 46 | set $CO_SET_SAVED_R13=$r13 47 | set $CO_SET_SAVED_R14=$r14 48 | set $CO_SET_SAVED_R15=$r15 49 | set $CO_SET_SAVED_RBP=$rbp 50 | set $CO_SET_SAVED_PC=$pc 51 | set $CO_SET_SAVED_SP=$sp 52 | set $UCONTEXT=(void**)$CO.context.sp 53 | set $rbx=$UCONTEXT[0] 54 | set $r12=$UCONTEXT[1] 55 | set $r13=$UCONTEXT[2] 56 | set $r14=$UCONTEXT[3] 57 | set $r15=$UCONTEXT[4] 58 | set $rbp=$UCONTEXT[5] 59 | set $pc=$UCONTEXT[6] 60 | set $sp=$UCONTEXT+7 61 | else 62 | printf "Unable to select coroutine %p (state %#x)\n", $CO, $STATE 63 | end 64 | end 65 | 66 | document co_set 67 | Argument: "coroutine address" 68 | Switch gdb context to coroutine. Return to normal context with co_reset. 69 | Do not use co_set twice, yoy will lose original context 70 | end 71 | 72 | define co_reset 73 | select-frame 0 74 | set $rbx=$CO_SET_SAVED_RBX 75 | set $r12=$CO_SET_SAVED_R12 76 | set $r13=$CO_SET_SAVED_R13 77 | set $r14=$CO_SET_SAVED_R14 78 | set $r15=$CO_SET_SAVED_R15 79 | set $rbp=$CO_SET_SAVED_RBP 80 | set $pc=$CO_SET_SAVED_PC 81 | set $sp=$CO_SET_SAVED_SP 82 | end 83 | 84 | document co_reset 85 | Switch gdb context back to normal after co_reset 86 | end 87 | 88 | define co_bt 89 | if $argc == 0 90 | set $END=&proc.co_list.list 91 | else 92 | set $END=&((struct pcs_process*)$arg0).co_list.list 93 | end 94 | set $CO=(struct pcs_coroutine*)$END.next 95 | select-frame 0 96 | set $CO_SET_SAVED_RBX=$rbx 97 | set $CO_SET_SAVED_R12=$r12 98 | set $CO_SET_SAVED_R13=$r13 99 | set $CO_SET_SAVED_R14=$r14 100 | set $CO_SET_SAVED_R15=$r15 101 | set $CO_SET_SAVED_RBP=$rbp 102 | set $CO_SET_SAVED_PC=$pc 103 | set $CO_SET_SAVED_SP=$sp 104 | while $CO != $END 105 | set $STATE=$CO.state.val & ~$CO_BACKTRACE 106 | if $STATE != $CO_ZOMBIE && $STATE != $CO_IDLE 107 | set $NAME=$CO.name 108 | if $NAME == 0 109 | printf "\n%p %#x\n", $CO, $STATE 110 | else 111 | printf "\n%p %#x %s\n", $CO, $STATE, $NAME 112 | end 113 | if $STATE == $CO_READY || $STATE == $CO_WAITING 114 | set $UCONTEXT=(void**)$CO.context.sp 115 | set $rbx=$UCONTEXT[0] 116 | set $r12=$UCONTEXT[1] 117 | set $r13=$UCONTEXT[2] 118 | set $r14=$UCONTEXT[3] 119 | set $r15=$UCONTEXT[4] 120 | set $rbp=$UCONTEXT[5] 121 | set $pc=$UCONTEXT[6] 122 | set $sp=$UCONTEXT+7 123 | bt 124 | else 125 | printf "Coroutine is running\n" 126 | end 127 | end 128 | set $CO=(struct pcs_coroutine*)$CO.list.next 129 | end 130 | set $rbx=$CO_SET_SAVED_RBX 131 | set $r12=$CO_SET_SAVED_R12 132 | set $r13=$CO_SET_SAVED_R13 133 | set $r14=$CO_SET_SAVED_R14 134 | set $r15=$CO_SET_SAVED_R15 135 | set $rbp=$CO_SET_SAVED_RBP 136 | set $pc=$CO_SET_SAVED_PC 137 | set $sp=$CO_SET_SAVED_SP 138 | end 139 | 140 | document co_bt 141 | Argument: [ "proc" ] 142 | If no argument is given "proc" is taken from current context. 143 | 144 | Dumps backtraces of all coroutines. 145 | end 146 | -------------------------------------------------------------------------------- /libpcs_io/pcs_rdma_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_RDMA_IO_H_ 6 | #define _PCS_RDMA_IO_H_ 1 7 | 8 | #include 9 | 10 | #include "pcs_types.h" 11 | #include "pcs_sock.h" 12 | #include "pcs_process.h" 13 | #include "pcs_error.h" 14 | #include "pcs_net.h" 15 | #include "pcs_rdma_prot.h" 16 | 17 | /* We don't send NOOP until save up RIO_QUEUE_DEPTH/2 outstanding credits, 18 | hence we don't expect more than two NOOP in-flight */ 19 | #define RIO_NOOP_CREDITS 2 20 | 21 | /* per connection */ 22 | #define RIO_N_RXS (RIO_QUEUE_DEPTH * 2 + 2) 23 | /* RIO_QUEUE_DEPTH from us + up to RIO_QUEUE_DEPTH as answers to large RX. */ 24 | #define RIO_N_TXS (RIO_QUEUE_DEPTH * 2 + 2) 25 | 26 | /* per rdma device */ 27 | #define RIO_N_TXS_PER_DEV 2000 28 | 29 | enum { 30 | RIO_STATE_CONNECTING, /* needn't rdma_disconnect (yet) */ 31 | RIO_STATE_ESTABLISHED, /* main "working" state */ 32 | RIO_STATE_DISCONNECTED, /* needn't rdma_disconnect (already) */ 33 | RIO_STATE_ABORTED, /* rio_abort was called at least once */ 34 | }; 35 | 36 | struct pcs_rdmaio 37 | { 38 | /* 39 | * That's not very obvious, we need two poll-able objects: netio.iocomp 40 | * and compc. The former handles DISCONNECT event. The latter (compc) 41 | * handles WQE completion events. */ 42 | struct pcs_netio netio; 43 | struct pcs_ioconn compc; 44 | int refcnt; 45 | 46 | int rio_state; /* see enum above */ 47 | 48 | /* 49 | * Intentionally switch rdma-engine to errored state forcing 50 | * completion of all posted RXs and TXs. Being in this state, we only 51 | * have to count total number of posted RXs and TXs, and, when it 52 | * drops to zero, schedule freeing rio. See rio_destroy() and 53 | * rio_handle_errored() for details */ 54 | int errored; 55 | 56 | int hdr_size; /* minimum allowed payload */ 57 | 58 | /* 59 | * It's easier to have the same queue_depth for both directions. 60 | * rdma_connect gets a value from a tunable and sends it via 61 | * conn_param; rdma_listen sees it in conn request event and 62 | * blindly accepts the value. */ 63 | int queue_depth; 64 | 65 | 66 | struct rio_rx *rx_descs; /* plain array of RX descriptors */ 67 | char *rx_bufs; /* MR-ed area for payload of RXs */ 68 | struct ibv_mr *rx_mr; /* covers rx_bufs */ 69 | struct cd_list pended_rxs; /* list head of pended RX frames */ 70 | 71 | int n_rx_posted; /* # posted RXs */ 72 | int n_tx_posted; /* # posted TXs */ 73 | 74 | int n_peer_credits; /* what we think about peer's n_rx_posted */ 75 | int n_reserved_credits; /* limits # RDMA in flight */ 76 | 77 | int n_os_credits; /* outstanding credits: # RXs we re-post-ed, 78 | * but have not returned to our peer (yet) */ 79 | 80 | int n_th_credits; /* threshold: when to return outstanding 81 | * credits urgently */ 82 | 83 | struct pcs_process *proc; /* need for sio_count ++/-- */ 84 | void *private; /* stash ep between check_accept and nl_accepted */ 85 | 86 | struct pcs_rdma_device *dev; 87 | struct rdma_cm_id *cmid; 88 | struct ibv_comp_channel *cc; 89 | struct ibv_cq *cq; 90 | 91 | struct cd_list write_queue; 92 | int write_queue_len; /* # messages */ 93 | 94 | struct cd_list reserved_queue; /* out of reserved credits */ 95 | int reserved_queue_len; /* # messages */ 96 | 97 | int no_kick; /* do not kick processing write_queue */ 98 | int throttled; /* pcs_rpc asked us to quiesce */ 99 | 100 | struct cd_list active_txs; /* list head of active TX frames: tx->msg->done() 101 | * is postponed until ACK from our peer */ 102 | 103 | u64 xid_generator; /* provides unique (per rio) xids */ 104 | 105 | struct pcs_rdmaio_conn_req conn_req; 106 | }; 107 | 108 | struct pcs_rdmaio_stats { 109 | u64 memory_regs_total; /* total amount of ibv_reg_mr_calls */ 110 | u64 memory_deregs_total; /* total amout of ibv_dereg_mr calls */ 111 | ssize_t memory_registered; /* the amout of registered memory */ 112 | u64 bounce_buf_total; /* total amout of data transfered via bb */ 113 | }; 114 | extern struct pcs_rdmaio_stats pcs_rdmaio_stats; 115 | 116 | #define rio_from_netio(nio) container_of(nio, struct pcs_rdmaio, netio) 117 | #define rio_from_ioconn(conn) container_of(conn, struct pcs_rdmaio, netio.ioconn) 118 | #define rio_from_compc(conn) container_of(conn, struct pcs_rdmaio, compc) 119 | 120 | #endif /* _PCS_RDMA_IO_H_ */ 121 | -------------------------------------------------------------------------------- /Makefile.inc: -------------------------------------------------------------------------------- 1 | CC ?= gcc 2 | LD ?= ld 3 | AR ?= ar 4 | 5 | cc-option = $(shell $(CC) -Werror $(1:-Wno-%=-W%) -c -x c /dev/null -o /dev/null >/dev/null 2>&1 && echo "$(1)") 6 | 7 | UNAME := $(shell uname) 8 | MACHINE := $(shell uname -m) 9 | 10 | libdir.x86_64 = /usr/lib64 11 | libdir.i686 = /usr/lib 12 | 13 | 14 | PCS_PRODUCT_NAME = $(if $(PRODUCT_NAME),$(PRODUCT_NAME),vstorage) 15 | PCS_USER ?= vstorage 16 | PCS_BIN_DIR ?= /usr/bin 17 | PCS_CFG_DIR ?= /etc/$(PCS_PRODUCT_NAME) 18 | PCS_LIB_DIR ?= $(libdir.$(MACHINE)) 19 | PCS_INCLUDE_DIR ?= /usr/include/pcs 20 | PCS_LIBEXEC_DIR ?= /usr/libexec/$(PCS_PRODUCT_NAME) 21 | PCS_SHARE_DIR ?= /usr/share/$(PCS_PRODUCT_NAME) 22 | PCS_VAR_LIB_DIR ?= /var/lib/$(PCS_PRODUCT_NAME) 23 | PCS_VAR_RUN_DIR ?= /var/run/$(PCS_PRODUCT_NAME) 24 | PCS_VAR_LOG_DIR ?= /var/log/$(PCS_PRODUCT_NAME) 25 | PCS_GCOV_DIR ?= /var/lib/$(PCS_PRODUCT_NAME)-gcov 26 | 27 | #PCS_ENABLE_LICENSING ?= 1 28 | PCS_ENABLE_DNS_RESOLVER ?= 1 29 | #PCS_ENABLE_AVAHI_RESOLVER ?= 1 30 | PCS_ENABLE_MDNS_RESOLVER ?= 1 31 | 32 | CFLAGS_USER := $(CFLAGS) 33 | BUILD_CFLAGS := -Wall -Wextra -Werror -Wstrict-prototypes -Wno-sign-compare -Wno-unused-parameter 34 | BUILD_CFLAGS += $(call cc-option,-Wno-type-limits,) 35 | BUILD_CFLAGS += $(call cc-option,-Wno-missing-field-initializers,) 36 | BUILD_CFLAGS += $(call cc-option,-Wno-address-of-packed-member,) 37 | BUILD_CFLAGS += $(call cc-option,-Wno-format-truncation,) 38 | BUILD_CFLAGS += $(call cc-option,-Wno-packed-not-aligned,) 39 | ifneq ($(UNAME),SunOS) 40 | # Solaris requires ssp_noshared library to be provided by toolchain to link executable files compiled with stack protector 41 | # TODO: For reliable detection need try to link executable compiled with given option 42 | BUILD_CFLAGS += $(call cc-option,-fstack-protector-all,) 43 | endif 44 | BUILD_CFLAGS += -fno-strict-aliasing -fno-omit-frame-pointer $(TARGET_ARCH) 45 | BUILD_CFLAGS += $(if $(PCS_TCMALLOC),-fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free,) 46 | BUILD_CFLAGS += $(if $(DEBUG),-DDEBUG -O0 -g,-O2 -g) 47 | BUILD_CFLAGS += -D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 48 | BUILD_CFLAGS += $(if $(PCS_ADDR_SANIT),-fsanitize=address -fno-omit-frame-pointer,) 49 | BUILD_CFLAGS += $(if $(PCS_CODE_COVERAGE),-fprofile-arcs -ftest-coverage,) 50 | BUILD_CFLAGS += $(if $(PCS_PRODUCT_NAME),-D__PCS_PRODUCT_NAME=$(PCS_PRODUCT_NAME),) 51 | BUILD_CFLAGS += $(if $(VENDOR_NAME),-D__PCS_VENDOR_NAME=$(VENDOR_NAME),) 52 | BUILD_CFLAGS += $(if $(PRODUCT_NAME_LONG),-D__PCS_PRODUCT_NAME_LONG=$(PRODUCT_NAME_LONG),) 53 | BUILD_CFLAGS += $(if $(PCS_USER),-D__PCS_USER=$(PCS_USER),-D__PCS_USER=$(PCS_PRODUCT_NAME)) 54 | BUILD_CFLAGS += $(if $(PCS_BUILD_VERSION),-D__PCS_BUILD_VERSION=$(PCS_BUILD_VERSION),) 55 | BUILD_CFLAGS += $(if $(PCS_CFG_DIR),-D__PCS_CFG_DIR=$(PCS_CFG_DIR),) 56 | BUILD_CFLAGS += $(if $(PCS_LIBEXEC_DIR),-D__PCS_LIBEXEC_DIR=$(PCS_LIBEXEC_DIR),) 57 | 58 | BUILD_CFLAGS += $(if $(PCS_ENABLE_DNS_RESOLVER),-D_ENABLE_DNS_RESOLVER=1) 59 | BUILD_CFLAGS += $(if $(PCS_ENABLE_AVAHI_RESOLVER),-D_ENABLE_AVAHI_RESOLVER=1) 60 | BUILD_CFLAGS += $(if $(PCS_ENABLE_MDNS_RESOLVER),-D_ENABLE_MDNS_RESOLVER=1) 61 | 62 | BUILD_CFLAGS += $(if $(USE_VALGRIND),-DUSE_VALGRIND=1) 63 | BUILD_CFLAGS += $(if $(COROUTINE_ABORT_ON_MUTEX_TIMEOUT),-DCOROUTINE_ABORT_ON_MUTEX_TIMEOUT=1) 64 | 65 | BUILD_CFLAGS += $(if $(PCS_ENABLE_RDMA), -DPCS_ENABLE_RDMA,) 66 | 67 | BUILD_CFLAGS += $(CFLAGS_USER) 68 | 69 | LDFLAGS += -g -rdynamic $(TARGET_ARCH) 70 | LDFLAGS += $(if $(PCS_CODE_COVERAGE),-fprofile-arcs,) 71 | LDFLAGS += $(if $(PCS_ADDR_SANIT),-fsanitize=address,) 72 | ifeq ($(UNAME),Darwin) 73 | LDENV="DYLD_LIBRARY_PATH" 74 | LDFLAGS += -Wl,-dead_strip_dylibs -undefined error 75 | SONAME_FLAG = -install_name 76 | BUILD_CFLAGS += -Wno-deprecated-declarations $(shell pkg-config --cflags openssl) 77 | LDFLAGS += $(shell pkg-config --libs-only-L openssl) 78 | else ifeq ($(UNAME),SunOS) 79 | LDENV="LD_LIBRARY_PATH" 80 | BUILD_CFLAGS += -D_POSIX_PTHREAD_SEMANTICS -D_XOPEN_SOURCE=600L -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ 81 | LDLIBS += -lsocket 82 | else 83 | LDENV="LD_LIBRARY_PATH" 84 | LDFLAGS += -Wl,--as-needed -Wl,--no-undefined -Wl,--allow-shlib-undefined 85 | endif 86 | 87 | CFLAGS += $(BUILD_CFLAGS) 88 | 89 | SONAME_FLAG ?= -soname 90 | LDLIBS += -lpthread -ldl 91 | 92 | VZKERNEL_VERSION ?=$(shell uname -r) 93 | VZKERNEL_HEADERS ?= /lib/modules/$(VZKERNEL_VERSION)/build/include 94 | SONAME_MJ_VER ?= 1 95 | SONAME_MN_VER ?= 0.0 96 | SONAME_FLAGS=-Wl,$(SONAME_FLAG),$@.$(SONAME_MJ_VER) 97 | LIB_VER=$(SONAME_MJ_VER).$(SONAME_MN_VER) 98 | 99 | # Avoid double rebuild of tools and tests 100 | %: %.c 101 | .PRECIOUS: %.o 102 | -------------------------------------------------------------------------------- /libpcs_io/pcs_winapi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_winapi.h" 6 | 7 | #ifdef __WINDOWS__ 8 | #include "log.h" 9 | #include "pcs_thread.h" 10 | #include "pcs_sock.h" 11 | 12 | PNtQueryInformationFile NtQueryInformationFilePtr; 13 | PNtQueryVolumeInformationFile NtQueryVolumeInformationFilePtr; 14 | PRtlNtStatusToDosError RtlNtStatusToDosErrorPtr; 15 | PNtQueryDirectoryFile NtQueryDirectoryFilePtr; 16 | 17 | PGetQueuedCompletionStatusEx GetQueuedCompletionStatusExPtr; 18 | PCancelIoEx CancelIoExPtr; 19 | PSetFileCompletionNotificationModes SetFileCompletionNotificationModesPtr; 20 | 21 | PGetSystemTimePreciseAsFileTime GetSystemTimePreciseAsFileTimePtr; 22 | PQueryUnbiasedInterruptTime QueryUnbiasedInterruptTimePtr; 23 | PSetThreadDescription SetThreadDescriptionPtr; 24 | 25 | Ptc_malloc tc_mallocPtr; 26 | Ptc_realloc tc_reallocPtr; 27 | Ptc_free tc_freePtr; 28 | 29 | // Copy-paste from VersionHelpers.h Win8.1 SDK 30 | // (GetVersion* functions are deprecated starting with Win8.1 - see https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getversion) 31 | FORCEINLINE BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) 32 | { 33 | OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, { 0 }, 0, 0 }; 34 | DWORDLONG const dwlConditionMask = VerSetConditionMask( 35 | VerSetConditionMask( 36 | VerSetConditionMask( 37 | 0, VER_MAJORVERSION, VER_GREATER_EQUAL), 38 | VER_MINORVERSION, VER_GREATER_EQUAL), 39 | VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); 40 | 41 | osvi.dwMajorVersion = wMajorVersion; 42 | osvi.dwMinorVersion = wMinorVersion; 43 | osvi.wServicePackMajor = wServicePackMajor; 44 | 45 | return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; 46 | } 47 | 48 | FORCEINLINE BOOL IsWindowsVistaOrGreater() 49 | { 50 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0); 51 | } 52 | 53 | int pcs_winapi_init(void) 54 | { 55 | static int rc = 1; 56 | if (rc <= 0) 57 | return rc; 58 | 59 | HMODULE ntdll_module = GetModuleHandleW(L"ntdll.dll"); 60 | BUG_ON(!ntdll_module); 61 | 62 | #define LOAD(module, sym) do { \ 63 | sym ## Ptr = (P ## sym)GetProcAddress(module, #sym); \ 64 | if (sym ## Ptr == NULL) { \ 65 | rc = -(int)GetLastError(); \ 66 | pcs_log(LOG_ERR, "failed to get proc address of %s", #sym); \ 67 | return rc; \ 68 | } \ 69 | } while (0) 70 | 71 | #define LOAD_OPTIONAL(module, sym) do { \ 72 | sym ## Ptr = (P ## sym)GetProcAddress(module, #sym); \ 73 | } while (0) 74 | 75 | LOAD(ntdll_module, RtlNtStatusToDosError); 76 | LOAD(ntdll_module, NtQueryInformationFile); 77 | LOAD(ntdll_module, NtQueryVolumeInformationFile); 78 | LOAD(ntdll_module, NtQueryDirectoryFile); 79 | 80 | HMODULE kern_module = GetModuleHandleW(L"kernel32.dll"); 81 | BUG_ON(!kern_module); 82 | 83 | LOAD_OPTIONAL(kern_module, GetQueuedCompletionStatusEx); 84 | LOAD_OPTIONAL(kern_module, SetFileCompletionNotificationModes); 85 | LOAD_OPTIONAL(kern_module, CancelIoEx); 86 | 87 | LOAD_OPTIONAL(kern_module, GetSystemTimePreciseAsFileTime); 88 | LOAD_OPTIONAL(kern_module, QueryUnbiasedInterruptTime); 89 | LOAD_OPTIONAL(kern_module, SetThreadDescription); 90 | 91 | tc_mallocPtr = &malloc; 92 | tc_reallocPtr = &realloc; 93 | tc_freePtr = &free; 94 | 95 | #ifdef _USE_TCMALLOC 96 | // tcmalloc library uses static TLS that is not supported along with dynamic library loading in Win XP/2003 97 | if (IsWindowsVistaOrGreater()) { 98 | HMODULE tcmalloc_module = LoadLibraryExW(L"tcmalloc.dll", 0, LOAD_WITH_ALTERED_SEARCH_PATH); 99 | if (tcmalloc_module) { 100 | LOAD(tcmalloc_module, tc_malloc); 101 | LOAD(tcmalloc_module, tc_realloc); 102 | LOAD(tcmalloc_module, tc_free); 103 | } 104 | } 105 | #endif 106 | 107 | rc = 0; 108 | return rc; 109 | } 110 | 111 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 112 | { 113 | switch (fdwReason) { 114 | case DLL_PROCESS_ATTACH: { 115 | int rc = pcs_winapi_init(); 116 | BUG_ON(rc); 117 | #ifndef HAVE_TLS_STATIC 118 | pcs_process_tls_alloc(); 119 | #endif 120 | break; 121 | } 122 | case DLL_PROCESS_DETACH: 123 | #ifndef HAVE_TLS_STATIC 124 | pcs_process_tls_free(); 125 | #endif 126 | break; 127 | case DLL_THREAD_ATTACH: 128 | break; 129 | case DLL_THREAD_DETACH: 130 | #ifndef HAVE_TLS_STATIC 131 | pcs_thread_tls_free(); 132 | #endif 133 | break; 134 | } 135 | return TRUE; 136 | } 137 | 138 | #else 139 | int _pcs_winapi_make_ranlib_has_no_symbols_happy = 1; 140 | #endif /* __WINDOWS__ */ 141 | -------------------------------------------------------------------------------- /libpcs_io/pcs_watchdog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "pcs_types.h" 14 | #include "pcs_process.h" 15 | #include "pcs_watchdog.h" 16 | #include "pcs_malloc.h" 17 | #include "pcs_thread.h" 18 | #include "log.h" 19 | 20 | #ifdef PCS_USE_WATCHDOG 21 | 22 | __no_sanitize_thread static u32 get_poll_count(struct pcs_evloop* evloop) 23 | { 24 | return evloop->poll_count; 25 | } 26 | 27 | static void dump_kstack(struct pcs_evloop* evloop) 28 | { 29 | struct pcs_watchdog *wd = evloop->wd; 30 | char buf[256]; 31 | FILE * stack_fp; 32 | abs_time_t now = get_abs_time_ms(); 33 | void * last_pc[1]; 34 | 35 | wd->wd_inactive_total += now - wd->wd_last_activity - wd->wd_accounted; 36 | wd->wd_accounted = now - wd->wd_last_activity; 37 | 38 | snprintf(buf, sizeof(buf), "/proc/%lu/stack", evloop->thr_id); 39 | stack_fp = fopen(buf, "re"); 40 | if (stack_fp == NULL) 41 | pcs_log(LOG_ERR, "pcs watchdog failed to open %s: err=%d", buf, errno); 42 | 43 | pcs_log(LOG_ERR, "pcs evloop #%d is inactive for %u msecs (%u)", 44 | evloop->id, 45 | (unsigned)(now - wd->wd_last_activity), 46 | (unsigned)(get_abs_time_ms() - now)); 47 | 48 | last_pc[0] = pcs_profiler_last_pc(evloop); 49 | if (last_pc[0]) { 50 | char ** s = backtrace_symbols(last_pc, sizeof(last_pc)); 51 | pcs_log(LOG_ERR, "Last PC: %s", s[0]); 52 | pcs_native_free(s); 53 | } 54 | 55 | if (stack_fp) { 56 | while (fgets(buf, sizeof(buf) - 1, stack_fp)) { 57 | buf[sizeof(buf) - 1] = 0; 58 | pcs_log(LOG_ERR, "%s", buf); 59 | } 60 | if (ferror(stack_fp)) 61 | pcs_log(LOG_ERR, "Stack is unavailable: err=%d", errno); 62 | fclose(stack_fp); 63 | } 64 | 65 | stack_fp = fopen("/proc/meminfo", "re"); 66 | if (stack_fp == NULL) 67 | return; 68 | 69 | while (fgets(buf, sizeof(buf) - 1, stack_fp)) { 70 | buf[sizeof(buf) - 1] = 0; 71 | pcs_log(LOG_ERR, "%s", buf); 72 | } 73 | fclose(stack_fp); 74 | 75 | stack_fp = fopen("/proc/vz/latency", "re"); 76 | if (stack_fp == NULL) 77 | return; 78 | 79 | while (fgets(buf, sizeof(buf) - 1, stack_fp)) { 80 | buf[sizeof(buf) - 1] = 0; 81 | pcs_log(LOG_ERR, "%s", buf); 82 | } 83 | fclose(stack_fp); 84 | 85 | stack_fp = popen("ps axv", "re"); 86 | if (stack_fp == NULL) 87 | return; 88 | 89 | while (fgets(buf, sizeof(buf) - 1, stack_fp)) { 90 | buf[sizeof(buf) - 1] = 0; 91 | pcs_log(LOG_ERR, "%s", buf); 92 | } 93 | pclose(stack_fp); 94 | } 95 | 96 | static void do_monitor(struct pcs_evloop * evloop) 97 | { 98 | struct pcs_watchdog *wd = evloop->wd; 99 | u32 poll_count = get_poll_count(evloop); 100 | 101 | if (poll_count != wd->wd_poll_checked || (poll_count & 1)) { 102 | wd->wd_poll_checked = poll_count; 103 | wd->wd_last_activity = get_abs_time_ms(); 104 | wd->wd_accounted = 0; 105 | return; 106 | } 107 | 108 | dump_kstack(evloop); 109 | } 110 | 111 | static pcs_thread_ret_t watchdog_thread(void * arg) 112 | { 113 | struct pcs_process *proc = arg; 114 | struct pcs_watchdog *wd = proc->evloops[0].wd; 115 | 116 | pcs_thread_setname("watchdog"); 117 | 118 | pthread_mutex_lock(&wd->wd_mutex); 119 | abs_time_t last = get_abs_time_ms(); 120 | while (wd->wd_run) { 121 | u32 i; 122 | for (i = 0; i < proc->nr_evloops; i++) 123 | do_monitor(&proc->evloops[i]); 124 | pcs_thread_cond_timedwait(&wd->wd_wake, &wd->wd_mutex, 1000); 125 | abs_time_t now = get_abs_time_ms(); 126 | abs_time_t elapsed = get_elapsed_time(now, last); 127 | if (elapsed >= 2000) 128 | pcs_log(LOG_ERR, "monitor process '%s' executed %llu ms", proc->name, (llu)elapsed); 129 | last = now; 130 | } 131 | pthread_mutex_unlock(&wd->wd_mutex); 132 | return 0; 133 | } 134 | 135 | void pcs_watchdog_init_evloop(struct pcs_evloop *evloop) 136 | { 137 | struct pcs_watchdog *wd = pcs_xzmalloc(sizeof(*wd)); 138 | wd->wd_last_activity = get_abs_time_ms(); 139 | wd->wd_poll_checked = ~0; 140 | evloop->wd = wd; 141 | } 142 | 143 | void pcs_watchdog_start(struct pcs_process *proc) 144 | { 145 | struct pcs_watchdog *wd = proc->evloops[0].wd; 146 | if (!wd) 147 | return; 148 | 149 | pthread_mutex_init(&wd->wd_mutex, NULL); 150 | pthread_cond_init(&wd->wd_wake, NULL); 151 | wd->wd_run = 1; 152 | 153 | if (pcs_thread_create(&wd->wd_thr, NULL, watchdog_thread, proc)) 154 | BUG(); 155 | } 156 | 157 | void pcs_watchdog_stop(struct pcs_process *proc) 158 | { 159 | struct pcs_watchdog *wd = proc->evloops[0].wd; 160 | if (!wd) 161 | return; 162 | 163 | pthread_mutex_lock(&wd->wd_mutex); 164 | wd->wd_run = 0; 165 | pthread_cond_signal(&wd->wd_wake); 166 | pthread_mutex_unlock(&wd->wd_mutex); 167 | 168 | pcs_thread_join(wd->wd_thr); 169 | 170 | u32 i; 171 | for (i = 0; i < proc->nr_evloops; i++) { 172 | pcs_free(proc->evloops[i].wd); 173 | proc->evloops[i].wd = NULL; 174 | } 175 | } 176 | 177 | #endif /* PCS_USE_WATCHDOG */ 178 | -------------------------------------------------------------------------------- /libpcs_io/pcs_random.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_random.h" 6 | #include "pcs_sync_io.h" 7 | #include "log.h" 8 | #include "timer.h" 9 | 10 | #ifndef __WINDOWS__ 11 | #include 12 | #else 13 | #define SystemFunction036 NTAPI SystemFunction036 14 | #include 15 | #undef SystemFunction036 16 | #endif 17 | 18 | /* Fill buffer with pseudo random content. Returns 0 on success and -1 otherwise */ 19 | int pcs_get_urandom(void *buf, int sz) 20 | { 21 | #ifndef __WINDOWS__ 22 | pcs_fd_t fd; 23 | int ret = pcs_sync_open("/dev/urandom", O_RDONLY, 0, &fd); 24 | if (ret) { 25 | pcs_log_syserror(LOG_ERR, ret, "Unable open /dev/urandom"); 26 | return -1; 27 | } 28 | ret = pcs_sync_sread(fd, buf, sz); 29 | pcs_sync_close(fd); 30 | if (ret < 0) { 31 | pcs_log_syserror(LOG_ERR, ret, "Can't read from /dev/urandom"); 32 | return -1; 33 | } 34 | if (ret != sz) { 35 | pcs_log(LOG_ERR, "Truncated read from /dev/urandom"); 36 | return -1; 37 | } 38 | #else 39 | if (!RtlGenRandom(buf, sz)) { 40 | pcs_log(LOG_ERR, "RtlGenRandom failed"); 41 | return -1; 42 | } 43 | #endif 44 | return 0; 45 | } 46 | 47 | void pcs_srandomdev(struct pcs_rng *rng) 48 | { 49 | if (!pcs_get_urandom(rng->data, sizeof(rng->data))) { 50 | rng->pos = ~0U; 51 | return; 52 | } 53 | 54 | u64 seed = get_real_time_us(); 55 | #ifndef __WINDOWS__ 56 | seed += getpid() + getppid(); 57 | #else 58 | seed += GetCurrentProcessId(); 59 | #endif 60 | pcs_srandom(rng, seed); 61 | } 62 | 63 | u64 pcs_rand_range(struct pcs_rng *rng, u64 min, u64 max) 64 | { 65 | BUG_ON(max < min); 66 | 67 | u64 rnd = pcs_random(rng); 68 | u64 range = max - min; 69 | if ((range & (range + 1)) == 0) 70 | rnd &= range; 71 | else 72 | rnd %= range + 1; 73 | return min + rnd; 74 | } 75 | 76 | /* 77 | This is a 64-bit version of Mersenne Twister pseudorandom number 78 | generator. 79 | 80 | Copyright (C) 2004, Makoto Matsumoto and Takuji Nishimura, 81 | All rights reserved. 82 | 83 | Redistribution and use in source and binary forms, with or without 84 | modification, are permitted provided that the following conditions 85 | are met: 86 | 87 | 1. Redistributions of source code must retain the above copyright 88 | notice, this list of conditions and the following disclaimer. 89 | 90 | 2. Redistributions in binary form must reproduce the above copyright 91 | notice, this list of conditions and the following disclaimer in the 92 | documentation and/or other materials provided with the distribution. 93 | 94 | 3. The names of its contributors may not be used to endorse or promote 95 | products derived from this software without specific prior written 96 | permission. 97 | 98 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 99 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 100 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 101 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 102 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 103 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 104 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 105 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 106 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 107 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 108 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 109 | */ 110 | 111 | #define NN 312 112 | #define MM 156 113 | #define MATRIX_A 0xB5026F5AA96619E9ULL 114 | #define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */ 115 | #define LM 0x7FFFFFFFULL /* Least significant 31 bits */ 116 | 117 | u64 pcs_random(struct pcs_rng *rng) 118 | { 119 | unsigned i; 120 | u64 x; 121 | 122 | if (unlikely(rng->pos >= NN)) { 123 | /* generate NN words at one time */ 124 | for (i = 0; i < NN - MM; i++) { 125 | x = (rng->data[i] & UM) | (rng->data[i + 1] & LM); 126 | rng->data[i] = rng->data[i + MM] ^ (x >> 1) ^ (x & 1 ? MATRIX_A : 0); 127 | } 128 | for (i = NN - MM; i < NN - 1; i++) { 129 | x = (rng->data[i] & UM) | (rng->data[i + 1] & LM); 130 | rng->data[i] = rng->data[i + MM - NN] ^ (x >> 1) ^ (x & 1 ? MATRIX_A : 0); 131 | } 132 | x = (rng->data[NN - 1] & UM) | (rng->data[0] & LM); 133 | rng->data[NN - 1] = rng->data[MM - 1] ^ (x >> 1) ^ (x & 1 ? MATRIX_A : 0); 134 | rng->pos = 0; 135 | } 136 | 137 | x = rng->data[rng->pos++]; 138 | x ^= (x >> 29) & 0x5555555555555555ULL; 139 | x ^= (x << 17) & 0x71D67FFFEDA60000ULL; 140 | x ^= (x << 37) & 0xFFF7EEE000000000ULL; 141 | x ^= (x >> 43); 142 | return x; 143 | } 144 | 145 | void pcs_srandom(struct pcs_rng *rng, u64 seed) 146 | { 147 | unsigned i; 148 | 149 | rng->data[0] = seed; 150 | for (i = 1; i < NN; i++) 151 | rng->data[i] = 6364136223846793005ULL * (rng->data[i - 1] ^ (rng->data[i - 1] >> 62)) + i; 152 | rng->pos = NN; 153 | } 154 | -------------------------------------------------------------------------------- /libpcs_io/pcs_event_ioconn.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_types.h" 6 | #include "pcs_event_ioconn.h" 7 | #include "pcs_poll.h" 8 | #include "pcs_malloc.h" 9 | #include "bug.h" 10 | 11 | #if !defined(__WINDOWS__) 12 | 13 | #include "pcs_eventfd.h" 14 | 15 | #include 16 | #include 17 | 18 | static void event_data_ready(struct pcs_ioconn *conn) 19 | { 20 | struct pcs_event_ioconn *event = container_of(conn, struct pcs_event_ioconn, ioconn); 21 | 22 | #if defined(HAVE_EVENTFD) 23 | u64 efd_buf; 24 | int res = read(conn->fd, &efd_buf, 8); 25 | (void)res; 26 | #elif defined(HAVE_KQUEUE) 27 | /* nothing to do */ 28 | #elif defined(__SUN__) 29 | /* nothing to do */ 30 | #else /* pipe */ 31 | char buf[128]; 32 | while (read(conn->fd, buf, sizeof(buf)) == sizeof(buf)) 33 | /* */; 34 | #endif 35 | 36 | event->data_ready(event->priv); 37 | } 38 | 39 | int pcs_event_ioconn_init(struct pcs_process *proc, struct pcs_event_ioconn **event_p, void (*data_ready)(void *priv), void *priv) 40 | { 41 | struct pcs_event_ioconn *event = pcs_xmalloc(sizeof(*event)); 42 | event->data_ready = data_ready; 43 | event->priv = priv; 44 | event->send_event_fd = -1; 45 | 46 | struct pcs_ioconn * conn = &event->ioconn; 47 | pcs_ioconn_init(proc, conn); 48 | conn->next_mask = POLLIN | EPOLLET; 49 | conn->data_ready = event_data_ready; 50 | 51 | #if defined(HAVE_EVENTFD) 52 | int fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); 53 | if (fd < 0) { 54 | int err = -errno; 55 | pcs_free(event); 56 | return err; 57 | } 58 | 59 | conn->fd = event->send_event_fd = fd; 60 | pcs_ioconn_register(conn); 61 | #elif defined(HAVE_KQUEUE) 62 | struct kevent ev = {.ident = (uintptr_t)event, .filter = EVFILT_USER, .flags = EV_ADD | EV_CLEAR, .udata = &event->ioconn}; 63 | if (kevent(proc->kqueue, &ev, 1, NULL, 0, NULL)) { 64 | int err = -errno; 65 | pcs_free(event); 66 | return err; 67 | } 68 | #elif defined(__SUN__) 69 | /* nothing to do */ 70 | #else /* pipe */ 71 | int fds[2]; 72 | if (pipe(fds) < 0) { 73 | int err = -errno; 74 | pcs_free(event); 75 | return err; 76 | } 77 | 78 | fcntl(fds[0], F_SETFD, FD_CLOEXEC); 79 | fcntl(fds[0], F_SETFD, FD_CLOEXEC); 80 | fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL, 0) | O_NONBLOCK); 81 | fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL, 0) | O_NONBLOCK); 82 | conn->fd = fds[0]; 83 | event->send_event_fd = fds[1]; 84 | pcs_ioconn_register(conn); 85 | #endif 86 | 87 | *event_p = event; 88 | return 0; 89 | } 90 | 91 | void pcs_event_ioconn_wakeup(struct pcs_event_ioconn *event) 92 | { 93 | #if defined(HAVE_EVENTFD) 94 | u64 ev = 1; 95 | int res = write(event->send_event_fd, &ev, 8); 96 | BUG_ON(res < 0 && errno != EAGAIN); 97 | #elif defined(HAVE_KQUEUE) 98 | struct kevent ev = {.ident = (uintptr_t)event, .filter = EVFILT_USER, .fflags = NOTE_TRIGGER, .udata = &event->ioconn}; 99 | int res = kevent(event->ioconn.proc->kqueue, &ev, 1, NULL, 0, NULL); 100 | BUG_ON(res < 0); 101 | #elif defined(__SUN__) 102 | int res = port_send(event->ioconn.proc->port, 0, &event->ioconn); 103 | BUG_ON(res < 0); 104 | #else /* pipe */ 105 | unsigned char ev = 0; 106 | /* write() can fail with EAGAIN. We do not care, pipe is not empty, it is enough */ 107 | int res = write(event->send_event_fd, &ev, 1); 108 | BUG_ON(res < 0 && errno != EAGAIN); 109 | #endif 110 | } 111 | 112 | void pcs_event_ioconn_close(struct pcs_event_ioconn *event) 113 | { 114 | #if defined(HAVE_EVENTFD) 115 | pcs_ioconn_unregister(&event->ioconn); 116 | #elif defined(HAVE_KQUEUE) 117 | struct kevent ev = {.ident = (uintptr_t)event, .filter = EVFILT_USER, .flags = EV_DELETE, .udata = &event->ioconn}; 118 | kevent(event->ioconn.proc->kqueue, &ev, 1, NULL, 0, NULL); 119 | pcs_free(event); 120 | #elif defined(__SUN__) 121 | pcs_free(event); 122 | #else /* pipe */ 123 | /* close one end of pipe, another end will be closed by ioconn destructor */ 124 | if (event->send_event_fd >= 0) { 125 | close(event->send_event_fd); 126 | event->send_event_fd = -1; 127 | } 128 | pcs_ioconn_unregister(&event->ioconn); 129 | #endif 130 | } 131 | 132 | #else /* __WINDOWS__ */ 133 | 134 | static void event_data_ready(struct pcs_iocp *iocp) 135 | { 136 | struct pcs_event_ioconn *event = container_of(iocp, struct pcs_event_ioconn, iocp); 137 | event->data_ready(event->priv); 138 | } 139 | 140 | int pcs_event_ioconn_init(struct pcs_process *proc, struct pcs_event_ioconn **event_p, void (*data_ready)(void *priv), void *priv) 141 | { 142 | struct pcs_event_ioconn *event = pcs_xmalloc(sizeof(*event)); 143 | event->proc = proc; 144 | event->data_ready = data_ready; 145 | event->priv = priv; 146 | event->iocp.done = event_data_ready; 147 | memset(&event->iocp.overlapped, 0, sizeof(event->iocp.overlapped)); 148 | *event_p = event; 149 | return 0; 150 | } 151 | 152 | void pcs_event_ioconn_wakeup(struct pcs_event_ioconn *event) 153 | { 154 | pcs_iocp_send(event->proc, &event->iocp); 155 | } 156 | 157 | void pcs_event_ioconn_close(struct pcs_event_ioconn *event) 158 | { 159 | pcs_free(event); 160 | } 161 | 162 | #endif /* __WINDOWS__ */ 163 | -------------------------------------------------------------------------------- /libpcs_io/pcs_sock_conn.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_types.h" 6 | #include "pcs_sock_conn.h" 7 | #include "pcs_sock_io.h" 8 | #include "pcs_errno.h" 9 | #include "pcs_poll.h" 10 | #include "pcs_malloc.h" 11 | #include "log.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #ifndef SOCK_CLOEXEC 21 | #define SOCK_CLOEXEC 0 22 | #endif 23 | 24 | #define sockconnect_from_ioconn(conn) container_of(conn, struct pcs_sockconnect, netconn.ioconn) 25 | 26 | static struct pcs_netconnect_tops netconn_tops; 27 | 28 | /* Socket connect work. Just do asynchronous connect, nothing else */ 29 | static void sockconn_write_space(struct pcs_ioconn * conn) 30 | { 31 | struct pcs_sockconnect * sh = sockconnect_from_ioconn(conn); 32 | if (connect(conn->fd, sh->sa, sh->sa_len)) { 33 | int err = pcs_sock_errno(); 34 | if (err == EALREADY) 35 | return; 36 | 37 | /* EISCONN == successfully connected */ 38 | if (err != EISCONN) { 39 | sh->error = err ? err : EIO; 40 | TRACE("Connect failed, errno=%d", err); 41 | } 42 | } 43 | sh->complete(sh); 44 | } 45 | 46 | static void sockconn_error_report(struct pcs_ioconn * conn) 47 | { 48 | struct pcs_sockconnect * sh = sockconnect_from_ioconn(conn); 49 | int error; 50 | 51 | socklen_t so_len = sizeof(error); 52 | if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, (char*)&error, &so_len)) 53 | error = EINVAL; 54 | 55 | TRACE("Connect failed, errno=%d", error); 56 | 57 | sh->error = error; 58 | sh->complete(sh); 59 | } 60 | 61 | void pcs_sockconnect_abort(struct pcs_sockconnect * conn, int error) 62 | { 63 | conn->error = error; 64 | conn->complete(conn); 65 | } 66 | 67 | void pcs_sockconnect_destroy(struct pcs_ioconn * conn) 68 | { 69 | conn->proc->conn_count--; 70 | pcs_ioconn_destruct(conn); 71 | } 72 | 73 | struct pcs_sockconnect * 74 | pcs_sockconnect_init(struct pcs_process * proc, PCS_NET_ADDR_T * addr) 75 | { 76 | struct pcs_sockconnect *sh; 77 | struct sockaddr *sa = NULL; 78 | int len; 79 | 80 | if (pcs_netaddr2sockaddr(addr, &sa, &len)) 81 | return NULL; 82 | 83 | sh = pcs_sockconnect_init_sa(proc, sa, len); 84 | pcs_free(sa); 85 | return sh; 86 | } 87 | 88 | static void sc_complete(struct pcs_sockconnect *sh) 89 | { 90 | struct pcs_netconnect *nc = &sh->netconn; 91 | struct pcs_process *proc = nc->cops->get_proc(nc); 92 | struct pcs_sockio * sio; 93 | 94 | if (nc->cops->handle_errors(nc, sh->error)) 95 | return; 96 | 97 | sio = pcs_sockio_fdinit(proc, nc->ioconn.fd, 98 | nc->alloc_size, nc->hdr_size); 99 | 100 | if (sio == NULL) { 101 | nc->cops->sched_reconnect(nc, PCS_ERR_NET); 102 | return; 103 | } 104 | 105 | nc->cops->nc_complete(nc, &sio->netio); 106 | } 107 | 108 | struct pcs_sockconnect * 109 | pcs_sockconnect_init_sa(struct pcs_process * proc, struct sockaddr *sa, int len) 110 | { 111 | struct pcs_sockconnect *sh; 112 | 113 | sh = pcs_malloc(sizeof(struct pcs_sockconnect) + len); 114 | if (!sh) 115 | return NULL; 116 | 117 | proc->conn_count++; 118 | 119 | sh->error = 0; 120 | sh->sa_len = len; 121 | memcpy(&sh->sa, sa, len); 122 | 123 | pcs_ioconn_init(proc, &sh->netconn.ioconn); 124 | 125 | sh->netconn.ioconn.destruct = pcs_sockconnect_destroy; 126 | 127 | /* methods */ 128 | sh->netconn.tops = &netconn_tops; 129 | 130 | /* callbacks: those who use sh directly (bypassing pcs_rpc) will override */ 131 | sh->complete = sc_complete; 132 | 133 | return sh; 134 | } 135 | 136 | void pcs_sockconnect_start(struct pcs_process * proc, struct pcs_sockconnect * sh) 137 | { 138 | int fd; 139 | int err; 140 | 141 | while (1) { 142 | fd = socket(sh->sa->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0); 143 | if (!pcs_sock_invalid(fd)) 144 | break; 145 | 146 | err = pcs_sock_errno(); 147 | if (err == EMFILE || err == ENFILE) { 148 | if (pcs_fd_gc(proc)) 149 | continue; 150 | } 151 | goto done; 152 | } 153 | 154 | pcs_sock_nonblock(fd); 155 | 156 | err = connect(fd, sh->sa, sh->sa_len); 157 | if (err) 158 | err = pcs_sock_errno(); 159 | 160 | if (err == 0 || err == EINPROGRESS) 161 | { 162 | struct pcs_ioconn *conn = &sh->netconn.ioconn; 163 | conn->fd = fd; 164 | conn->next_mask = POLLOUT; 165 | conn->write_space = sockconn_write_space; 166 | conn->error_report = sockconn_error_report; 167 | pcs_ioconn_register(conn); 168 | return; 169 | } 170 | 171 | pcs_sock_close(fd); 172 | done: 173 | sh->error = err ? err : EIO; 174 | sh->complete(sh); 175 | } 176 | 177 | static void sc_abort_connect(struct pcs_netconnect * conn, int error) 178 | { 179 | pcs_sockconnect_abort(sockconn_from_netconn(conn), error); 180 | } 181 | 182 | /* netconnect transport operations */ 183 | 184 | static int sc_getmyname(struct pcs_netconnect *netconn, PCS_NET_ADDR_T * addr) 185 | { 186 | return pcs_sock_getsockname(netconn->ioconn.fd, addr); 187 | } 188 | 189 | static void sc_connect_start(struct pcs_netconnect *nc) 190 | { 191 | struct pcs_process *proc = nc->cops->get_proc(nc); 192 | struct pcs_sockconnect *sh = sockconn_from_netconn(nc); 193 | 194 | pcs_sockconnect_start(proc, sh); 195 | } 196 | 197 | static struct pcs_netconnect_tops netconn_tops = { 198 | .abort_connect = sc_abort_connect, 199 | .getmyname = sc_getmyname, 200 | .connect_start = sc_connect_start, 201 | }; 202 | -------------------------------------------------------------------------------- /libpcs_io/user.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #include "pcs_types.h" 6 | #include "pcs_malloc.h" 7 | #include "pcs_compat.h" 8 | 9 | #ifndef __WINDOWS__ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #endif 17 | 18 | #ifdef HAVE_LINUX_CAPS 19 | #include 20 | #include 21 | #endif 22 | 23 | #include "log.h" 24 | #include "user.h" 25 | 26 | #ifndef __WINDOWS__ 27 | int is_dir_user_valid(const char *dir) 28 | { 29 | uid_t uid; 30 | struct stat st; 31 | 32 | uid = geteuid(); 33 | if (stat(dir, &st) < 0) { 34 | pcs_log(LOG_ERR, "Can't stat dir '%s': %s", dir, strerror(errno)); 35 | return 0; 36 | } 37 | 38 | if (!S_ISDIR(st.st_mode)) { 39 | pcs_log(LOG_ERR, "Path %s isn't directory", dir); 40 | return 0; 41 | } 42 | 43 | if (uid != st.st_uid) { 44 | pcs_log(LOG_ERR, "Directory %s was created for a different user", dir); 45 | return 0; 46 | } 47 | return 1; 48 | } 49 | 50 | int is_root(void) 51 | { 52 | return (geteuid() == 0); 53 | } 54 | 55 | struct passwd *get_user(const char *user) 56 | { 57 | struct passwd *pw; 58 | 59 | errno = 0; 60 | pw = getpwnam(user); 61 | if (!pw) { 62 | if (errno == 0) 63 | pcs_log(LOG_ERR, "User '%s' not found", user); 64 | else 65 | pcs_log(LOG_ERR, "Failed to get user '%s' info: %s", 66 | user, strerror(errno)); 67 | return NULL; 68 | } 69 | 70 | return pw; 71 | } 72 | 73 | struct group *get_group(const char *group) 74 | { 75 | struct group *gr; 76 | 77 | errno = 0; 78 | gr = getgrnam(group); 79 | if (!gr) { 80 | if (errno == 0) 81 | pcs_log(LOG_ERR, "Group '%s' not found", group); 82 | else 83 | pcs_log(LOG_ERR, "Failed to get group '%s' info: %s", 84 | group, strerror(errno)); 85 | return NULL; 86 | } 87 | 88 | return gr; 89 | } 90 | 91 | int set_user_if_root(const char *user, const char *group) 92 | { 93 | struct passwd *pw; 94 | gid_t gid; 95 | 96 | pw = get_user(user); 97 | if (!pw) 98 | return -1; 99 | 100 | if (group) { 101 | struct group *gr = get_group(group); 102 | if (!gr) 103 | return -1; 104 | gid = gr->gr_gid; 105 | } else 106 | gid = pw->pw_gid; 107 | 108 | #if defined(__MAC__) 109 | /* on mac, getgrouplist() takes int[], and setgroups() takes gid_t[] */ 110 | BUILD_BUG_ON(sizeof(int) != sizeof(gid_t)); 111 | #define GETGROUPLIST_GID_T int 112 | #else 113 | #define GETGROUPLIST_GID_T gid_t 114 | #endif 115 | gid_t groups[64]; 116 | int ngroups = sizeof(groups) / sizeof(groups[0]); 117 | if (getgrouplist(user, gid, (GETGROUPLIST_GID_T *)groups, &ngroups) < 0) { 118 | pcs_log(LOG_ERR, "getgrouplist failed: %s", strerror(errno)); 119 | return -1; 120 | } 121 | 122 | /* don't try to switch if we can't */ 123 | if (!is_root()) { 124 | pcs_log(LOG_WARN, "Only root can switch users"); 125 | return 0; 126 | } 127 | 128 | #ifdef HAVE_LINUX_CAPS 129 | prctl(PR_SET_KEEPCAPS, 1); 130 | #endif 131 | 132 | if (setgroups(ngroups, groups) < 0) { 133 | pcs_log(LOG_ERR, "setgroups failed: %s", strerror(errno)); 134 | return -1; 135 | } 136 | 137 | if (setgid(gid) < 0) { 138 | pcs_log(LOG_ERR, "setgid failed: %s", strerror(errno)); 139 | return -1; 140 | } 141 | 142 | if (setuid(pw->pw_uid) < 0) { 143 | pcs_log(LOG_ERR, "setuid failed: %s", strerror(errno)); 144 | return -1; 145 | } 146 | 147 | #ifdef HAVE_LINUX_CAPS 148 | struct __user_cap_header_struct caph; 149 | struct __user_cap_data_struct capv[2]; 150 | 151 | caph.version = _LINUX_CAPABILITY_VERSION_3; 152 | caph.pid = getpid(); 153 | memset(capv, 0, sizeof(capv)); 154 | capv[0].effective = capv[0].permitted = capv[0].inheritable = (1 << CAP_NET_ADMIN); 155 | if (capset(&caph, capv)) 156 | pcs_log(LOG_ERR, "capset : %d", errno); 157 | 158 | if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) 159 | pcs_log(LOG_WARN, "Can't enable core dump - %s", 160 | strerror(errno)); 161 | #endif 162 | return 0; 163 | } 164 | 165 | #else /* __WINDOWS__ */ 166 | 167 | #include 168 | #include 169 | 170 | int is_dir_user_valid(const char *dir) { return 0; } 171 | 172 | int is_root(void) 173 | { 174 | /* available since Windows XP */ 175 | return IsUserAnAdmin() == TRUE; 176 | } 177 | 178 | struct passwd *get_user(const char *user) { return NULL; } 179 | struct group *get_group(const char *group) { return NULL; } 180 | 181 | /* It possible to try keeping the same behavior as linux version, using LogonUser + ImpersonateLoggedOnUser API. 182 | But Windows security policy allows logon only (without hacking) with correct domain name and password. 183 | Example: https://code.msdn.microsoft.com/windowsapps/CppImpersonateUser-a0fbfd54 */ 184 | int set_user_if_root(const char *user, const char *group) 185 | { 186 | #if 0 187 | if (!is_root()) 188 | return -1; 189 | #endif 190 | 191 | WCHAR * w_user = pcs_utf8_to_utf16(user, -1); 192 | if (!w_user) 193 | return -(int)GetLastError(); 194 | 195 | int r = -1; 196 | WCHAR w_curr_user[UNLEN + 1]; 197 | /* FIXME: use _countof() for _MSC_VER >= 1400 (Visual Studio 2005) */ 198 | DWORD len = UNLEN + 1; 199 | 200 | if (GetUserNameW(w_curr_user, &len)) { 201 | if (!_wcsnicmp(w_user, w_curr_user, len)) { 202 | /* success returns only if the current user is a required */ 203 | r = 0; 204 | } 205 | } 206 | else 207 | r = -(int)GetLastError(); 208 | 209 | pcs_free(w_user); 210 | 211 | return r; 212 | } 213 | 214 | #endif 215 | -------------------------------------------------------------------------------- /libpcs_io/pcs_sock_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2003-2018 Acronis International GmbH. 3 | */ 4 | 5 | #ifndef _PCS_SOCK_IO_H_ 6 | #define _PCS_SOCK_IO_H_ 1 7 | 8 | #include "pcs_types.h" 9 | #include "pcs_sock.h" 10 | #include "pcs_process.h" 11 | #include "pcs_error.h" 12 | #include "pcs_net.h" 13 | #include "bug.h" 14 | 15 | #define PCS_MSG_MAX_CALENDAR 64 16 | #define PCS_SIO_TIMEOUT (60*1000) 17 | 18 | #define PCS_SIO_PREEMPT_LIMIT 16 19 | 20 | struct pcs_msg 21 | { 22 | struct cd_list list; 23 | 24 | pcs_error_t error; 25 | abs_time_t start_time; 26 | 27 | void *private; 28 | void *private2; /* Huh? Need to do something else here. */ 29 | struct pcs_msg *response; /* Consider removing. It can be done passing the second 30 | * argument to done(); 31 | */ 32 | struct pcs_netio *netio; 33 | struct pcs_rpc *rpc; 34 | 35 | int size; 36 | int _iocount; 37 | unsigned short timeout; 38 | unsigned char kill_slot; 39 | unsigned char stage; 40 | abs_time_t io_start_time; 41 | 42 | struct cd_hlist_node kill_link; 43 | 44 | void * (*get_chunk)(struct pcs_msg *, int offset, int *len); 45 | 46 | void (*done)(struct pcs_msg *); 47 | void (*destructor)(struct pcs_msg *); 48 | void *pool; 49 | 50 | int accounted; 51 | 52 | short _align_offset; 53 | short _inline_len; 54 | 55 | char _inline_buffer[0]; 56 | }; 57 | 58 | static inline void * pcs_msg_aligned_data(struct pcs_msg * msg, int offset) 59 | { 60 | return (void*)((char *)msg + msg->_align_offset + offset); 61 | } 62 | 63 | enum 64 | { 65 | PCS_MSG_STAGE_NONE = 0, /* Initial state */ 66 | PCS_MSG_STAGE_UNSENT = 1, /* Message queued somewhere before send */ 67 | PCS_MSG_STAGE_SEND = 2, /* Message queued on socket queue */ 68 | PCS_MSG_STAGE_SENT = 3, /* Message is sent */ 69 | PCS_MSG_STAGE_WAIT = 4, /* Message is waiting for respnose */ 70 | PCS_MSG_STAGE_DONE = 5, /* Response received */ 71 | }; 72 | 73 | enum 74 | { 75 | PCS_SOCK_F_THROTTLE = 1, 76 | PCS_SOCK_F_CORK = 2, 77 | PCS_SOCK_F_DYNAMIC_SIZE = 4, 78 | PCS_SOCK_F_EOF = 8, 79 | }; 80 | 81 | struct pcs_sockio 82 | { 83 | struct pcs_netio netio; 84 | u32 msg_count; 85 | 86 | struct cd_list write_queue; 87 | int write_queue_len; 88 | 89 | pcs_error_t error; 90 | 91 | struct pcs_timer write_timer; 92 | int send_timeout; 93 | 94 | int hdr_ptr; 95 | int hdr_max; 96 | unsigned int flags; 97 | u32 retrans; 98 | 99 | struct pcs_msg *current_msg; 100 | int read_offset; 101 | int write_offset; 102 | 103 | struct pcs_splice_buf *splice_wbuf; 104 | struct pcs_splice_buf *splice_rbuf; 105 | 106 | void (*write_wakeup)(struct pcs_sockio *); 107 | 108 | char _inline_buffer[0]; 109 | }; 110 | 111 | #define sio_from_netio(nio) container_of(nio, struct pcs_sockio, netio) 112 | #define sio_from_ioconn(conn) container_of(conn, struct pcs_sockio, netio.ioconn) 113 | static inline pcs_sock_t sio_sock(struct pcs_sockio * sio) { return sio->netio.ioconn.fd; } 114 | 115 | struct pcs_sockio * pcs_sockio_fdinit(struct pcs_process * proc, pcs_sock_t fd, int alloc_max, int hdr_max); 116 | void pcs_sockio_start(struct pcs_sockio * sio); 117 | void pcs_sock_sendmsg(struct pcs_sockio * sio, struct pcs_msg *msg); 118 | int pcs_sock_cancel_msg(struct pcs_msg * msg); 119 | int pcs_sock_queuelen(struct pcs_sockio * sio); 120 | void pcs_sock_abort(struct pcs_sockio * sio); 121 | void pcs_sock_error(struct pcs_sockio * sio, int error); 122 | 123 | void pcs_sock_throttle(struct pcs_sockio * sio); 124 | void pcs_sock_unthrottle(struct pcs_sockio * sio); 125 | 126 | struct pcs_msg * pcs_alloc_input_msg(struct pcs_sockio * sio, int datalen); 127 | struct pcs_msg * pcs_alloc_output_msg(int datalen); 128 | struct pcs_msg * pcs_clone_msg(struct pcs_msg * msg); 129 | struct pcs_msg * pcs_cow_msg(struct pcs_msg * msg, int data_len); 130 | void pcs_clone_done(struct pcs_msg * msg); 131 | void pcs_free_msg(struct pcs_msg * msg); 132 | void * pcs_get_chunk_inline(struct pcs_msg * msg, int offset, int *len); 133 | void pcs_msg_sent(struct pcs_msg * msg); 134 | 135 | static inline void * msg_inline_head(struct pcs_msg * msg) 136 | { 137 | int len; 138 | 139 | return msg->get_chunk(msg, 0, &len); 140 | } 141 | 142 | static inline void * sio_inline_buffer(struct pcs_sockio * sio) 143 | { 144 | return sio->_inline_buffer; 145 | } 146 | 147 | static inline void pcs_msg_io_init(struct pcs_msg * msg) 148 | { 149 | pcs_clear_error(&msg->error); 150 | msg->_iocount = 0; 151 | msg->done = pcs_free_msg; 152 | } 153 | 154 | static inline void pcs_msg_io_start(struct pcs_msg * msg, void (*done)(struct pcs_msg *)) 155 | { 156 | BUG_ON(msg->_iocount != 0); 157 | msg->_iocount = 1; 158 | msg->done = done; 159 | } 160 | 161 | static inline struct pcs_msg * pcs_msg_io_sched(struct pcs_msg * msg) 162 | { 163 | BUG_ON(msg->_iocount <= 0); 164 | msg->_iocount++; 165 | return msg; 166 | } 167 | 168 | static inline void pcs_msg_io_end(struct pcs_msg * msg) 169 | { 170 | BUG_ON(msg->_iocount <= 0); 171 | if (--msg->_iocount == 0) 172 | msg->done(msg); 173 | } 174 | 175 | static inline void pcs_msg_io_fini(struct pcs_msg * msg) 176 | { 177 | BUG_ON(msg->_iocount != 0); 178 | } 179 | 180 | 181 | struct bufqueue; 182 | 183 | /** 184 | Present a portion of @bq as a pcs_msg that may be passed to pcs_sock_sendmsg(). 185 | Reading data from the pcs_msg will drain @bq. 186 | 187 | \param @bq the buffer queue with the data of a message 188 | \param @size the length of the head of @bq that will be presented as a pcs_msg 189 | \returns a pcs_msg that reads data from @bq 190 | */ 191 | struct pcs_msg* bufqueue_as_pcs_output_msg(struct bufqueue *bq, u32 size); 192 | 193 | #endif /* _PCS_SOCK_IO_H_ */ 194 | --------------------------------------------------------------------------------