Following Linux, allow the pathname argument to be empty if the AT_EMPTY_PATH is specified. In this case the dirfd argument can refer to any type of file, not just a directory, and the call operates on that file. In particular, dirfd can refer to a symlink that was opened with O_PATH and O_NOFOLLOW.
Add a new optional argument to gen_full_path_at to help implement this. --- winsup/cygwin/syscalls.cc | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 2be8693c9..1bc856268 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -4635,7 +4635,8 @@ pclose (FILE *fp) static int gen_full_path_at (char *path_ret, int dirfd, const char *pathname, - bool null_pathname_allowed = false) + bool null_pathname_allowed = false, + bool at_empty_path_flag = false) { /* Set null_pathname_allowed to true to allow GLIBC compatible behaviour for NULL pathname. Only used by futimesat. */ @@ -4644,20 +4645,25 @@ gen_full_path_at (char *path_ret, int dirfd, const char *pathname, set_errno (EFAULT); return -1; } + bool empty_path = false; if (pathname) { if (!*pathname) { - set_errno (ENOENT); - return -1; + empty_path = true; + if (!at_empty_path_flag) + { + set_errno (ENOENT); + return -1; + } } - if (strlen (pathname) >= PATH_MAX) + else if (strlen (pathname) >= PATH_MAX) { set_errno (ENAMETOOLONG); return -1; } } - if (pathname && isabspath (pathname)) + if (pathname && !empty_path && isabspath (pathname)) stpcpy (path_ret, pathname); else { @@ -4674,12 +4680,14 @@ gen_full_path_at (char *path_ret, int dirfd, const char *pathname, cygheap_fdget cfd (dirfd); if (cfd < 0) return -1; - if (!cfd->pc.isdir ()) + if (!empty_path && !cfd->pc.isdir ()) { set_errno (ENOTDIR); return -1; } p = stpcpy (path_ret, cfd->get_name ()); + if (empty_path) + return 0; } if (!p) { @@ -4785,13 +4793,14 @@ fchownat (int dirfd, const char *pathname, uid_t uid, gid_t gid, int flags) tmp_pathbuf tp; __try { - if (flags & ~AT_SYMLINK_NOFOLLOW) + if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) { set_errno (EINVAL); __leave; } char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) + if (gen_full_path_at (path, dirfd, pathname, false, + flags & AT_EMPTY_PATH)) __leave; return chown_worker (path, (flags & AT_SYMLINK_NOFOLLOW) ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW, uid, gid); @@ -4808,13 +4817,14 @@ fstatat (int dirfd, const char *__restrict pathname, struct stat *__restrict st, tmp_pathbuf tp; __try { - if (flags & ~AT_SYMLINK_NOFOLLOW) + if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) { set_errno (EINVAL); __leave; } char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) + if (gen_full_path_at (path, dirfd, pathname, false, + flags & AT_EMPTY_PATH)) __leave; path_conv pc (path, ((flags & AT_SYMLINK_NOFOLLOW) ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW) -- 2.21.0