From 4e6d0dc9476e5cf43ff0bc5e66a54d812ddb4225 Mon Sep 17 00:00:00 2001
From: Ron Yorston <rmy@pobox.com>
Date: Thu, 22 Feb 2018 11:40:53 +0000
Subject: win32: handle /dev/zero and /dev/urandom in open and read functions

Currently /dev/zero is handled as a special case in dd.  Add hacks
to the open and read functions in mingw.c to handle the zero and
urandom devices.

- Opening /dev/zero or /dev/urandom actually opens the special
  Windows file 'nul' which behaves like /dev/null.  This allows
  manipulation of the file descriptor with things like seek and
  close

- When /dev/zero or /dev/urandom is opened the resulting file
  descriptor is stored and used to override the behaviour of read.

- No attempt is made to track duplicated file descriptors, so using
  these devices for redirections in the shell isn't going to work
  and won't be permitted.  (Could be, but won't.)

- Limited control of the special file descriptors is provided by
  allowing the internal variables to be changed.

- The numbers from /dev/urandom aren't very random.
---
 include/mingw.h |  5 +++++
 win32/mingw.c   | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 win32/winansi.c |  2 +-
 3 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/include/mingw.h b/include/mingw.h
index a59ce6fc4..46be4dc23 100644
--- a/include/mingw.h
+++ b/include/mingw.h
@@ -395,6 +395,10 @@ int kill(pid_t pid, int sig);
 int link(const char *oldpath, const char *newpath);
 NOIMPL(mknod,const char *name UNUSED_PARAM, mode_t mode UNUSED_PARAM, dev_t device UNUSED_PARAM);
 int mingw_open (const char *filename, int oflags, ...);
+void mingw_read_zero(int fd);
+void mingw_read_random(int fd);
+ssize_t mingw_read(int fd, void *buf, size_t count);
+int mingw_close(int fd);
 int pipe(int filedes[2]);
 NOIMPL(readlink,const char *path UNUSED_PARAM, char *buf UNUSED_PARAM, size_t bufsiz UNUSED_PARAM);
 NOIMPL(setgid,gid_t gid UNUSED_PARAM);
@@ -418,6 +422,7 @@ int mingw_isatty(int fd);
 #define getcwd mingw_getcwd
 #define lchown chown
 #define open mingw_open
+#define close mingw_close
 #define unlink mingw_unlink
 #define rmdir mingw_rmdir
 #undef lseek
diff --git a/win32/mingw.c b/win32/mingw.c
index a55fb4f40..595042d85 100644
--- a/win32/mingw.c
+++ b/win32/mingw.c
@@ -138,12 +138,28 @@ int err_win_to_posix(DWORD winerr)
 	return error;
 }
 
+static int zero_fd = -1;
+static int rand_fd = -1;
+
+void mingw_read_zero(int fd)
+{
+	zero_fd = fd;
+}
+
+void mingw_read_random(int fd)
+{
+	rand_fd = fd;
+}
+
 #undef open
 int mingw_open (const char *filename, int oflags, ...)
 {
 	va_list args;
 	unsigned mode;
 	int fd;
+	int devnull = 0;
+	int devzero = 0;
+	int devrand = 0;
 
 	va_start(args, oflags);
 	mode = va_arg(args, int);
@@ -152,14 +168,29 @@ int mingw_open (const char *filename, int oflags, ...)
 	if (oflags & O_NONBLOCK) {
 		oflags &= ~O_NONBLOCK;
 	}
-	if (filename && !strcmp(filename, "/dev/null"))
-		filename = "nul";
+	if (filename && !strncmp(filename, "/dev/", 5)) {
+		if (!strcmp(filename+5, "null"))
+			devnull = 1;
+		else if (!strcmp(filename+5, "zero"))
+			devzero = 1;
+		else if (!strcmp(filename+5, "urandom"))
+			devrand = 1;
+
+		if (devnull || devzero || devrand )
+			filename = "nul";
+	}
 	fd = open(filename, oflags, mode);
 	if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
 		DWORD attrs = GetFileAttributes(filename);
 		if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
 			errno = EISDIR;
 	}
+	if (fd >= 0 ) {
+		if (devzero)
+			zero_fd = fd;
+		else if (devrand)
+			rand_fd = fd;
+	}
 	return fd;
 }
 
@@ -171,6 +202,32 @@ FILE *mingw_fopen (const char *filename, const char *otype)
 	return fopen(filename, otype);
 }
 
+#undef read
+ssize_t mingw_read(int fd, void *buf, size_t count)
+{
+	if (fd == zero_fd) {
+		memset(buf, 0, count);
+		return count;
+	}
+	else if (fd == rand_fd) {
+		memset(buf, 0x5A, count);
+		return count;
+	}
+	return read(fd, buf, count);
+}
+
+#undef close
+int mingw_close(int fd)
+{
+	if (fd == zero_fd) {
+		zero_fd = -1;
+	}
+	if (fd == rand_fd) {
+		rand_fd = -1;
+	}
+	return close(fd);
+}
+
 #undef dup2
 int mingw_dup2 (int fd, int fdto)
 {
diff --git a/win32/winansi.c b/win32/winansi.c
index 8fffc5a00..b357fc642 100644
--- a/win32/winansi.c
+++ b/win32/winansi.c
@@ -695,7 +695,7 @@ int winansi_read(int fd, void *buf, size_t count)
 {
 	int rv;
 
-	rv = read(fd, buf, count);
+	rv = mingw_read(fd, buf, count);
 	if (!isatty(fd))
 		return rv;
 
-- 
cgit v1.2.3-55-g6feb