delorie.com/archives/browse.cgi   search  
Mail Archives: cygwin/2006/02/22/11:00:51

X-Spam-Check-By: sourceware.org
From: ericblake AT comcast DOT net (Eric Blake)
To: Cygwin List <cygwin AT cygwin DOT com>
Subject: another instance of .. issues
Date: Wed, 22 Feb 2006 16:00:34 +0000
Message-Id: <022220061600.18471.43FC8AA2000A6AE70000482722073007930A050E040D0C079D0A@comcast.net>
Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm
List-Subscribe: <mailto:cygwin-subscribe AT cygwin DOT com>
List-Archive: <http://sourceware.org/ml/cygwin/>
List-Post: <mailto:cygwin AT cygwin DOT com>
List-Help: <mailto:cygwin-help AT cygwin DOT com>, <http://sourceware.org/ml/#faqs>
Sender: cygwin-owner AT cygwin DOT com
Mail-Followup-To: cygwin AT cygwin DOT com
Delivered-To: mailing list cygwin AT cygwin DOT com

CVS coreutils has recently provided emulation of the *at()
functions available in Solaris and recent glibc for other
platforms without them.  The *at functions are nice because
you can recurse through directories without changing
the current working directory, which adds a measure
of thread-safety previously not possible when using
[f]chdir (since the current working directory is per-process,
not per-thread).  For example,

dir = open("/tmp", O_RDONLY);
fd = openat(dir, "foo", O_RDONLY);

opens /tmp/foo, even though "foo" was a relative
pathname, without regards to getcwd().  Coreutils
emulates this on cygwin (and on Linux with older
glibc) by this slick trick:
openat(int dirfd, const char *name, int flags, ...) {
  char *buf /* alloca'd properly... */;
  int mode /* grabbed from va_arg properly ... */;
  sprintf(buf, "/proc/self/fd/%d/%s", dirfd, name);
  return open(buf, flags, mode);
}

Now for the problem.  Start with 'mkdir -p foo/bar', then
'touch foo/bar/file', and finally do 'rm -Rf foo'.  Since coreutils
now uses openat() and friends for directory traversal, the
recursive operation eventually gets file desriptor 3 open on
"foo", calls openat(3, "bar") which the emulation resolves to
open("/proc/self/fd/3/bar") to set the virtual directory
to foo/bar, then calls close(3) to avoid having too many open
file descriptors.  From there, rm empties foo/bar, then desires
to return back up the hierarchy to remove bar.  But it does so
by calling openat(4, ".."), which translates to
open("/proc/self/fd/4/..").  Since cygwin is improperly treating
this as open("/proc/self/fd") rather than dereferencing the
symlink and (re-)opening "foo", it triggers a sanity check in
rm complaining that the inode of foo has changed during
traversal, aborting the recursive operation:

$ rm --version | head -n1
rm (GNU coreutils) 6.0-cvs
$ mkdir -p foo/bar
$ touch foo/bar/file
$ rm -Rf foo
rm: FATAL: directory `foo' changed dev/ino
$ ls -R foo
foo:
bar

foo/bar:
$

I know that http://cygwin.com/acronyms/#PTC, and that this
won't be fixed for 1.5.20, but it is yet another datapoint
where blindly treating .. logically in cygwin path resolution
is violating POSIX requirements and thus breaking
assumptions being made by coreutils.  I will be able to
patch coreutils to work around this issue (coreutils does
have non-threadsafe fallback code that calls 'cwd=open(".");
fchdir(dirfd); open(name); fchdir(cwd); close(cwd)', triggered
on older Linux where /proc/self doesn't exist; my patch would
be to use this non-threadsafe fallback unconditionally on cygwin),
but it would be nice to have .. semantics working the same as in
Linux without having to hack the openat emulation fallback code,
and/or have openat() implemented directly in cygwin so that the
openat emulation of open("/proc/self/fd/4/..") is avoided (not to
mention more efficient by avoiding several other syscalls during
the emulation).

-- 
Eric Blake

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019