Re: [PATCH] Set errno to EEXIST if mkdir returns EACCES or EPERM

From: Junio C Hamano <junkio@cox.net>
Date: 2006-01-31 07:33:51
Alex Riesen <raa.lkml@gmail.com> writes:

> ... and the directory already exists. I.E. Cygwin is such
> a case: mkdir fail for mounts which reference directly
> to windows mounts ("drives").
>
> ---
>
> The discussion, which ended up with this patch can be read
> here: http://www.cygwin.com/ml/cygwin/2006-01/msg01276.html
>
> BTW, there is this:
> http://www.cygwin.com/ml/cygwin/2006-01/msg01380.html
> So this patch will probably be not needed soon.

Thanks for all the background information.  Although it is very
tempting for me to adopt your patch as an easy way out, I would
feel *dirty* if I did so.

Eric Blake is right in his argument in that thread.  Our code
should not depend on the Linux EEXIST behaviour.  The reason
Cygwin folks want to be as close to Linux is to work around
application bugs like this.  Which is a valid concern for them,
but it does not mean the application has license to depend on
Linux behaviour.

We, as an application, should take a different attitude -- we
should fix things to make things portable, not work things
around, if both are equally easily doable.  I do not object to
using a function that has a semantics Linux mkdir() gives us,
but calling that mkdir() does not feel quite right.  

Also the wrapper implies that in our future use of mkdir() we
cannot tell the difference between EEXIST and other errors if we
later wanted to.

So let's look at our existing uses first:

apply.c:1577:		if (mkdir(buf, 0777) < 0) {
apply.c-1578-			if (errno != EEXIST)
apply.c-1579-				break;
apply.c-1580-		}
apply.c-1581-	}

This is "we want to see buf directory exist and we would create
one if there isn't".  Instead of checking errno, we could stat
as your patch does.

entry.c:15:		if (mkdir(buf, 0777)) {
entry.c-16-			if (errno == EEXIST) {
entry.c-17-				struct stat st;
entry.c:18:				if (len > state->base_dir_len && state->force && !unlink(buf) && !mkdir(buf, 0777))
entry.c-19-					continue;
entry.c-20-				if (!stat(buf, &st) && S_ISDIR(st.st_mode))
entry.c-21-					continue; /* ok */
entry.c-22-			}

This is a bit more involved. "we want to have buf directory and
we would create it, and if force is given to have something
under that directory, we would even unlink the nondirectory that
sits there".

I asked "git-grep" where else we use mkdir(), and all other
users want the semantics of the apply.c use quoted above.

So I'd rather see us create a generic helper function like this:

	int make_directory(const char *path, int force)
        {
		struct stat st;
		int mkdir_errno;
		if (!mkdir(path, 0777))
			return 0;
                mkdir_errno = errno;
		if (!lstat(path, &st) && S_ISDIR(st.st_mode))
                	return 0;
                if (!force) {
		bad:
                	errno = mkdir_errno;
                        return -1;
                }
                if (!unlink(path, &st) && !mkdir(path, 0777))
                	return 0;
                /* we might have failed to unlink an existing symlink
		 * which happens to point at an existing directory; that
                 * directory is not what we want here.
                 */
		if (!lstat(path, &st) && S_ISDIR(st.st_mode))
                	return 0;
		goto bad;
        }

and have current callers of mkdir() use it, regardless of the
platform.  It may not worth saving mkdir_errno, though.

Then everybody but entry.c one would say force=0, and entry.c
one passes force appropriately using the condition it uses in
its current if() statement.

-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Received on Tue Jan 31 07:34:31 2006

This archive was generated by hypermail 2.1.8 : 2006-01-31 07:34:42 EST