POSIX requires stat("foo/", &st) to fail if "foo" isn't a directory or
a symbolic link to a directory. See ENOTDIR in [1].

The statXX functions, that are used with MSVCRT, already have code
to remove a trailing slash. Extend it to reject non-directories with
ENOTDIR if a trailing slash was removed.

These functions aren't used with UCRT because UCRT handles trailing
slashes better than MSVCRT. One minor issue with UCRT is that "foo/"
results in ENOENT instead of ENOTDIR. This commit doesn't fix it.

Preserve errno when calling free().

[1] https://pubs.opengroup.org/onlinepubs/9799919799/functions/fstatat.html
---
 mingw-w64-crt/Makefile.am                     |  2 +-
 mingw-w64-crt/stdio/__mingw_fix_stat.h        |  2 ++
 mingw-w64-crt/stdio/__mingw_fix_stat_finish.c | 36 +++++++++++++++++++
 mingw-w64-crt/stdio/msvcr110plus_stat32.c     |  4 +--
 mingw-w64-crt/stdio/msvcr110plus_stat64i32.c  |  4 +--
 mingw-w64-crt/stdio/msvcr110plus_wstat32.c    |  4 +--
 mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c |  4 +--
 mingw-w64-crt/stdio/msvcr110pre_stat32.c      |  3 +-
 mingw-w64-crt/stdio/msvcr110pre_stat64i32.c   |  3 +-
 mingw-w64-crt/stdio/msvcr110pre_wstat32.c     |  3 +-
 mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c  |  3 +-
 mingw-w64-crt/stdio/stat32i64.c               |  4 +--
 mingw-w64-crt/stdio/stat64.c                  |  4 +--
 mingw-w64-crt/stdio/wstat32i64.c              |  4 +--
 mingw-w64-crt/stdio/wstat64.c                 |  4 +--
 15 files changed, 51 insertions(+), 33 deletions(-)
 create mode 100644 mingw-w64-crt/stdio/__mingw_fix_stat_finish.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index c8812f2d5..4adfbff3f 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -1103,7 +1103,7 @@ src_libmingwex=\
   stdio/asprintf.c \
   stdio/fopen64.c          stdio/fseeko32.c          stdio/fseeko64.c          
                         stdio/ftello.c          \
   stdio/ftello64.c         stdio/ftruncate64.c       stdio/lltoa.c            
stdio/lltow.c             stdio/lseek64.c         \
-  stdio/__mingw_fix_stat.h \
+  stdio/__mingw_fix_stat.h stdio/__mingw_fix_stat_finish.c \
   stdio/__mingw_fix_stat_path.c stdio/__mingw_fix_wstat_path.c \
   \
   stdio/mingw_pformat.h    mingw_sformat.h           mingw_swformat.h \
diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat.h 
b/mingw-w64-crt/stdio/__mingw_fix_stat.h
index 5aad9efb6..909606204 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_stat.h
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat.h
@@ -9,5 +9,7 @@
 
 char* __mingw_fix_stat_path (const char* _path);
 wchar_t* __mingw_fix_wstat_path (const wchar_t* _path);
+int __mingw_fix_stat_finish(int ret, const void *orig_path, void *used_path,
+                            unsigned short mode);
 
 #endif
diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat_finish.c 
b/mingw-w64-crt/stdio/__mingw_fix_stat_finish.c
new file mode 100644
index 000000000..8b7a128c2
--- /dev/null
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat_finish.c
@@ -0,0 +1,36 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "__mingw_fix_stat.h"
+
+int __mingw_fix_stat_finish(int ret, const void *orig_path, void *used_path,
+                            unsigned short mode)
+{
+  /*
+   * If the original pathname and used pathname differ, it means that
+   * __mingw_fix_stat_path or __mingw_fix_wstat_path had to allocate
+   * a temporary buffer and remove a trailing directory separator.
+   * In this case the temporary allocation has to be freed, and the
+   * stat function succeeds only if the pathname was a directory.
+   */
+  if (orig_path != used_path) {
+    /* Save errno because we call free. */
+    int saved_errno = errno;
+    free(used_path);
+
+    if (ret == 0 && !S_ISDIR(mode)) {
+      ret = -1;
+      saved_errno = ENOTDIR;
+    }
+
+    errno = saved_errno;
+  }
+
+  return ret;
+}
diff --git a/mingw-w64-crt/stdio/msvcr110plus_stat32.c 
b/mingw-w64-crt/stdio/msvcr110plus_stat32.c
index f4cb9567a..c31527fb1 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_stat32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_stat32.c
@@ -15,9 +15,7 @@ int __cdecl stat32(const char *_Filename, struct _stat32 
*_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _stat32(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat32))(const char *, struct _stat32 *) = 
stat32;
 
diff --git a/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c 
b/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c
index 7ccd23b34..7df90a001 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c
@@ -15,9 +15,7 @@ int __cdecl stat64i32(const char *_Filename, struct 
_stat64i32 *_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _stat64i32(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat64i32))(const char *, struct _stat64i32 
*) = stat64i32;
 
