Subject: Re: cygwin's unlink
From: Steve Jorgensen <[email-masked]>
To: "13mb80000-HallM(10053584)37x10" <[email-masked]>
Cc: [email-masked]
Date: Wed, 27 Oct 1999 12:27:28 -0600 (MDT)

> On Tue, Oct 26, 1999 at 02:01:10PM -0600, Steve Jorgensen wrote:
> >Anyway, in the case where the file is truely a directory, under all
> >unix systems we've tried, unlink returns and error, and we proceed with
> >the recursive directory removal, but under cygwin it returns success
> >(even though nothing is removed), and our code returns success without
> >removing the directory.  Is this a limitation in the underlying windows
> >unlink, or a bug in cygwin?

> From: Chris Faylor <[email-masked]>
> > When I try this I get a "Permission denied" error.  It should be a
> > "Is a directory error" though.  I've changed the sources here so
> > this should be fixed in the next snapshot.

> Finally,  J. J. Farrell <[email-masked]> says:
> > Before you fix that (he says, probably too late) ...
> > 
> > This one's a bit complex. First off: under POSIX.1 it is
> > implementation-defined whether or not unlink() works on
> > directories. This means that Steve's algorithm is wrong,
> > since what he saw as a bug in cygwin's unlink() is perfectly
> > valid behaviour for a POSIX.1 system. The fact that POSIX
> > allows it suggests that at least one UNIX implementation
> > behaved that way when POSIX.1 was drawn up, and future ones
> > are allowed to if they wish (though rmdir() is the preferred
> > interface).
> > 
> > Secondly: in the case where the implementation does not
> > support unlink() on directories, the required error value
> > is EPERM which would result in a "Permission denied" error.
> > From the XPG3 documentation (which is aligned with POSIX.1):
> > 
> >  [EPERM] The file named by path is a directory, and either
> >          the calling process does not have appropriate
> >          privileges, or the implementation prohibits using
> >          unlink() on directories.

> It appears that unlink() applied to a directory does not work (which is
> consistent with POSIX.1), but returns success.  It seems that POSIX.1
> requires EPERM to be returned if unlink() does not work, so Steve's
> algorithm relying on an unsuccessful unlink() of a directory returning
> an error is probably OK, although perhaps it would be more robust to
> do a stat() first to determine if it is a directory.

	The algorithm is fine we're not trying to use unlink to remove
	directories, just to remove symbolic links before we recursively
	follow it.  Stat already reported it as a directory
	(stat doesn't know about symbolic links), so this call to unlink
	on the directory would only work if the directory is a symbolic
	link to a directory.  This allows the algorithm to avoid following
	symbolic links (which can be dangerous to your file system) and
	keeps us from having to use the less portable lstat (which does
	understand symbolic links) to determine the real file type.

> A historical note as to why I think that POSIX.1 allows unlink() to maybe
> work on directories, and the reason for the EPERM error (instead of EISDIR
> which might seem more sensible) is that originally Unix did not have a
> rmdir() system call.  Directories were removed by the /bin/rmdir command,
> which was suid to root.  As root, it was possible to use the unlink() call
> to remove a directory.  Of course, you had to remove "directory/.." first,
> to keep the reference count of the parent directory correct, and to ensure
> that there were no files in the directory being removed.  So, since unlink()
> would simply remove the hard link, but did not adjust reference counts in
> the filesystem, it was not safe to let just anyone make the call, so if
> you weren't root, you got the EPERM error.

> Making a directory (since there was no mkdir() system call) was done via a
> call to mknod() to create a directory type file, then link() calls to create
> "." and ".." in the new directory.

> This meant that to create or delete a directory, a normal user program had to
> do a fork() and exec() of /bin/mkdir or /bin/rmdir, which was somewhat
> inconvenient, but because these programs made several system calls to
> perform their tasks, there were race conditions that could occur with other
> processes creating files in a directory /bin/rmdir was removing and had
> already verified was empty, etc.  By making mkdir() and rmdir() system calls,
> they can be made atomic removing race conditions and making the file system
> a little more stable.

-----------------------------------------------------------
Steven Jorgensen      [email-masked]	    [email-masked]
------------------------------+----------------------------
Khoral Research Inc.          | PHONE: (505) 837-6500
6200 Uptown Blvd, Suite 200   | FAX:   (505) 881-3842
Albuquerque, NM 87110         | URL: http://www.khoral.com/
-----------------------------------------------------------

--
Want to unsubscribe from this list?
Send a message to [email-masked]