1 /*
2 ** Copyright (c) 2006 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Author contact information:
13 ** drh@hwaci.com
14 ** http://www.hwaci.com/drh/
15 **
16 *******************************************************************************
17 **
18 ** This file implements several non-trivial file handling wrapper functions
19 ** on Windows using the Win32 API.
20 */
21 #include "config.h"
22 #ifdef _WIN32
23 /* This code is for win32 only */
24 #include <sys/stat.h>
25 #include <windows.h>
26 #include "winfile.h"
27
28 #ifndef LABEL_SECURITY_INFORMATION
29 # define LABEL_SECURITY_INFORMATION (0x00000010L)
30 #endif
31
32 /*
33 ** Fill stat buf with information received from stat() or lstat().
34 ** lstat() is called on Unix if eFType is RepoFile and the allow-symlinks
35 ** setting is on. But as windows does not support symbolic links, the
36 ** eFType parameter is ignored here.
37 */
win32_stat(const wchar_t * zFilename,struct fossilStat * buf,int eFType)38 int win32_stat(const wchar_t *zFilename, struct fossilStat *buf, int eFType){
39 WIN32_FILE_ATTRIBUTE_DATA attr;
40 int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr);
41 if( rc ){
42 ULARGE_INTEGER ull;
43 ull.LowPart = attr.ftLastWriteTime.dwLowDateTime;
44 ull.HighPart = attr.ftLastWriteTime.dwHighDateTime;
45 buf->st_mode = (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
46 S_IFDIR : S_IFREG;
47 buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow;
48 buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL;
49 }
50 return !rc;
51 }
52
53 /*
54 ** Wrapper around the access() system call. This code was copied from Tcl
55 ** 8.6 and then modified.
56 */
win32_access(const wchar_t * zFilename,int flags)57 int win32_access(const wchar_t *zFilename, int flags){
58 int rc = 0;
59 PSECURITY_DESCRIPTOR pSd = NULL;
60 unsigned long size = 0;
61 PSID pSid = NULL;
62 BOOL sidDefaulted;
63 BOOL impersonated = FALSE;
64 SID_IDENTIFIER_AUTHORITY unmapped = {{0, 0, 0, 0, 0, 22}};
65 GENERIC_MAPPING genMap;
66 HANDLE hToken = NULL;
67 DWORD desiredAccess = 0, grantedAccess = 0;
68 BOOL accessYesNo = FALSE;
69 PPRIVILEGE_SET pPrivSet = NULL;
70 DWORD privSetSize = 0;
71 DWORD attr = GetFileAttributesW(zFilename);
72
73 if( attr==INVALID_FILE_ATTRIBUTES ){
74 /*
75 * File might not exist.
76 */
77
78 if( GetLastError()!=ERROR_SHARING_VIOLATION ){
79 rc = -1; goto done;
80 }
81 }
82
83 if( flags==F_OK ){
84 /*
85 * File exists, nothing else to check.
86 */
87
88 goto done;
89 }
90
91 if( (flags & W_OK)
92 && (attr & FILE_ATTRIBUTE_READONLY)
93 && !(attr & FILE_ATTRIBUTE_DIRECTORY) ){
94 /*
95 * The attributes say the file is not writable. If the file is a
96 * regular file (i.e., not a directory), then the file is not
97 * writable, full stop. For directories, the read-only bit is
98 * (mostly) ignored by Windows, so we can't ascertain anything about
99 * directory access from the attrib data.
100 */
101
102 rc = -1; goto done;
103 }
104
105 /*
106 * It looks as if the permissions are ok, but if we are on NT, 2000 or XP,
107 * we have a more complex permissions structure so we try to check that.
108 * The code below is remarkably complex for such a simple thing as finding
109 * what permissions the OS has set for a file.
110 */
111
112 /*
113 * First find out how big the buffer needs to be.
114 */
115
116 GetFileSecurityW(zFilename,
117 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
118 DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
119 0, 0, &size);
120
121 /*
122 * Should have failed with ERROR_INSUFFICIENT_BUFFER
123 */
124
125 if( GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
126 /*
127 * Most likely case is ERROR_ACCESS_DENIED, which we will convert to
128 * EACCES - just what we want!
129 */
130
131 rc = -1; goto done;
132 }
133
134 /*
135 * Now size contains the size of buffer needed.
136 */
137
138 pSd = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(), 0, size);
139
140 if( pSd==NULL ){
141 rc = -1; goto done;
142 }
143
144 /*
145 * Call GetFileSecurity() for real.
146 */
147
148 if( !GetFileSecurityW(zFilename,
149 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
150 DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
151 pSd, size, &size) ){
152 /*
153 * Error getting owner SD
154 */
155
156 rc = -1; goto done;
157 }
158
159 /*
160 * As of Samba 3.0.23 (10-Jul-2006), unmapped users and groups are
161 * assigned to SID domains S-1-22-1 and S-1-22-2, where "22" is the
162 * top-level authority. If the file owner and group is unmapped then
163 * the ACL access check below will only test against world access,
164 * which is likely to be more restrictive than the actual access
165 * restrictions. Since the ACL tests are more likely wrong than
166 * right, skip them. Moreover, the unix owner access permissions are
167 * usually mapped to the Windows attributes, so if the user is the
168 * file owner then the attrib checks above are correct (as far as they
169 * go).
170 */
171
172 if( !GetSecurityDescriptorOwner(pSd, &pSid, &sidDefaulted) ||
173 memcmp(GetSidIdentifierAuthority(pSid), &unmapped,
174 sizeof(SID_IDENTIFIER_AUTHORITY))==0 ){
175 goto done; /* Attrib tests say access allowed. */
176 }
177
178 /*
179 * Perform security impersonation of the user and open the resulting
180 * thread token.
181 */
182
183 if( !ImpersonateSelf(SecurityImpersonation) ){
184 /*
185 * Unable to perform security impersonation.
186 */
187
188 rc = -1; goto done;
189 }
190 impersonated = TRUE;
191
192 if( !OpenThreadToken(GetCurrentThread(),
193 TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &hToken) ){
194 /*
195 * Unable to get current thread's token.
196 */
197
198 rc = -1; goto done;
199 }
200
201 /*
202 * Setup desiredAccess according to the access priveleges we are
203 * checking.
204 */
205
206 if( flags & R_OK ){
207 desiredAccess |= FILE_GENERIC_READ;
208 }
209 if( flags & W_OK){
210 desiredAccess |= FILE_GENERIC_WRITE;
211 }
212
213 memset(&genMap, 0, sizeof(GENERIC_MAPPING));
214 genMap.GenericRead = FILE_GENERIC_READ;
215 genMap.GenericWrite = FILE_GENERIC_WRITE;
216 genMap.GenericExecute = FILE_GENERIC_EXECUTE;
217 genMap.GenericAll = FILE_ALL_ACCESS;
218
219 AccessCheck(pSd, hToken, desiredAccess, &genMap, 0,
220 &privSetSize, &grantedAccess, &accessYesNo);
221 /*
222 * Should have failed with ERROR_INSUFFICIENT_BUFFER
223 */
224
225 if( GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
226 rc = -1; goto done;
227 }
228 pPrivSet = (PPRIVILEGE_SET)HeapAlloc(GetProcessHeap(), 0, privSetSize);
229
230 if( pPrivSet==NULL ){
231 rc = -1; goto done;
232 }
233
234 /*
235 * Perform access check using the token.
236 */
237
238 if( !AccessCheck(pSd, hToken, desiredAccess, &genMap, pPrivSet,
239 &privSetSize, &grantedAccess, &accessYesNo) ){
240 /*
241 * Unable to perform access check.
242 */
243
244 rc = -1; goto done;
245 }
246 if( !accessYesNo ){
247 rc = -1;
248 }
249
250 done:
251
252 if( hToken != NULL ){
253 CloseHandle(hToken);
254 }
255 if( impersonated ){
256 RevertToSelf();
257 impersonated = FALSE;
258 }
259 if( pPrivSet!=NULL ){
260 HeapFree(GetProcessHeap(), 0, pPrivSet);
261 }
262 if( pSd!=NULL ){
263 HeapFree(GetProcessHeap(), 0, pSd);
264 }
265 return rc;
266 }
267
268 /*
269 ** Wrapper around the chdir() system call.
270 */
win32_chdir(const wchar_t * zChDir,int bChroot)271 int win32_chdir(const wchar_t *zChDir, int bChroot){
272 int rc = (int)!SetCurrentDirectoryW(zChDir);
273 return rc;
274 }
275
276 /*
277 ** Get the current working directory.
278 **
279 ** On windows, the name is converted from unicode to UTF8 and all '\\'
280 ** characters are converted to '/'.
281 */
win32_getcwd(char * zBuf,int nBuf)282 void win32_getcwd(char *zBuf, int nBuf){
283 int i;
284 char *zUtf8;
285 wchar_t *zWide = fossil_malloc( sizeof(wchar_t)*nBuf );
286 if( GetCurrentDirectoryW(nBuf, zWide)==0 ){
287 fossil_fatal("cannot find current working directory.");
288 }
289 zUtf8 = fossil_path_to_utf8(zWide);
290 fossil_free(zWide);
291 for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/';
292 strncpy(zBuf, zUtf8, nBuf);
293 fossil_path_free(zUtf8);
294 }
295 #endif /* _WIN32 -- This code is for win32 only */
296