Commit 60136d87 authored by Christopher Dykes's avatar Christopher Dykes Committed by Facebook Github Bot 3

Support read and write from blocking and non-blocking pipes and sockets

Summary: Also switch `pipe` to return a socket pair instead, as libevent can't poll against a pipe on Windows. And lastly, fix the file descriptor for sockets leaking when close is called.

Reviewed By: yfeldblum

Differential Revision: D3691255

fbshipit-source-id: c684242d255ac5158a4c805d432d345dac1b3bd8
parent cc01a8a4
...@@ -54,10 +54,24 @@ int access(char const* fn, int am) { return _access(fn, am); } ...@@ -54,10 +54,24 @@ int access(char const* fn, int am) { return _access(fn, am); }
int chdir(const char* path) { return _chdir(path); } int chdir(const char* path) { return _chdir(path); }
// We aren't hooking into the internals of the CRT, nope, not at all.
extern "C" int __cdecl _free_osfhnd(int const fh);
int close(int fh) { int close(int fh) {
if (folly::portability::sockets::is_fh_socket(fh)) { if (folly::portability::sockets::is_fh_socket(fh)) {
SOCKET h = (SOCKET)_get_osfhandle(fh); SOCKET h = (SOCKET)_get_osfhandle(fh);
return closesocket(h);
// If we were to just call _close on the descriptor, it would
// close the HANDLE, but it wouldn't free any of the resources
// associated to the SOCKET, and we can't call _close after
// calling closesocket, because closesocket has already closed
// the HANDLE, and _close would attempt to close the HANDLE
// again, resulting in a double free.
// Luckily though, there is a function in the internals of the
// CRT that is used to free only the file descriptor, so we
// can call that to avoid leaking the file descriptor itself.
auto c = closesocket(h);
_free_osfhnd(fh);
return c;
} }
return _close(fh); return _close(fh);
} }
...@@ -114,7 +128,11 @@ long lseek(int fh, long off, int orig) { return _lseek(fh, off, orig); } ...@@ -114,7 +128,11 @@ long lseek(int fh, long off, int orig) { return _lseek(fh, off, orig); }
int rmdir(const char* path) { return _rmdir(path); } int rmdir(const char* path) { return _rmdir(path); }
int pipe(int* pth) { return _pipe(pth, 0, _O_BINARY); } int pipe(int pth[2]) {
// We need to be able to listen to pipes with
// libevent, so they need to be actual sockets.
return socketpair(PF_UNIX, SOCK_STREAM, 0, pth);
}
int pread(int fd, void* buf, size_t count, off_t offset) { int pread(int fd, void* buf, size_t count, off_t offset) {
return wrapPositional(_read, fd, offset, buf, (unsigned int)count); return wrapPositional(_read, fd, offset, buf, (unsigned int)count);
...@@ -124,7 +142,26 @@ int pwrite(int fd, const void* buf, size_t count, off_t offset) { ...@@ -124,7 +142,26 @@ int pwrite(int fd, const void* buf, size_t count, off_t offset) {
return wrapPositional(_write, fd, offset, buf, (unsigned int)count); return wrapPositional(_write, fd, offset, buf, (unsigned int)count);
} }
int read(int fh, void* buf, unsigned int mcc) { return _read(fh, buf, mcc); } int read(int fh, void* buf, unsigned int mcc) {
if (folly::portability::sockets::is_fh_socket(fh)) {
SOCKET s = (SOCKET)_get_osfhandle(fh);
if (s != INVALID_SOCKET) {
auto r = folly::portability::sockets::recv(fh, buf, (size_t)mcc, 0);
if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) {
errno = EAGAIN;
}
return r;
}
}
auto r = _read(fh, buf, mcc);
if (r == -1 && GetLastError() == ERROR_NO_DATA) {
// This only happens if the file was non-blocking and
// no data was present. We have to translate the error
// to a form that the rest of the world is expecting.
errno = EAGAIN;
}
return r;
}
ssize_t readlink(const char* path, char* buf, size_t buflen) { ssize_t readlink(const char* path, char* buf, size_t buflen) {
if (!buflen) { if (!buflen) {
...@@ -198,8 +235,36 @@ int usleep(unsigned int ms) { ...@@ -198,8 +235,36 @@ int usleep(unsigned int ms) {
return 0; return 0;
} }
int write(int fh, void const* buf, unsigned int mcc) { int write(int fh, void const* buf, unsigned int count) {
return _write(fh, buf, mcc); if (folly::portability::sockets::is_fh_socket(fh)) {
SOCKET s = (SOCKET)_get_osfhandle(fh);
if (s != INVALID_SOCKET) {
auto r = folly::portability::sockets::send(fh, buf, (size_t)count, 0);
if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) {
errno = EAGAIN;
}
return r;
}
}
auto r = _write(fh, buf, count);
if ((r > 0 && r != count) || (r == -1 && errno == ENOSPC)) {
// Writing to a pipe with a full buffer doesn't generate
// any error type, unless it caused us to write exactly 0
// bytes, so we have to see if we have a pipe first. We
// don't touch the errno for anything else.
HANDLE h = (HANDLE)_get_osfhandle(fh);
if (GetFileType(h) == FILE_TYPE_PIPE) {
DWORD state = 0;
if (GetNamedPipeHandleState(
h, &state, nullptr, nullptr, nullptr, nullptr, 0)) {
if ((state & PIPE_NOWAIT) == PIPE_NOWAIT) {
errno = EAGAIN;
return -1;
}
}
}
}
return r;
} }
} }
} }
......
...@@ -71,7 +71,7 @@ int lockf(int fd, int cmd, off_t len); ...@@ -71,7 +71,7 @@ int lockf(int fd, int cmd, off_t len);
long lseek(int fh, long off, int orig); long lseek(int fh, long off, int orig);
int read(int fh, void* buf, unsigned int mcc); int read(int fh, void* buf, unsigned int mcc);
int rmdir(const char* path); int rmdir(const char* path);
int pipe(int* pth); int pipe(int pth[2]);
int pread(int fd, void* buf, size_t count, off_t offset); int pread(int fd, void* buf, size_t count, off_t offset);
int pwrite(int fd, const void* buf, size_t count, off_t offset); int pwrite(int fd, const void* buf, size_t count, off_t offset);
ssize_t readlink(const char* path, char* buf, size_t buflen); ssize_t readlink(const char* path, char* buf, size_t buflen);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment