summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKen Brown <kbrown@cornell.edu>2021-02-25 07:54:10 -0500
committerKen Brown <kbrown@cornell.edu>2021-02-25 17:44:18 -0500
commit182ba1f022fb42b22fd2c7d925bf687227b2b3c7 (patch)
treee814587d0460934e55a46e42b2d4930b75b96005
parent425203384a6c3e19af782c86570740464423eb4d (diff)
downloadcygnal-182ba1f022fb42b22fd2c7d925bf687227b2b3c7.tar.gz
cygnal-182ba1f022fb42b22fd2c7d925bf687227b2b3c7.tar.bz2
cygnal-182ba1f022fb42b22fd2c7d925bf687227b2b3c7.zip
Cygwin: simplify linkat with AT_EMPTY_PATH
linkat(olddirfd, oldpath, oldname, newdirfd, newname, AT_EMPTY_PATH) is supposed to create a link to the file referenced by olddirfd if oldname is the empty string. Currently this is done via the /proc filesystem by converting the call to linkat(AT_FDCWD, "/proc/self/fd/<olddirfd>", newdirfd, newname, AT_SYMLINK_FOLLOW), which ultimately leads to a call to the appropriate fhandler's link method. Simplify this by using cygheap_fdget to obtain the fhandler directly.
-rw-r--r--winsup/cygwin/syscalls.cc24
1 files changed, 16 insertions, 8 deletions
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
index 460fe6801..6ba4f10f7 100644
--- a/winsup/cygwin/syscalls.cc
+++ b/winsup/cygwin/syscalls.cc
@@ -4962,6 +4962,8 @@ linkat (int olddirfd, const char *oldpathname,
int flags)
{
tmp_pathbuf tp;
+ fhandler_base *fh = NULL;
+
__try
{
if (flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH))
@@ -4970,21 +4972,25 @@ linkat (int olddirfd, const char *oldpathname,
__leave;
}
char *oldpath = tp.c_get ();
- /* AT_EMPTY_PATH with an empty oldpathname is equivalent to
-
- linkat(AT_FDCWD, "/proc/self/fd/<olddirfd>", newdirfd,
- newname, AT_SYMLINK_FOLLOW);
-
- Convert the request accordingly. */
if ((flags & AT_EMPTY_PATH) && oldpathname && oldpathname[0] == '\0')
{
+ /* Operate directly on olddirfd, which can be anything
+ except a directory. */
if (olddirfd == AT_FDCWD)
{
set_errno (EPERM);
__leave;
}
- __small_sprintf (oldpath, "/proc/%d/fd/%d", myself->pid, olddirfd);
- flags = AT_SYMLINK_FOLLOW;
+ cygheap_fdget cfd (olddirfd);
+ if (cfd < 0)
+ __leave;
+ if (cfd->pc.isdir ())
+ {
+ set_errno (EPERM);
+ __leave;
+ }
+ fh = cfd;
+ flags = 0; /* In case AT_SYMLINK_FOLLOW was set. */
}
else if (gen_full_path_at (oldpath, olddirfd, oldpathname))
__leave;
@@ -5003,6 +5009,8 @@ linkat (int olddirfd, const char *oldpathname,
}
strcpy (oldpath, old_name.get_posix ());
}
+ if (fh)
+ return fh->link (newpath);
return link (oldpath, newpath);
}
__except (EFAULT) {}