diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 74eb1d7cb..1c751c110 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -2555,6 +2555,7 @@ class fhandler_process: public fhandler_proc int __reg3 readdir (DIR *, dirent *); int open (int flags, mode_t mode = 0); virtual fhandler_base *fd_reopen (int); + int __reg2 link (const char *); int __reg2 fstat (struct stat *buf); bool fill_filebuf (); diff --git a/winsup/cygwin/fhandler_process.cc b/winsup/cygwin/fhandler_process.cc index 0fad96fce..a52314810 100644 --- a/winsup/cygwin/fhandler_process.cc +++ b/winsup/cygwin/fhandler_process.cc @@ -399,6 +399,88 @@ err_out: return fh; } +int +fhandler_process::link (const char *newpath) +{ + const char *path; + int fd; + char *e; + + path = get_name () + proc_len + 1; + pid = atoi (path); + while (*path != 0 && !isdirsep (*path)) + path++; + if (*path == 0) + goto err_out; + + virt_tab_t *entry; + entry = virt_tab_search (path + 1, true, process_tab, PROCESS_LINK_COUNT); + if (!entry || entry->fhandler != FH_PROCESSFD) + goto err_out; + if (path[3] != '/' || path[4] == '\0') + goto err_out; + + fd = strtoul (path + 4, &e, 10); + if (fd < 0 || e == path + 4 || (*e != '/' && *e != '\0')) + goto err_out; + if (pid == myself->pid) + { + cygheap_fdget cfd (fd); + if (cfd < 0) + goto err_out; + return cfd->link (newpath); + } + else + { + HANDLE proc; + size_t size; + void *buf; + path_conv pc; + HANDLE hdl; + fhandler_base *fh = NULL; + int ret = -1; + + pinfo p (pid); + if (!p) + { + set_errno (ENOENT); + return -1; + } + if (!(proc = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId))) + goto err_out; + buf = p->file_pathconv (fd, size); + if (size == 0) + { + set_errno (EPERM); + goto err_out_close_proc; + } + hdl = pc.deserialize (buf); + if (!DuplicateHandle (proc, hdl, GetCurrentProcess (), &hdl, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + __seterrno (); + goto err_out_close_proc; + } + fh = build_fh_pc (pc); + if (!fh) + goto err_out_close_dup; + + fh->set_io_handle (hdl); + ret = fh->link (newpath); + delete fh; + +err_out_close_dup: + CloseHandle (hdl); +err_out_close_proc: + CloseHandle (proc); + return ret; + } + +err_out: + set_errno (EPERM); + return -1; +} + struct process_fd_t { const char *path; _pinfo *p; diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 2fb4bc442..9f43512b2 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -1484,6 +1484,10 @@ open (const char *unix_path, int flags, ...) else if ((fh->is_fs_special () && fh->device_access_denied (flags)) || !fh->open_with_arch (flags, mode & 07777)) __leave; /* errno already set */ + /* Move O_TMPFILEs to the bin to avoid blocking the parent dir. */ + if ((flags & O_TMPFILE) && !fh->pc.isremote ()) + try_to_bin (fh->pc, fh->get_handle (), DELETE, + FILE_OPEN_FOR_BACKUP_INTENT); fd = fh; if (fd <= 2) set_std_handle (fd); @@ -4791,7 +4795,9 @@ linkat (int olddirfd, const char *oldpathname, __leave; if (flags & AT_SYMLINK_FOLLOW) { - path_conv old_name (oldpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes); + path_conv old_name (oldpath, + PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_PROCFD | PC_POSIX, + stat_suffixes); if (old_name.error) { set_errno (old_name.error);