blob: 2cbe21f58dcf047e6b335cd278eb58015d360d1b [file] [log] [blame]
From 9f49390e2cd9085ca1cc03906a146861dbe8135f Mon Sep 17 00:00:00 2001
From: Ray Donnelly <mingw.android@gmail.com>
Date: Wed, 5 Aug 2015 23:36:07 +0100
Subject: [PATCH 03/15] Windows: Follow Posix dir-exists semantics more closely
Make Windows behave the same as Posix in the consideration
of whether folder "/doesnt-exist/.." is a valid
path. In Posix, it isn't.
A concrete instance of when this matters is when cross
compiling GNU/Linux glibc on Windows.
---
libcpp/files.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/libcpp/files.c b/libcpp/files.c
index ea2cc23..ac17272 100644
--- a/libcpp/files.c
+++ b/libcpp/files.c
@@ -30,6 +30,13 @@ along with this program; see the file COPYING3. If not see
#include "md5.h"
#include <dirent.h>
+/* Needed for stat_st_mode_symlink below */
+#if defined(_WIN32)
+# include <windows.h>
+# define S_IFLNK 0xF000
+# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+
/* Variable length record files on VMS will have a stat size that includes
record control characters that won't be included in the read size. */
#ifdef VMS
@@ -200,6 +207,49 @@ static int pchf_save_compare (const void *e1, const void *e2);
static int pchf_compare (const void *d_p, const void *e_p);
static bool check_file_against_entries (cpp_reader *, _cpp_file *, bool);
+#if defined(_WIN32)
+
+static int stat_st_mode_symlink (char const* path, struct stat* buf)
+{
+ WIN32_FILE_ATTRIBUTE_DATA attr;
+ memset(buf,0,sizeof(*buf));
+ int err = GetFileAttributesExA (path, GetFileExInfoStandard, &attr) ? 0 : 1;
+ if (!err)
+ {
+ WIN32_FIND_DATAA finddata;
+ HANDLE h = FindFirstFileA (path, &finddata);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ FindClose (h);
+ if ((finddata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (finddata.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
+ buf->st_mode = S_IFLNK;
+ else if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ buf->st_mode = S_IFDIR;
+ else if (finddata.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
+ buf->st_mode = S_IFDIR;
+ else
+ buf->st_mode = S_IFREG;
+ buf->st_mode |= S_IREAD;
+ if (!(finddata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
+ buf->st_mode |= S_IWRITE;
+ }
+ else
+ {
+ buf->st_mode = S_IFDIR;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+#else
+
+#define stat_st_mode_symlink (_name, _buf) stat ((_name), (_buf))
+
+#endif
+
+
/* Given a filename in FILE->PATH, with the empty string interpreted
as <stdin>, open it.
@@ -229,6 +279,43 @@ open_file (_cpp_file *file)
}
else
file->fd = open (file->path, O_RDONLY | O_NOCTTY | O_BINARY, 0666);
+#if defined(_WIN32) || defined(__CYGWIN__)
+ /* Windows and Posix differ in the face of paths of the form:
+ nonexistantdir/.. in that Posix will return ENOENT whereas
+ Windows won't care that we stepped into a non-existant dir
+ Only do these slow checks if ".." appears in file->path.
+ Cygwin also suffers from the same problem (but doesn't need
+ a new stat function):
+ http://cygwin.com/ml/cygwin/2013-05/msg00222.html
+ */
+ if (file->fd > 0)
+ {
+ char filepath[MAX_PATH];
+ strncpy (filepath, file->path, sizeof(filepath) - 1);
+ char* dirsep = &filepath[0];
+ while ( (dirsep = strchr (dirsep, '\\')) != NULL)
+ *dirsep = '/';
+ if (strstr(file->path, "/../"))
+ {
+ dirsep = &filepath[0];
+ char dirsepc;
+ /* Check each directory in the chain. */
+ while ( (dirsep = strpbrk (dirsep, "\\/")) != NULL)
+ {
+ dirsepc = *(++dirsep);
+ *dirsep = '\0';
+ if (stat_st_mode_symlink (filepath, &file->st) == -1)
+ {
+ *dirsep = dirsepc;
+ close (file->fd);
+ file->fd = -1;
+ return false;
+ }
+ *dirsep++ = dirsepc;
+ }
+ }
+ }
+#endif
if (file->fd != -1)
{
--
2.8.1