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)