diff --git a/ChangeLog b/ChangeLog index 91802ae3..2c8275ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2013-04-28 Eli Zaretskii + + * dir.c (local_stat) [WINDOWS32]: Use the wrapper on MS-Windows. + If the argument ends in "dir/.", make sure the parent dir exists + and is indeed a directory. Fixes Savannah bug #37065. + 2013-04-28 Paul Smith Implement a "per-job" output synchronization option. diff --git a/dir.c b/dir.c index 59b2a8c7..dc0ef763 100644 --- a/dir.c +++ b/dir.c @@ -1185,8 +1185,11 @@ ansi_free (void *p) /* On 64 bit ReliantUNIX (5.44 and above) in LFS mode, stat() is actually a * macro for stat64(). If stat is a macro, make a local wrapper function to * invoke it. + * + * On MS-Windows, stat() "succeeds" for foo/bar/. where foo/bar is a + * regular file; fix that here. */ -#ifndef stat +#if !defined(stat) && !defined(WINDOWS32) # ifndef VMS int stat (const char *path, struct stat *sbuf); # endif @@ -1196,6 +1199,23 @@ static int local_stat (const char *path, struct stat *buf) { int e; +#ifdef WINDOWS32 + size_t plen = strlen (path); + + /* Make sure the parent of "." exists and is a directory, not a + file. This is because 'stat' on Windows normalizes the argument + foo/. => foo without checking first that foo is a directory. */ + if (plen > 1 && path[plen - 1] == '.' + && (path[plen - 2] == '/' || path[plen - 2] == '\\')) + { + char parent[MAXPATHLEN]; + + strncpy (parent, path, plen - 2); + parent[plen - 2] = '\0'; + if (stat (parent, buf) < 0 || !_S_ISDIR (buf->st_mode)) + return -1; + } +#endif EINTRLOOP (e, stat (path, buf)); return e;