Re: [PATCH 3/3] Cygwin: fchownat and fstatat: support the AT_EMPTY_PATH flag
On 12/28/2019 2:52 PM, Ken Brown wrote: > 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. I don't like the way I did this, at least for fstatat. If dirfd was opened with O_PATH, I've ignored the purpose of that flag. I'll rethink this and possibly submit a v2. Ken
[PATCH v2 1/3] Cygwin: allow opening a symlink with O_PATH | O_NOFOLLOW
Up to now, opening a symlink with O_NOFOLLOW fails with ELOOP. Following Linux, allow this to succeed if O_PATH is also specified. --- winsup/cygwin/syscalls.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 20126ce10..038a316db 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -1470,7 +1470,7 @@ open (const char *unix_path, int flags, ...) if (!(fh = build_fh_name (unix_path, opt, stat_suffixes))) __leave;/* errno already set */ - if ((flags & O_NOFOLLOW) && fh->issymlink ()) + if ((flags & O_NOFOLLOW) && fh->issymlink () && !(flags & O_PATH)) { set_errno (ELOOP); __leave; -- 2.21.0
[PATCH v2 2/3] Cygwin: readlinkat: allow pathname to be empty
Following Linux, allow the pathname argument to be an empty string, provided the dirfd argument refers to a symlink opened with O_PATH and O_NOFOLLOW. The readlinkat call then operates on that symlink. --- winsup/cygwin/syscalls.cc | 19 +-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 038a316db..2be8693c9 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -4979,8 +4979,23 @@ readlinkat (int dirfd, const char *__restrict pathname, char *__restrict buf, __try { char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - __leave; + int res = gen_full_path_at (path, dirfd, pathname); + if (res) + { + if (errno != ENOENT) + __leave; + /* pathname is an empty string. This is OK if dirfd refers +to a symlink that was opened with O_PATH and O_NOFOLLOW. +In this case, readlinkat operates on the symlink. */ + cygheap_fdget cfd (dirfd); + if (cfd < 0) + __leave; + if (!(cfd->issymlink () + && cfd->get_flags () & O_PATH + && cfd->get_flags () & O_NOFOLLOW)) + __leave; + strcpy (path, cfd->get_name ()); + } return readlink (path, buf, bufsize); } __except (EFAULT) {} -- 2.21.0
[PATCH v2 0/3] Support opening a symlink with O_PATH | O_NOFOLLOW
Currently, opening a symlink with O_NOFOLLOW fails with ELOOP. Following Linux, the first patch in this series allows the call to succeed if O_PATH is also specified. According to the Linux man page for 'open', the file descriptor returned by the call should be usable as the dirfd argument in calls to fstatat and readlinkat with an empty pathname, to have the calls operate on the symbolic link. The second and third patches achieve this. For fstatat, we do this by adding support for the AT_EMPTY_PATH flag. Note: The man page mentions fchownat and linkat also. linkat already supports the AT_EMPTY_PATH flag, so nothing needs to be done. But I don't understand how this could work for fchownat, because fchown fails with EBADF if its fd argument was opened with O_PATH. So I haven't touched fchownat. Am I missing something? Ken Brown (3): Cygwin: allow opening a symlink with O_PATH | O_NOFOLLOW Cygwin: readlinkat: allow pathname to be empty Cygwin: fstatat: support the AT_EMPTY_PATH flag winsup/cygwin/syscalls.cc | 40 +-- 1 file changed, 34 insertions(+), 6 deletions(-) -- 2.21.0
[PATCH v2 3/3] Cygwin: fstatat: support the AT_EMPTY_PATH flag
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. --- winsup/cygwin/syscalls.cc | 19 --- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 2be8693c9..9b7d6dbfd 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -4808,14 +4808,27 @@ 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)) - __leave; + int res = gen_full_path_at (path, dirfd, pathname); + if (res) + { + if (!(errno == ENOENT && (flags & AT_EMPTY_PATH))) + __leave; + /* pathname is an empty string. Operate on dirfd. */ + if (dirfd == AT_FDCWD) + { + cwdstuff::cwd_lock.acquire (); + strcpy (path, cygheap->cwd.get_posix ()); + cwdstuff::cwd_lock.release (); + } + else + return fstat (dirfd, st); + } path_conv pc (path, ((flags & AT_SYMLINK_NOFOLLOW) ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW) | PC_POSIX | PC_KEEP_HANDLE, stat_suffixes); -- 2.21.0