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
signature.asc
Description: PGP signature