summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--winsup/cygwin/fhandler.cc7
-rw-r--r--winsup/cygwin/fhandler.h2
-rw-r--r--winsup/cygwin/fhandler_process.cc78
-rw-r--r--winsup/cygwin/path.cc4
-rw-r--r--winsup/cygwin/path.h2
-rw-r--r--winsup/cygwin/syscalls.cc20
6 files changed, 108 insertions, 5 deletions
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 01afdb29b..4ecbf61ee 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 0ead0d90c..74eb1d7cb 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 24ef7d00c..0fad96fce 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 df11d5339..b039801d5 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 1e3095ec9..981269995 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 3a7ecd218..b15fa0aa7 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)