aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSeebs <seebs@seebs.net>2019-04-10 10:36:01 -0500
committerSeebs <seebs@seebs.net>2019-04-10 11:03:43 -0500
commita1fafc4a96e07d20678a243d2f158c4a3f06a17a (patch)
tree909ab9fee31ef2231fc93bc0bcc73441eab7773d
parent6ebc7d6bc8ab973d0ba949eeb363821811ce8dc5 (diff)
downloadpseudo-a1fafc4a96e07d20678a243d2f158c4a3f06a17a.tar.gz
pseudo-a1fafc4a96e07d20678a243d2f158c4a3f06a17a.tar.bz2
pseudo-a1fafc4a96e07d20678a243d2f158c4a3f06a17a.zip
Try to handle blocking open.
This is a heck of a special case: If you call open on a FIFO/pipe, and you didn't have O_NONBLOCK, and you used O_RDONLY or O_WRONLY, but not O_RDWR, the open can block forever. Unfortunately, pseudo assumes syscalls complete. We attempt to drop the lock and restore our state, then recover it later. Why? Because the .NET runtime does this for a debug hook.
-rw-r--r--ChangeLog.txt4
-rw-r--r--ports/linux/guts/openat.c37
2 files changed, 41 insertions, 0 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt
index f79c2d1..b5f64c7 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,3 +1,7 @@
+2019-04-10:
+ * (seebs) Experimental workaround for special non-blocking open
+ case.
+
2019-04-09:
* (seebs) Partial fix for db corruption issue.
* (seebs) Make a glibc renameat2 wrapper that just fails because
diff --git a/ports/linux/guts/openat.c b/ports/linux/guts/openat.c
index 924e354..cc6b118 100644
--- a/ports/linux/guts/openat.c
+++ b/ports/linux/guts/openat.c
@@ -7,8 +7,10 @@
* int rc = -1;
*/
struct stat64 buf;
+ int overly_magic_nonblocking = 0;
int existed = 1;
int save_errno;
+ sigset_t local_saved_sigmask;
/* mask out mode bits appropriately */
mode = mode & ~pseudo_umask;
@@ -61,6 +63,27 @@
errno = save_errno;
}
+ /* if a pipe is opened without O_NONBLOCK, for only reading or
+ * only writing, it can block forever. We need to do extra magic
+ * in that case...
+ */
+ if (!(flags & O_NONBLOCK) && ((flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_RDWR)) {
+ save_errno = errno;
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ rc = real___xstat64(_STAT_VER, path, &buf);
+#else
+ rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, 0);
+#endif
+ if (rc != -1 && S_ISFIFO(buf.st_mode)) {
+ overly_magic_nonblocking = 1;
+ }
+ }
+
+ /* this is a horrible special case and i do not know whether it will work */
+ if (overly_magic_nonblocking) {
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &pseudo_saved_sigmask, &local_saved_sigmask);
+ }
/* because we are not actually root, secretly mask in 0600 to the
* underlying mode. The ", 0" is because the only time mode matters
* is if a file is going to be created, in which case it's
@@ -71,6 +94,20 @@
#else
rc = real_openat(dirfd, path, flags, PSEUDO_FS_MODE(mode, 0));
#endif
+ if (overly_magic_nonblocking) {
+ save_errno = errno;
+ sigprocmask(SIG_SETMASK, &local_saved_sigmask, NULL);
+ /* well this is a problem. we can't NOT proceed; we may have
+ * already opened the file! we can't even return up the call
+ * stack to stuff that's going to try to drop the lock.
+ */
+ if (pseudo_getlock()) {
+ pseudo_diag("PANIC: after opening a readonly/writeonly FIFO (path '%s', fd %d, errno %d, saved errno %d), could not regain lock. unrecoverable. sorry. bye.\n",
+ path, rc, errno, save_errno);
+ abort();
+ }
+ errno = save_errno;
+ }
if (rc != -1) {
save_errno = errno;