--- libeio/eio.c 2015/06/25 17:05:07 1.136 +++ libeio/eio.c 2018/06/15 02:57:36 1.150 @@ -1,7 +1,7 @@ /* * libeio implementation * - * Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann + * Copyright (c) 2007,2008,2009,2010,2011,2012,2013,2016,2017 Marc Alexander Lehmann * All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- @@ -44,11 +44,6 @@ #include "eio.h" #include "ecb.h" -#ifdef EIO_STACKSIZE -# define X_STACKSIZE EIO_STACKSIZE -#endif -#include "xthread.h" - #include #include #include @@ -124,6 +119,8 @@ #define dup2(fd1,fd2) _dup2 (fd1, fd2) #define pipe(fds) _pipe (fds, 4096, O_BINARY) + #define fcntl(fd,cmd,arg) EIO_ENOSYS () + #define ioctl(fd,cmd,arg) EIO_ENOSYS () #define fchmod(fd,mode) EIO_ENOSYS () #define chown(path,uid,gid) EIO_ENOSYS () #define fchown(fd,uid,gid) EIO_ENOSYS () @@ -206,11 +203,15 @@ symlink (const char *old, const char *neu) { #if WINVER >= 0x0600 - if (CreateSymbolicLink (neu, old, 1)) - return 0; + int flags; - if (CreateSymbolicLink (neu, old, 0)) - return 0; + /* This tries out all combinations of SYMBOLIC_LINK_FLAG_DIRECTORY + * and SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, + * with directory first. + */ + for (flags = 3; flags >= 0; --flags) + if (CreateSymbolicLink (neu, old, flags)) + return 0; #endif return EIO_ERRNO (ENOENT, -1); @@ -232,6 +233,7 @@ #else + #include #include #include #include @@ -285,10 +287,6 @@ # include #endif -#if HAVE_SYS_PRCTL_H -# include -#endif - #if HAVE_SENDFILE # if __linux # include @@ -304,6 +302,11 @@ # endif #endif +#if HAVE_RENAMEAT2 +# include +# include +#endif + #ifndef D_TYPE # define D_TYPE(de) 0 #endif @@ -321,9 +324,19 @@ /* used for readlink etc. */ #ifndef PATH_MAX -# define PATH_MAX 4096 +# define PATH_MAX 0 #endif +#ifndef O_CLOEXEC + #define O_CLOEXEC 0 +#endif + +#ifndef EIO_PATH_MIN +# define EIO_PATH_MIN 8160 +#endif + +#define EIO_PATH_MAX (PATH_MAX <= EIO_PATH_MIN ? EIO_PATH_MIN : PATH_MAX) + /* buffer size for various temporary buffers */ #define EIO_BUFSIZE 65536 @@ -368,8 +381,14 @@ #define ETP_TYPE_QUIT -1 #define ETP_TYPE_GROUP EIO_GROUP -struct etp_worker; +static void eio_nop_callback (void) { } +static void (*eio_want_poll_cb)(void) = eio_nop_callback; +static void (*eio_done_poll_cb)(void) = eio_nop_callback; + +#define ETP_WANT_POLL(pool) eio_want_poll_cb () +#define ETP_DONE_POLL(pool) eio_done_poll_cb () +struct etp_worker; #define ETP_REQ eio_req #define ETP_DESTROY(req) eio_destroy (req) static int eio_finish (eio_req *req); @@ -379,6 +398,9 @@ #include "etp.c" +static struct etp_pool eio_pool; +#define EIO_POOL (&eio_pool) + /*****************************************************************************/ static void @@ -454,89 +476,104 @@ void eio_grp_cancel (eio_req *grp) { - etp_grp_cancel (grp); + etp_grp_cancel (EIO_POOL, grp); } void eio_cancel (eio_req *req) { - etp_cancel (req); + etp_cancel (EIO_POOL, req); } void eio_submit (eio_req *req) { - etp_submit (req); + etp_submit (EIO_POOL, req); } unsigned int eio_nreqs (void) { - return etp_nreqs (); + return etp_nreqs (EIO_POOL); } unsigned int eio_nready (void) { - return etp_nready (); + return etp_nready (EIO_POOL); } unsigned int eio_npending (void) { - return etp_npending (); + return etp_npending (EIO_POOL); } unsigned int ecb_cold eio_nthreads (void) { - return etp_nthreads (); + return etp_nthreads (EIO_POOL); } void ecb_cold eio_set_max_poll_time (double nseconds) { - etp_set_max_poll_time (nseconds); + etp_set_max_poll_time (EIO_POOL, nseconds); } void ecb_cold eio_set_max_poll_reqs (unsigned int maxreqs) { - etp_set_max_poll_reqs (maxreqs); + etp_set_max_poll_reqs (EIO_POOL, maxreqs); } void ecb_cold eio_set_max_idle (unsigned int nthreads) { - etp_set_max_idle (nthreads); + etp_set_max_idle (EIO_POOL, nthreads); } void ecb_cold eio_set_idle_timeout (unsigned int seconds) { - etp_set_idle_timeout (seconds); + etp_set_idle_timeout (EIO_POOL, seconds); } void ecb_cold eio_set_min_parallel (unsigned int nthreads) { - etp_set_min_parallel (nthreads); + etp_set_min_parallel (EIO_POOL, nthreads); } void ecb_cold eio_set_max_parallel (unsigned int nthreads) { - etp_set_max_parallel (nthreads); + etp_set_max_parallel (EIO_POOL, nthreads); } int eio_poll (void) { - return etp_poll (); + return etp_poll (EIO_POOL); } /*****************************************************************************/ /* work around various missing functions */ +#if HAVE_POSIX_CLOSE && !__linux +# define eio__close(fd) posix_close (fd, 0) +#else +# define eio__close(fd) close (fd) +#endif + +/* close() without disturbing errno */ +static void +silent_close (int fd) +{ + int saved_errno = errno; + eio__close (fd); + errno = saved_errno; +} + #ifndef HAVE_UTIMES # undef utimes @@ -924,9 +961,9 @@ if (addr < end) if (flags & EIO_MT_MODIFY) /* modify */ - do { *((volatile sig_atomic_t *)addr) |= 0; } while ((addr += page) < len && !EIO_CANCELLED (req)); + do { *((volatile sig_atomic_t *)addr) |= 0; } while ((addr += page) < end && !EIO_CANCELLED (req)); else - do { *((volatile sig_atomic_t *)addr) ; } while ((addr += page) < len && !EIO_CANCELLED (req)); + do { *((volatile sig_atomic_t *)addr) ; } while ((addr += page) < end && !EIO_CANCELLED (req)); } return 0; @@ -970,15 +1007,15 @@ if (!*rel) return -1; - res = etp_tmpbuf_get (tmpbuf, PATH_MAX * 3); + res = etp_tmpbuf_get (tmpbuf, EIO_PATH_MAX * 3); #ifdef _WIN32 if (_access (rel, 4) != 0) return -1; - symlinks = GetFullPathName (rel, PATH_MAX * 3, res, 0); + symlinks = GetFullPathName (rel, EIO_PATH_MAX * 3, res, 0); errno = ENAMETOOLONG; - if (symlinks >= PATH_MAX * 3) + if (symlinks >= EIO_PATH_MAX * 3) return -1; errno = EIO; @@ -988,8 +1025,8 @@ return symlinks; #else - tmp1 = res + PATH_MAX; - tmp2 = tmp1 + PATH_MAX; + tmp1 = res + EIO_PATH_MAX; + tmp2 = tmp1 + EIO_PATH_MAX; #if 0 /* disabled, the musl way to do things is just too racy */ #if __linux && defined(O_NONBLOCK) && defined(O_NOATIME) @@ -1000,9 +1037,9 @@ if (fd >= 0) { sprintf (tmp1, "/proc/self/fd/%d", fd); - req->result = readlink (tmp1, res, PATH_MAX); + req->result = readlink (tmp1, res, EIO_PATH_MAX); /* here we should probably stat the open file and the disk file, to make sure they still match */ - close (fd); + eio__close (fd); if (req->result > 0) goto done; @@ -1023,7 +1060,7 @@ if (wd == EIO_CWD) { - if (!getcwd (res, PATH_MAX)) + if (!getcwd (res, EIO_PATH_MAX)) return -1; len = strlen (res); @@ -1080,7 +1117,7 @@ res [len + 1] = 0; /* now check if it's a symlink */ - linklen = readlink (tmpbuf->ptr, tmp1, PATH_MAX); + linklen = readlink (tmpbuf->ptr, tmp1, EIO_PATH_MAX); if (linklen < 0) { @@ -1096,7 +1133,7 @@ int rellen = strlen (rel); errno = ENAMETOOLONG; - if (linklen + 1 + rellen >= PATH_MAX) + if (linklen + 1 + rellen >= EIO_PATH_MAX) /* also catch linklen >= EIO_PATH_MAX */ return -1; errno = ELOOP; @@ -1370,7 +1407,7 @@ dirp = fdopendir (fd); if (!dirp) - close (fd); + silent_close (fd); } else dirp = opendir (req->ptr1); @@ -1659,7 +1696,7 @@ if (wd != EIO_INVALID_WD && wd != EIO_CWD) { #if HAVE_AT - close (wd->fd); + eio__close (wd->fd); #endif free (wd); } @@ -1667,6 +1704,19 @@ #if HAVE_AT +static int +eio__renameat2 (int olddirfd, const char *oldpath, int newdirfd, const char *newpath, unsigned int flags) +{ +#if HAVE_RENAMEAT2 + return syscall (SYS_renameat2, olddirfd, oldpath, newdirfd, newpath, flags); +#else + if (flags) + return EIO_ENOSYS (); + + return renameat (olddirfd, oldpath, newdirfd, newpath); +#endif +} + /* they forgot these */ static int @@ -1679,7 +1729,7 @@ return fd; res = ftruncate (fd, length); - close (fd); + silent_close (fd); return res; } @@ -1693,9 +1743,8 @@ return fd; res = fstatvfs (fd, buf); - close (fd); + silent_close (fd); return res; - } #endif @@ -1705,24 +1754,55 @@ #define ALLOC(len) \ if (!req->ptr2) \ { \ - X_LOCK (wrklock); \ + X_LOCK (EIO_POOL->wrklock); \ req->flags |= EIO_FLAG_PTR2_FREE; \ - X_UNLOCK (wrklock); \ + X_UNLOCK (EIO_POOL->wrklock); \ req->ptr2 = malloc (len); \ if (!req->ptr2) \ { \ errno = ENOMEM; \ req->result = -1; \ - break; \ + goto alloc_fail; \ } \ } /*****************************************************************************/ +static void +eio__slurp (int fd, eio_req *req) +{ + req->result = fd; + + if (fd < 0) + return; + + if (req->offs < 0 || !req->size) /* do we need the size? */ + { + off_t size = lseek (fd, 0, SEEK_END); + + if (req->offs < 0) + req->offs += size; + + if (!req->size) + req->size = size - req->offs; + } + + ALLOC (req->size); + req->result = pread (fd, req->ptr2, req->size, req->offs); + + silent_close (fd); + +alloc_fail: + ; +} + int ecb_cold eio_init (void (*want_poll)(void), void (*done_poll)(void)) { - return etp_init (want_poll, done_poll); + eio_want_poll_cb = want_poll; + eio_done_poll_cb = done_poll; + + return etp_init (EIO_POOL, 0, 0, 0); } ecb_inline void @@ -1806,6 +1886,9 @@ ? pwrite (req->int1, req->ptr2, req->size, req->offs) : write (req->int1, req->ptr2, req->size); break; + case EIO_FCNTL: req->result = fcntl (req->int1, (int) req->int2, req->ptr2); break; + case EIO_IOCTL: req->result = ioctl (req->int1, (unsigned long)req->int2, req->ptr2); break; + case EIO_READAHEAD: req->result = readahead (req->int1, req->offs, req->size); break; case EIO_SENDFILE: req->result = eio__sendfile (req->int1, req->int2, req->offs, req->size); break; @@ -1819,6 +1902,7 @@ case EIO_CHMOD: req->result = fchmodat (dirfd, req->ptr1, (mode_t)req->int2, 0); break; case EIO_TRUNCATE: req->result = eio__truncateat (dirfd, req->ptr1, req->offs); break; case EIO_OPEN: req->result = openat (dirfd, req->ptr1, req->int1, (mode_t)req->int2); break; + case EIO_SLURP: eio__slurp ( openat (dirfd, req->ptr1, O_RDONLY | O_CLOEXEC), req); break; case EIO_UNLINK: req->result = unlinkat (dirfd, req->ptr1, 0); break; case EIO_RMDIR: /* complications arise because "." cannot be removed, so we might have to expand */ @@ -1826,17 +1910,28 @@ ? rmdir (req->wd->str) : unlinkat (dirfd, req->ptr1, AT_REMOVEDIR); break; case EIO_MKDIR: req->result = mkdirat (dirfd, req->ptr1, (mode_t)req->int2); break; - case EIO_RENAME: /* complications arise because "." cannot be renamed, so we might have to expand */ - req->result = req->wd && SINGLEDOT (req->ptr1) - ? rename (req->wd->str, req->ptr2) - : renameat (dirfd, req->ptr1, WD2FD ((eio_wd)req->int3), req->ptr2); break; + case EIO_RENAME: req->result = eio__renameat2 ( + dirfd, + /* complications arise because "." cannot be renamed, so we might have to expand */ + req->wd && SINGLEDOT (req->ptr1) ? req->wd->str : req->ptr1, + WD2FD ((eio_wd)req->int3), + req->ptr2, + req->int2 + ); + break; case EIO_LINK: req->result = linkat (dirfd, req->ptr1, WD2FD ((eio_wd)req->int3), req->ptr2, 0); break; case EIO_SYMLINK: req->result = symlinkat (req->ptr1, dirfd, req->ptr2); break; case EIO_MKNOD: req->result = mknodat (dirfd, req->ptr1, (mode_t)req->int2, (dev_t)req->offs); break; - case EIO_READLINK: ALLOC (PATH_MAX); - req->result = readlinkat (dirfd, req->ptr1, req->ptr2, PATH_MAX); break; case EIO_STATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS)); req->result = eio__statvfsat (dirfd, req->ptr1, (EIO_STRUCT_STATVFS *)req->ptr2); break; + case EIO_READLINK: ALLOC (EIO_PATH_MAX); + req->result = readlinkat (dirfd, req->ptr1, req->ptr2, EIO_PATH_MAX); + if (req->result == EIO_PATH_MAX) + { + req->result = -1; + errno = ENAMETOOLONG; + } + break; case EIO_UTIME: case EIO_FUTIME: { @@ -1871,18 +1966,25 @@ case EIO_CHMOD: req->result = chmod (path , (mode_t)req->int2); break; case EIO_TRUNCATE: req->result = truncate (path , req->offs); break; case EIO_OPEN: req->result = open (path , req->int1, (mode_t)req->int2); break; + case EIO_SLURP: eio__slurp ( open (path , O_RDONLY | O_CLOEXEC), req); break; case EIO_UNLINK: req->result = unlink (path ); break; case EIO_RMDIR: req->result = rmdir (path ); break; case EIO_MKDIR: req->result = mkdir (path , (mode_t)req->int2); break; - case EIO_RENAME: req->result = rename (path , req->ptr2); break; + case EIO_RENAME: req->result = req->int2 ? EIO_ENOSYS () : rename (path, req->ptr2); break; case EIO_LINK: req->result = link (path , req->ptr2); break; case EIO_SYMLINK: req->result = symlink (path , req->ptr2); break; case EIO_MKNOD: req->result = mknod (path , (mode_t)req->int2, (dev_t)req->offs); break; - case EIO_READLINK: ALLOC (PATH_MAX); - req->result = readlink (path, req->ptr2, PATH_MAX); break; case EIO_STATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS)); req->result = statvfs (path , (EIO_STRUCT_STATVFS *)req->ptr2); break; + case EIO_READLINK: ALLOC (EIO_PATH_MAX); + req->result = readlink (path, req->ptr2, EIO_PATH_MAX); + if (req->result == EIO_PATH_MAX) + { + req->result = -1; + errno = ENAMETOOLONG; + } + break; case EIO_UTIME: case EIO_FUTIME: @@ -1927,7 +2029,7 @@ case EIO_FCHMOD: req->result = fchmod (req->int1, (mode_t)req->int2); break; case EIO_FTRUNCATE: req->result = ftruncate (req->int1, req->offs); break; - case EIO_CLOSE: req->result = close (req->int1); break; + case EIO_CLOSE: req->result = eio__close (req->int1); break; case EIO_DUP2: req->result = dup2 (req->int1, req->int2); break; case EIO_SYNC: req->result = 0; sync (); break; case EIO_FSYNC: req->result = fsync (req->int1); break; @@ -1975,6 +2077,7 @@ break; } +alloc_fail: req->errorno = errno; } @@ -2075,6 +2178,16 @@ REQ (EIO_WRITE); req->int1 = fd; req->offs = offset; req->size = length; req->ptr2 = buf; SEND; } +eio_req *eio_fcntl (int fd, int cmd, void *arg, int pri, eio_cb cb, void *data) +{ + REQ (EIO_IOCTL); req->int1 = fd; req->int2 = cmd; req->ptr2 = arg; SEND; +} + +eio_req *eio_ioctl (int fd, unsigned long request, void *buf, int pri, eio_cb cb, void *data) +{ + REQ (EIO_IOCTL); req->int1 = fd; req->int2 = request; req->ptr2 = buf; SEND; +} + eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data) { REQ (EIO_FSTAT); req->int1 = fd; SEND; @@ -2227,6 +2340,11 @@ return eio__2path (EIO_RENAME, path, new_path, pri, cb, data); } +eio_req *eio_slurp (const char *path, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) +{ + REQ (EIO_SLURP); PATH; req->offs = offset; req->size = length; req->ptr2 = buf; SEND; +} + eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data) { REQ (EIO_CUSTOM); req->feed = execute; SEND;