[newlib-cygwin] Cygwin: implement /proc/PID/fd/DESCRIPTOR reopening by handle

Corinna Vinschen corinna@sourceware.org
Sun Jan 6 19:42:00 GMT 2019


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=732613f30a5db6bfb4eb7a90e8822d4ee3c629bc

commit 732613f30a5db6bfb4eb7a90e8822d4ee3c629bc
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Sat Jan 5 22:13:42 2019 +0100

    Cygwin: implement /proc/PID/fd/DESCRIPTOR reopening by handle
    
    Allows expressions along the lines of `cat /proc/self/fd/0 <<EOF'.
    The problem here is that the temporary file used for the here script
    has already been deleted by the shell.  Opening by filename, as
    implemented so far, doesn't work because the file has been moved
    to the bin.
    
    Allow reopening files by handle the same way from another process
    as long as we have sufficient permissions on the foreign process.
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/fhandler.cc         |  7 ++++
 winsup/cygwin/fhandler.h          |  2 +
 winsup/cygwin/fhandler_process.cc | 78 +++++++++++++++++++++++++++++++++++++++
 winsup/cygwin/path.cc             |  4 ++
 winsup/cygwin/path.h              |  2 +
 winsup/cygwin/syscalls.cc         | 20 +++++++---
 6 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 01afdb2..4ecbf61 100644
--- a/winsup/cygwin/fhandler.cc
+++ b/winsup/cygwin/fhandler.cc
@@ -767,6 +767,13 @@ done:
   return res;
 }
 
+fhandler_base *
+fhandler_base::fd_reopen (int)
+{
+  /* This is implemented in fhandler_process only. */
+  return NULL;
+}
+
 void
 fhandler_base::open_setup (int)
 {
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 0ead0d9..74eb1d7 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -331,6 +331,7 @@ class fhandler_base
   int open_with_arch (int, mode_t = 0);
   int open_null (int flags);
   virtual int open (int, mode_t);
+  virtual fhandler_base *fd_reopen (int);
   virtual void open_setup (int flags);
   void set_unique_id (int64_t u) { unique_id = u; }
   void set_unique_id () { NtAllocateLocallyUniqueId ((PLUID) &unique_id); }
@@ -2553,6 +2554,7 @@ class fhandler_process: public fhandler_proc
   int closedir (DIR *);
   int __reg3 readdir (DIR *, dirent *);
   int open (int flags, mode_t mode = 0);
+  virtual fhandler_base *fd_reopen (int);
   int __reg2 fstat (struct stat *buf);
   bool fill_filebuf ();
 
diff --git a/winsup/cygwin/fhandler_process.cc b/winsup/cygwin/fhandler_process.cc
index 24ef7d0..0fad96f 100644
--- a/winsup/cygwin/fhandler_process.cc
+++ b/winsup/cygwin/fhandler_process.cc
@@ -321,6 +321,84 @@ out:
   return res;
 }
 
+fhandler_base *
+fhandler_process::fd_reopen (int flags)
+{
+  const char *path;
+  char *e;
+  int fd;
+  HANDLE proc = NULL;
+  HANDLE hdl = NULL;
+  fhandler_base *fh = NULL;
+
+  path = get_name () + proc_len + 1;
+  pid = strtoul (path, &e, 10);
+  path = e + 4;
+  fd = strtoul (path, &e, 10);
+  if (e == path || *e != '\0')
+    {
+      set_errno (ENOENT);
+      return NULL;
+    }
+
+  if (pid == myself->pid)
+    {
+      cygheap_fdget cfd (fd);
+      if (cfd < 0)
+	return NULL;
+      fh = build_fh_pc (cfd->pc);
+      if (!fh)
+	goto err_out;
+      fh->set_io_handle (cfd->get_handle ());
+    }
+  else
+    {
+      size_t size;
+      path_conv pc;
+
+      pinfo p (pid);
+      if (!p)
+	{
+	  set_errno (ENOENT);
+	  return NULL;
+	}
+      if (!(proc = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId)))
+	{
+	  __seterrno ();
+	  return NULL;
+	}
+      void *buf = p->file_pathconv (fd, size);
+      if (size == 0)
+	{
+	  set_errno (EPERM);
+	  goto err_out;
+	}
+      hdl = pc.deserialize (buf);
+      if (!DuplicateHandle (proc, hdl, GetCurrentProcess (), &hdl,
+			    0, FALSE, DUPLICATE_SAME_ACCESS))
+	{
+	  __seterrno ();
+	  hdl = NULL;
+	  goto err_out;
+	}
+      fh = build_fh_pc (pc);
+      if (!fh)
+	goto err_out;
+      fh->set_io_handle (hdl);
+    }
+  if (!fh->open_with_arch (flags, 0))
+    {
+      delete fh;
+      fh = NULL;
+    }
+err_out:
+  if (hdl)
+    CloseHandle (hdl);
+  if (proc)
+    CloseHandle (proc);
+  return fh;
+}
+
 struct process_fd_t {
   const char *path;
   _pinfo *p;
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index df11d53..b039801 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -844,6 +844,10 @@ path_conv::check (const char *src, unsigned opt,
 			  fileattr = 0;
 			break;
 		      case virt_fdsymlink:
+			/* Allow open/linkat to do the right thing. */
+			if (opt & PC_SYM_NOFOLLOW_PROCFD)
+			  opt &= ~PC_SYM_FOLLOW;
+			/*FALLTHRU*/
 		      case virt_symlink:
 			goto is_virtual_symlink;
 		      case virt_pipe:
diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h
index 1e3095e..9812699 100644
--- a/winsup/cygwin/path.h
+++ b/winsup/cygwin/path.h
@@ -47,10 +47,12 @@ enum pathconv_arg
   PC_SYM_CONTENTS	= 0x0008,
   PC_NOFULL		= 0x0010,
   PC_NULLEMPTY		= 0x0020,
+  PC_DO_NOT_USE		= 0x0040,
   PC_POSIX		= 0x0080,
   PC_NOWARN		= 0x0100,
   PC_OPEN		= 0x0200,	/* use open semantics */
   PC_CTTY		= 0x0400,	/* could later be used as ctty */
+  PC_SYM_NOFOLLOW_PROCFD = 0x0800,
   PC_KEEP_HANDLE	= 0x00400000,
   PC_NO_ACCESS_CHECK	= 0x00800000
 };
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
index 3a7ecd2..b15fa0a 100644
--- a/winsup/cygwin/syscalls.cc
+++ b/winsup/cygwin/syscalls.cc
@@ -1368,6 +1368,7 @@ open (const char *unix_path, int flags, ...)
   va_list ap;
   mode_t mode = 0;
   fhandler_base *fh = NULL;
+  fhandler_base *fh_file = NULL;
 
   pthread_testcancel ();
 
@@ -1394,8 +1395,9 @@ open (const char *unix_path, int flags, ...)
 	 with a change in behavior that implements linux functionality:
 	 opening a tty should not automatically cause it to become the
 	 controlling tty for the process.  */
-      int opt = PC_OPEN | ((flags & (O_NOFOLLOW | O_EXCL))
-			   ?  PC_SYM_NOFOLLOW : PC_SYM_FOLLOW);
+      int opt = PC_OPEN | PC_SYM_NOFOLLOW_PROCFD;
+      opt |= (flags & (O_NOFOLLOW | O_EXCL)) ? PC_SYM_NOFOLLOW
+					     : PC_SYM_FOLLOW;
       if (!(flags & O_NOCTTY) && fd > 2 && myself->ctty != -2)
 	{
 	  flags |= O_NOCTTY;
@@ -1438,7 +1440,6 @@ open (const char *unix_path, int flags, ...)
 	     followed by an 8 byte unique hex number, followed by an 8 byte
 	     random hex number. */
 	  int64_t rnd;
-	  fhandler_base *fh_file;
 	  char *new_path;
 
 	  new_path = (char *) malloc (strlen (fh->get_name ())
@@ -1464,8 +1465,17 @@ open (const char *unix_path, int flags, ...)
 	  fh = fh_file;
 	}
 
-      if ((fh->is_fs_special () && fh->device_access_denied (flags))
-	  || !fh->open_with_arch (flags, mode & 07777))
+      if (fh->dev () == FH_PROCESSFD)
+	{
+	  /* Reopen file by descriptor */
+	  fh_file = fh->fd_reopen (flags);
+	  if (!fh_file)
+	    __leave;
+	  delete fh;
+	  fh = fh_file;
+	}
+      else if ((fh->is_fs_special () && fh->device_access_denied (flags))
+	       || !fh->open_with_arch (flags, mode & 07777))
 	__leave;		/* errno already set */
       fd = fh;
       if (fd <= 2)



More information about the Cygwin-cvs mailing list