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