diff --git a/mingw-w64-crt/stdio/msvcr110plus_wstat32.c 
b/mingw-w64-crt/stdio/msvcr110plus_wstat32.c
index 3c6576ae0..15f55cd8b 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_wstat32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_wstat32.c
@@ -15,9 +15,7 @@ int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 
*_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _wstat32(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat32))(const wchar_t *, struct _stat32 *) 
= wstat32;
 
diff --git a/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c 
b/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c
index 7fc681b2a..c2f1e32cf 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c
@@ -15,9 +15,7 @@ int __cdecl wstat64i32(const wchar_t *_Filename, struct 
_stat64i32 *_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _wstat64i32(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat64i32))(const wchar_t *, struct 
_stat64i32 *) = wstat64i32;
 
diff --git a/mingw-w64-crt/stdio/msvcr110pre_stat32.c 
b/mingw-w64-crt/stdio/msvcr110pre_stat32.c
index fe3cb20d6..63d753e61 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_stat32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_stat32.c
@@ -23,8 +23,7 @@ int __cdecl stat32(const char *_Filename, struct _stat32 
*_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _stat32i64(_path, &st);
-  if (_path != _Filename)
-    free(_path);
+  ret = __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
   if (ret != 0)
     return ret;
   if (st.st_size > UINT32_MAX) {
diff --git a/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c 
b/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c
index d87a14f54..6741c9b65 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c
@@ -23,8 +23,7 @@ int __cdecl stat64i32(const char *_Filename, struct 
_stat64i32 *_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _stat64(_path, &st);
-  if (_path != _Filename)
-    free(_path);
+  ret = __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
   if (ret != 0)
     return ret;
   if (st.st_size > UINT32_MAX) {
diff --git a/mingw-w64-crt/stdio/msvcr110pre_wstat32.c 
b/mingw-w64-crt/stdio/msvcr110pre_wstat32.c
index 4fc02ee0f..4e6c92299 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_wstat32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_wstat32.c
@@ -23,8 +23,7 @@ int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 
*_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _wstat32i64(_path, &st);
-  if (_path != _Filename)
-    free(_path);
+  ret = __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
   if (ret != 0)
     return ret;
   if (st.st_size > UINT32_MAX) {
diff --git a/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c 
b/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c
index 21c10b58b..a94703ea0 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c
@@ -23,8 +23,7 @@ int __cdecl wstat64i32(const wchar_t *_Filename, struct 
_stat64i32 *_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _wstat64(_path, &st);
-  if (_path != _Filename)
-    free(_path);
+  ret = __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
   if (ret != 0)
     return ret;
   if (st.st_size > UINT32_MAX) {
diff --git a/mingw-w64-crt/stdio/stat32i64.c b/mingw-w64-crt/stdio/stat32i64.c
index b8d154605..0b0c5cde9 100644
--- a/mingw-w64-crt/stdio/stat32i64.c
+++ b/mingw-w64-crt/stdio/stat32i64.c
@@ -15,8 +15,6 @@ int __cdecl stat32i64(const char *_Filename, struct 
_stat32i64 *_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _stat32i64(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat32i64))(const char *, struct _stat32i64 
*) = stat32i64;
diff --git a/mingw-w64-crt/stdio/stat64.c b/mingw-w64-crt/stdio/stat64.c
index 687135f3e..b1f5b4309 100644
--- a/mingw-w64-crt/stdio/stat64.c
+++ b/mingw-w64-crt/stdio/stat64.c
@@ -14,8 +14,6 @@ int __cdecl stat64(const char *_Filename, struct stat64 
*_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _stat64(_path, (struct _stat64 *)_Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat64))(const char *, struct stat64 *) = 
stat64;
diff --git a/mingw-w64-crt/stdio/wstat32i64.c b/mingw-w64-crt/stdio/wstat32i64.c
index 2dc34b8f9..98dcaf11f 100644
--- a/mingw-w64-crt/stdio/wstat32i64.c
+++ b/mingw-w64-crt/stdio/wstat32i64.c
@@ -15,8 +15,6 @@ int __cdecl wstat32i64(const wchar_t *_Filename, struct 
_stat32i64 *_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _wstat32i64(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat32i64))(const wchar_t *, struct 
_stat32i64 *) = wstat32i64;
diff --git a/mingw-w64-crt/stdio/wstat64.c b/mingw-w64-crt/stdio/wstat64.c
index 8cc58521a..76d547435 100644
--- a/mingw-w64-crt/stdio/wstat64.c
+++ b/mingw-w64-crt/stdio/wstat64.c
@@ -14,8 +14,6 @@ int __cdecl wstat64(const wchar_t *_Filename, struct stat64 
*_Stat)
   if (_path == NULL && _Filename != NULL)
     return -1;
   int ret = _wstat64(_path, (struct _stat64 *)_Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat64))(const wchar_t *, struct stat64 *) = 
wstat64;
-- 
2.49.0



_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to