Abstract fd operations better in tool.
Windows and POSIX implement very similar fd operations, but differ
slightly:
- ssize_t in POSIX is usually int on Windows.
- POSIX needs EINTR retry loops.
- Windows wants _open rather than open, etc.
- POSIX fds and sockets are the same thing, while Windows sockets are
HANDLEs and leaves fd as a C runtime construct.
Rather than ad-hoc macros and redefinitions of ssize_t (which reportedly
upset MINGW), add some actual abstractions. While I'm here, add a scoped
file descriptor type.
That still leaves recv/send which are only used in one file, so defined
a socket_result_t for them.
Change-Id: I17fca2a50c77191f573852bfd27553996e3e9c3f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/41725
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt
index 7f34017..7658713 100644
--- a/tool/CMakeLists.txt
+++ b/tool/CMakeLists.txt
@@ -8,6 +8,7 @@
client.cc
const.cc
digest.cc
+ fd.cc
file.cc
generate_ed25519.cc
genrsa.cc
diff --git a/tool/digest.cc b/tool/digest.cc
index 742fa7f..4adaf8d 100644
--- a/tool/digest.cc
+++ b/tool/digest.cc
@@ -37,7 +37,6 @@
OPENSSL_MSVC_PRAGMA(warning(pop))
#include <io.h>
#define PATH_MAX MAX_PATH
-typedef int ssize_t;
#endif
#include <openssl/digest.h>
@@ -45,19 +44,6 @@
#include "internal.h"
-struct close_delete {
- void operator()(int *fd) {
- BORINGSSL_CLOSE(*fd);
- }
-};
-
-template<typename T, typename R, R (*func) (T*)>
-struct func_delete {
- void operator()(T* obj) {
- func(obj);
- }
-};
-
// Source is an awkward expression of a union type in C++: Stdin | File filename.
struct Source {
enum Type {
@@ -79,37 +65,31 @@
static const char kStdinName[] = "standard input";
-// OpenFile opens the regular file named |filename| and sets |*out_fd| to be a
-// file descriptor to it. Returns true on sucess or prints an error to stderr
-// and returns false on error.
-static bool OpenFile(int *out_fd, const std::string &filename) {
- *out_fd = -1;
-
- int fd = BORINGSSL_OPEN(filename.c_str(), O_RDONLY | O_BINARY);
- if (fd < 0) {
+// OpenFile opens the regular file named |filename| and returns a file
+// descriptor to it.
+static ScopedFD OpenFile(const std::string &filename) {
+ ScopedFD fd = OpenFD(filename.c_str(), O_RDONLY | O_BINARY);
+ if (!fd) {
fprintf(stderr, "Failed to open input file '%s': %s\n", filename.c_str(),
strerror(errno));
- return false;
+ return ScopedFD();
}
- std::unique_ptr<int, close_delete> scoped_fd(&fd);
#if !defined(OPENSSL_WINDOWS)
struct stat st;
- if (fstat(fd, &st)) {
+ if (fstat(fd.get(), &st)) {
fprintf(stderr, "Failed to stat input file '%s': %s\n", filename.c_str(),
strerror(errno));
- return false;
+ return ScopedFD();
}
if (!S_ISREG(st.st_mode)) {
fprintf(stderr, "%s: not a regular file\n", filename.c_str());
- return false;
+ return ScopedFD();
}
#endif
- *out_fd = fd;
- scoped_fd.release();
- return true;
+ return fd;
}
// SumFile hashes the contents of |source| with |md| and sets |*out_hex| to the
@@ -119,16 +99,17 @@
// error.
static bool SumFile(std::string *out_hex, const EVP_MD *md,
const Source &source) {
- std::unique_ptr<int, close_delete> scoped_fd;
+ ScopedFD scoped_fd;
int fd;
if (source.is_stdin()) {
fd = 0;
} else {
- if (!OpenFile(&fd, source.filename())) {
+ scoped_fd = OpenFile(source.filename());
+ if (!scoped_fd) {
return false;
}
- scoped_fd.reset(&fd);
+ fd = scoped_fd.get();
}
static const size_t kBufSize = 8192;
@@ -141,21 +122,18 @@
}
for (;;) {
- ssize_t n;
-
- do {
- n = BORINGSSL_READ(fd, buf.get(), kBufSize);
- } while (n == -1 && errno == EINTR);
-
- if (n == 0) {
- break;
- } else if (n < 0) {
+ size_t n;
+ if (!ReadFromFD(fd, &n, buf.get(), kBufSize)) {
fprintf(stderr, "Failed to read from %s: %s\n",
source.is_stdin() ? kStdinName : source.filename().c_str(),
strerror(errno));
return false;
}
+ if (n == 0) {
+ break;
+ }
+
if (!EVP_DigestUpdate(ctx.get(), buf.get(), n)) {
fprintf(stderr, "Failed to update hash.\n");
return false;
@@ -221,25 +199,23 @@
// returns false.
static bool Check(const CheckModeArguments &args, const EVP_MD *md,
const Source &source) {
- std::unique_ptr<FILE, func_delete<FILE, int, fclose>> scoped_file;
FILE *file;
+ ScopedFILE scoped_file;
if (source.is_stdin()) {
file = stdin;
} else {
- int fd;
- if (!OpenFile(&fd, source.filename())) {
+ ScopedFD fd = OpenFile(source.filename());
+ if (!fd) {
return false;
}
- file = BORINGSSL_FDOPEN(fd, "rb");
- if (!file) {
+ scoped_file = FDToFILE(std::move(fd), "rb");
+ if (!scoped_file) {
perror("fdopen");
- BORINGSSL_CLOSE(fd);
return false;
}
-
- scoped_file = std::unique_ptr<FILE, func_delete<FILE, int, fclose>>(file);
+ file = scoped_file.get();
}
const size_t hex_size = EVP_MD_size(md) * 2;
diff --git a/tool/fd.cc b/tool/fd.cc
new file mode 100644
index 0000000..2c27ccd
--- /dev/null
+++ b/tool/fd.cc
@@ -0,0 +1,105 @@
+/* Copyright (c) 2020, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/base.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include <algorithm>
+
+#include "internal.h"
+
+#if defined(OPENSSL_WINDOWS)
+#include <io.h>
+#else
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+
+
+ScopedFD OpenFD(const char *path, int flags) {
+#if defined(OPENSSL_WINDOWS)
+ return ScopedFD(_open(path, flags));
+#else
+ int fd;
+ do {
+ fd = open(path, flags);
+ } while (fd == -1 && errno == EINTR);
+ return ScopedFD(fd);
+#endif
+}
+
+void CloseFD(int fd) {
+#if defined(OPENSSL_WINDOWS)
+ _close(fd);
+#else
+ close(fd);
+#endif
+}
+
+bool ReadFromFD(int fd, size_t *out_bytes_read, void *out, size_t num) {
+#if defined(OPENSSL_WINDOWS)
+ // On Windows, the buffer must be at most |INT_MAX|. See
+ // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/read?view=vs-2019
+ int ret = _read(fd, out, std::min(size_t{INT_MAX}, num));
+#else
+ ssize_t ret;
+ do {
+ ret = read(fd, out, num);
+ } while (ret == -1 && errno == EINVAL);
+#endif
+
+ if (ret < 0) {
+ *out_bytes_read = 0;
+ return false;
+ }
+ *out_bytes_read = ret;
+ return true;
+}
+
+bool WriteToFD(int fd, size_t *out_bytes_written, const void *in, size_t num) {
+#if defined(OPENSSL_WINDOWS)
+ // The documentation for |_write| does not say the buffer must be at most
+ // |INT_MAX|, but clamp it to |INT_MAX| instead of |UINT_MAX| in case.
+ int ret = _write(fd, in, std::min(size_t{INT_MAX}, num));
+#else
+ ssize_t ret;
+ do {
+ ret = write(fd, in, num);
+ } while (ret == -1 && errno == EINVAL);
+#endif
+
+ if (ret < 0) {
+ *out_bytes_written = 0;
+ return false;
+ }
+ *out_bytes_written = ret;
+ return true;
+}
+
+ScopedFILE FDToFILE(ScopedFD fd, const char *mode) {
+ ScopedFILE ret;
+#if defined(OPENSSL_WINDOWS)
+ ret.reset(_fdopen(fd.get(), mode));
+#else
+ ret.reset(fdopen(fd.get(), mode));
+#endif
+ // |fdopen| takes ownership of |fd| on success.
+ if (ret) {
+ fd.release();
+ }
+ return ret;
+}
diff --git a/tool/internal.h b/tool/internal.h
index 4cace9d..eb9e4ba 100644
--- a/tool/internal.h
+++ b/tool/internal.h
@@ -18,32 +18,17 @@
#include <openssl/base.h>
#include <string>
+#include <utility>
#include <vector>
-OPENSSL_MSVC_PRAGMA(warning(push))
// MSVC issues warning C4702 for unreachable code in its xtree header when
// compiling with -D_HAS_EXCEPTIONS=0. See
// https://connect.microsoft.com/VisualStudio/feedback/details/809962
+OPENSSL_MSVC_PRAGMA(warning(push))
OPENSSL_MSVC_PRAGMA(warning(disable: 4702))
-
#include <map>
-
OPENSSL_MSVC_PRAGMA(warning(pop))
-#if defined(OPENSSL_WINDOWS)
- #define BORINGSSL_OPEN _open
- #define BORINGSSL_FDOPEN _fdopen
- #define BORINGSSL_CLOSE _close
- #define BORINGSSL_READ _read
- #define BORINGSSL_WRITE _write
-#else
- #define BORINGSSL_OPEN open
- #define BORINGSSL_FDOPEN fdopen
- #define BORINGSSL_CLOSE close
- #define BORINGSSL_READ read
- #define BORINGSSL_WRITE write
-#endif
-
struct FileCloser {
void operator()(FILE *file) {
fclose(file);
@@ -52,6 +37,67 @@
using ScopedFILE = std::unique_ptr<FILE, FileCloser>;
+// The following functions abstract between POSIX and Windows differences in
+// file descriptor I/O functions.
+
+// CloseFD behaves like |close|.
+void CloseFD(int fd);
+
+class ScopedFD {
+ public:
+ ScopedFD() {}
+ explicit ScopedFD(int fd) : fd_(fd) {}
+ ScopedFD(ScopedFD &&other) { *this = std::move(other); }
+ ScopedFD(const ScopedFD &) = delete;
+ ~ScopedFD() { reset(); }
+
+ ScopedFD &operator=(const ScopedFD &) = delete;
+ ScopedFD &operator=(ScopedFD &&other) {
+ reset();
+ fd_ = other.fd_;
+ other.fd_ = -1;
+ return *this;
+ }
+
+ explicit operator bool() const { return fd_ >= 0; }
+
+ int get() const { return fd_; }
+
+ void reset() {
+ if (fd_ >= 0) {
+ CloseFD(fd_);
+ }
+ fd_ = -1;
+ }
+
+ int release() {
+ int fd = fd_;
+ fd_ = -1;
+ return fd;
+ }
+
+ private:
+ int fd_ = -1;
+};
+
+// OpenFD behaves like |open| but handles |EINTR| and works on Windows.
+ScopedFD OpenFD(const char *path, int flags);
+
+// ReadFromFD reads up to |num| bytes from |fd| and writes the result to |out|.
+// On success, it returns true and sets |*out_bytes_read| to the number of bytes
+// read. Otherwise, it returns false and leaves an error in |errno|. On POSIX,
+// it handles |EINTR| internally.
+bool ReadFromFD(int fd, size_t *out_bytes_read, void *out, size_t num);
+
+// WriteToFD writes up to |num| bytes from |in| to |fd|. On success, it returns
+// true and sets |*out_bytes_written| to the number of bytes written. Otherwise,
+// it returns false and leaves an error in |errno|. On POSIX, it handles |EINTR|
+// internally.
+bool WriteToFD(int fd, size_t *out_bytes_written, const void *in, size_t num);
+
+// FDToFILE behaves like |fdopen|.
+ScopedFILE FDToFILE(ScopedFD fd, const char *mode);
+
enum ArgumentType {
kRequiredArgument,
kOptionalArgument,
diff --git a/tool/pkcs12.cc b/tool/pkcs12.cc
index a8ddb0e..3d8a1cd 100644
--- a/tool/pkcs12.cc
+++ b/tool/pkcs12.cc
@@ -40,12 +40,6 @@
#include "internal.h"
-#if defined(OPENSSL_WINDOWS)
-typedef int read_result_t;
-#else
-typedef ssize_t read_result_t;
-#endif
-
static const struct argument kArguments[] = {
{
"-dump", kOptionalArgument,
@@ -65,51 +59,52 @@
return false;
}
- int fd = BORINGSSL_OPEN(args_map["-dump"].c_str(), O_RDONLY);
- if (fd < 0) {
+ ScopedFD fd = OpenFD(args_map["-dump"].c_str(), O_RDONLY);
+ if (!fd) {
perror("open");
return false;
}
struct stat st;
- if (fstat(fd, &st)) {
+ if (fstat(fd.get(), &st)) {
perror("fstat");
- BORINGSSL_CLOSE(fd);
return false;
}
const size_t size = st.st_size;
std::unique_ptr<uint8_t[]> contents(new uint8_t[size]);
- read_result_t n;
size_t off = 0;
- do {
- n = BORINGSSL_READ(fd, &contents[off], size - off);
- if (n >= 0) {
- off += static_cast<size_t>(n);
+ while (off < size) {
+ size_t bytes_read;
+ if (!ReadFromFD(fd.get(), &bytes_read, contents.get() + off, size - off)) {
+ perror("read");
+ return false;
}
- } while ((n > 0 && off < size) || (n == -1 && errno == EINTR));
-
- if (off != size) {
- perror("read");
- BORINGSSL_CLOSE(fd);
- return false;
+ if (bytes_read == 0) {
+ fprintf(stderr, "Unexpected EOF\n");
+ return false;
+ }
+ off += bytes_read;
}
- BORINGSSL_CLOSE(fd);
-
printf("Enter password: ");
fflush(stdout);
char password[256];
off = 0;
- do {
- n = BORINGSSL_READ(0, &password[off], sizeof(password) - 1 - off);
- if (n >= 0) {
- off += static_cast<size_t>(n);
+ while (off < sizeof(password) - 1) {
+ size_t bytes_read;
+ if (!ReadFromFD(0, &bytes_read, password + off,
+ sizeof(password) - 1 - off)) {
+ perror("read");
+ return false;
}
- } while ((n > 0 && OPENSSL_memchr(password, '\n', off) == NULL &&
- off < sizeof(password) - 1) ||
- (n == -1 && errno == EINTR));
+
+ off += bytes_read;
+ if (bytes_read == 0 || OPENSSL_memchr(password, '\n', off) != nullptr) {
+ break;
+ }
+ }
char *newline = reinterpret_cast<char *>(OPENSSL_memchr(password, '\n', off));
if (newline == NULL) {
diff --git a/tool/transport_common.cc b/tool/transport_common.cc
index 7c5e962..88e9169 100644
--- a/tool/transport_common.cc
+++ b/tool/transport_common.cc
@@ -55,7 +55,6 @@
#include <ws2tcpip.h>
OPENSSL_MSVC_PRAGMA(warning(pop))
-typedef int ssize_t;
OPENSSL_MSVC_PRAGMA(comment(lib, "Ws2_32.lib"))
#endif
@@ -68,7 +67,10 @@
#include "transport_common.h"
-#if !defined(OPENSSL_WINDOWS)
+#if defined(OPENSSL_WINDOWS)
+using socket_result_t = int;
+#else
+using socket_result_t = ssize_t;
static int closesocket(int sock) {
return close(sock);
}
@@ -739,12 +741,13 @@
return true;
}
- ssize_t n;
- do {
- n = BORINGSSL_WRITE(1, buffer, ssl_ret);
- } while (n == -1 && errno == EINTR);
+ size_t n;
+ if (!WriteToFD(1, &n, buffer, ssl_ret)) {
+ fprintf(stderr, "Error writing to stdout.\n");
+ return false;
+ }
- if (n != ssl_ret) {
+ if (n != static_cast<size_t>(ssl_ret)) {
fprintf(stderr, "Short write to stderr.\n");
return false;
}
@@ -786,7 +789,7 @@
return false;
}
- ssize_t n;
+ socket_result_t n;
do {
n = recv(sock_, &buf_[buf_len_], sizeof(buf_) - buf_len_, 0);
} while (n == -1 && errno == EINTR);
@@ -871,7 +874,7 @@
size_t done = 0;
while (done < data_len) {
- ssize_t n;
+ socket_result_t n;
do {
n = send(sock, &data[done], data_len - done, 0);
} while (n == -1 && errno == EINTR);