--- libeio/eio.c 2015/06/25 17:40:24 1.137 +++ 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); @@ -540,6 +559,21 @@ /*****************************************************************************/ /* 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 @@ -927,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; @@ -973,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; @@ -991,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) @@ -1003,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; @@ -1026,7 +1060,7 @@ if (wd == EIO_CWD) { - if (!getcwd (res, PATH_MAX)) + if (!getcwd (res, EIO_PATH_MAX)) return -1; len = strlen (res); @@ -1083,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) { @@ -1099,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; @@ -1373,7 +1407,7 @@ dirp = fdopendir (fd); if (!dirp) - close (fd); + silent_close (fd); } else dirp = opendir (req->ptr1); @@ -1662,7 +1696,7 @@ if (wd != EIO_INVALID_WD && wd != EIO_CWD) { #if HAVE_AT - close (wd->fd); + eio__close (wd->fd); #endif free (wd); } @@ -1670,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 @@ -1682,7 +1729,7 @@ return fd; res = ftruncate (fd, length); - close (fd); + silent_close (fd); return res; } @@ -1696,9 +1743,8 @@ return fd; res = fstatvfs (fd, buf); - close (fd); + silent_close (fd); return res; - } #endif @@ -1708,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 (EIO_POOL, 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 @@ -1809,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; @@ -1822,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 */ @@ -1829,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: { @@ -1874,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: @@ -1930,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; @@ -1978,6 +2077,7 @@ break; } +alloc_fail: req->errorno = errno; } @@ -2078,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; @@ -2230,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;