[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