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

Attachment: signature.asc
Description: PGP signature

Reply via email to