summaryrefslogtreecommitdiffstats
path: root/PATCHES/0003-Windows-Follow-Posix-dir-exists-semantics-more-close.patch
blob: f4ce3411e146909e5e7ca095c2f69775e776d2f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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
@@ -198,6 +205,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.
 
@@ -227,6 +277,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