[PATCH v3 2/3] Cygwin: readlinkat, fchownat: allow pathname to be empty
Corinna Vinschen
corinna-cygwin@cygwin.com
Fri Jan 17 10:22:00 GMT 2020
On Jan 16 20:50, Ken Brown wrote:
> 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 or fchownat call then operates on that
> symlink. In the case of fchownat, the call must specify the
> AT_EMPTY_PATH flag.
> ---
> winsup/cygwin/syscalls.cc | 40 ++++++++++++++++++++++++++++++++++-----
> 1 file changed, 35 insertions(+), 5 deletions(-)
>
> diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
> index 038a316db..3d87fd685 100644
> --- a/winsup/cygwin/syscalls.cc
> +++ b/winsup/cygwin/syscalls.cc
> @@ -4785,14 +4785,29 @@ 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))
> - __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. This is OK if dirfd refers
> + to a symlink that was opened with O_PATH and O_NOFOLLOW.
> + In this case, fchownat 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;
I think this is not quite right. Per the Linux man page of fchownat,
if AT_EMPTY_PATH is given, any file type is ok as dirfd:
AT_EMPTY_PATH (since Linux 2.6.39)
If pathname is an empty string, operate on the file referred to
by dirfd (which may have been obtained using the open(2) O_PATH
flag). In this case, dirfd can refer to any type of file, not
just a directory...
Additionally AT_FDCWD is allowed, too:
... If dirfd is AT_FDCWD, the call operates on
the current working directory.
> + return lchown (cfd->get_name (), uid, gid);
Instead of calling lchown, this code could also just tweak the flags
and fall through to the below chown_worker call, I think, just as in
readlinkat below:
strcpy (path, cfd->get_name ());
flags = AT_SYMLINK_NOFOLLOW;
> + }
> return chown_worker (path, (flags & AT_SYMLINK_NOFOLLOW)
> ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW, uid, gid);
> }
> @@ -4979,8 +4994,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
Thanks,
Corinna
--
Corinna Vinschen
Cygwin Maintainer
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://cygwin.com/pipermail/cygwin-patches/attachments/20200117/30391b86/attachment.sig>
More information about the Cygwin-patches
mailing list