|
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
|
** [email protected] |
|
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
|
*/ |
|
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
|
*/ |
|
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
|
*/ |
|
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
|
*/ |
|
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
|
|
|
296
|
/* Perform case-insensitive comparison of two UTF-16 file names. Try to load the |
|
297
|
** CompareStringOrdinal() function on Windows Vista and newer, and resort to the |
|
298
|
** RtlEqualUnicodeString() function on Windows XP. |
|
299
|
** The dance to invoke RtlEqualUnicodeString() is necessary because lstrcmpiW() |
|
300
|
** performs linguistic comparison, while the former performs binary comparison. |
|
301
|
** As an example, matching "ß" (U+00DF Latin Small Letter Sharp S) with "ss" is |
|
302
|
** undesirable in file name comparison, so lstrcmpiW() is only invoked in cases |
|
303
|
** that are technically impossible and contradicting all known laws of physics. |
|
304
|
*/ |
|
305
|
int win32_filenames_equal_nocase( |
|
306
|
const wchar_t *fn1, |
|
307
|
const wchar_t *fn2 |
|
308
|
){ |
|
309
|
/* ---- Data types used by dynamically loaded API functions. -------------- */ |
|
310
|
typedef struct { /* UNICODE_STRING from <ntdef.h> */ |
|
311
|
USHORT Length; |
|
312
|
USHORT MaximumLength; |
|
313
|
PWSTR Buffer; |
|
314
|
} MY_UNICODE_STRING; |
|
315
|
/* ---- Prototypes for dynamically loaded API functions. ------------------ */ |
|
316
|
typedef int (WINAPI *FNCOMPARESTRINGORDINAL)(LPCWCH,int,LPCWCH,int,BOOL); |
|
317
|
typedef VOID (NTAPI *FNRTLINITUNICODESTRING)(MY_UNICODE_STRING*,PCWSTR); |
|
318
|
typedef BOOLEAN (NTAPI *FNRTLEQUALUNICODESTRING) |
|
319
|
(MY_UNICODE_STRING*,MY_UNICODE_STRING*,BOOLEAN); |
|
320
|
/* ------------------------------------------------------------------------ */ |
|
321
|
static FNCOMPARESTRINGORDINAL fnCompareStringOrdinal; |
|
322
|
static FNRTLINITUNICODESTRING fnRtlInitUnicodeString; |
|
323
|
static FNRTLEQUALUNICODESTRING fnRtlEqualUnicodeString; |
|
324
|
static int loaded_CompareStringOrdinal; |
|
325
|
static int loaded_RtlUnicodeStringAPIs; |
|
326
|
if( !loaded_CompareStringOrdinal ){ |
|
327
|
fnCompareStringOrdinal = (FNCOMPARESTRINGORDINAL) |
|
328
|
GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal"); |
|
329
|
loaded_CompareStringOrdinal = 1; |
|
330
|
} |
|
331
|
if( fnCompareStringOrdinal ){ |
|
332
|
return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0; |
|
333
|
} |
|
334
|
if( !loaded_RtlUnicodeStringAPIs ){ |
|
335
|
fnRtlInitUnicodeString = (FNRTLINITUNICODESTRING) |
|
336
|
GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString"); |
|
337
|
fnRtlEqualUnicodeString = (FNRTLEQUALUNICODESTRING) |
|
338
|
GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString"); |
|
339
|
loaded_RtlUnicodeStringAPIs = 1; |
|
340
|
} |
|
341
|
if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){ |
|
342
|
MY_UNICODE_STRING u1, u2; |
|
343
|
fnRtlInitUnicodeString(&u1,fn1); |
|
344
|
fnRtlInitUnicodeString(&u2,fn2); |
|
345
|
return (BOOLEAN/*unsigned char*/)fnRtlEqualUnicodeString(&u1,&u2,1); |
|
346
|
} |
|
347
|
/* In what kind of strange parallel universe are we? */ |
|
348
|
return lstrcmpiW(fn1,fn2)==0; |
|
349
|
} |
|
350
|
|
|
351
|
/* Helper macros to deal with directory separators. */ |
|
352
|
#define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' ) |
|
353
|
#define NEXT_DIRSEP(s,i) while( s[i] && s[i]!='/' && s[i]!='\\' ){i++;} |
|
354
|
|
|
355
|
/* The Win32 version of file_case_preferred_name() from file.c, which is able to |
|
356
|
** find case-preserved file names containing non-ASCII characters. The result is |
|
357
|
** allocated by fossil_malloc() and *should* be free'd by the caller. While this |
|
358
|
** function usually gets canonicalized paths, it is able to handle any input and |
|
359
|
** figure out more cases than the original: |
|
360
|
** |
|
361
|
** fossil test-case-filename C:/ .//..\WINDOWS\/.//.\SYSTEM32\.\NOTEPAD.EXE |
|
362
|
** → Original: .//..\WINDOWS\/.//.\SYSTEM32\.\NOTEPAD.EXE |
|
363
|
** → Modified: .//..\Windows\/.//.\System32\.\notepad.exe |
|
364
|
** |
|
365
|
** md ÄÖÜ |
|
366
|
** fossil test-case-filename ./\ .\äöü\/[empty]\\/ |
|
367
|
** → Original: ./äöü\/[empty]\\/ |
|
368
|
** → Modified: .\ÄÖÜ\/[empty]\\/ |
|
369
|
** |
|
370
|
** The function preserves slashes and backslashes: only single file or directory |
|
371
|
** components without directory separators ("basenames") are converted to UTF-16 |
|
372
|
** using fossil_utf8_to_path(), so bypassing its slash ↔ backslash translations. |
|
373
|
** Note that the original function doesn't preserve all slashes and backslashes, |
|
374
|
** for example in the second example above. |
|
375
|
** |
|
376
|
** NOTE: As of Windows 10, version 1803, case sensitivity may be enabled on a |
|
377
|
** per-directory basis, as returned by NtQueryInformationFile() with the file |
|
378
|
** information class FILE_CASE_SENSITIVE_INFORMATION. So this function may be |
|
379
|
** changed to act like fossil_strdup() for files located in such directories. |
|
380
|
*/ |
|
381
|
char *win32_file_case_preferred_name( |
|
382
|
const char *zBase, |
|
383
|
const char *zPath |
|
384
|
){ |
|
385
|
int cchBase; |
|
386
|
int cchPath; |
|
387
|
int cchBuf; |
|
388
|
int cchRes; |
|
389
|
char *zBuf; |
|
390
|
char *zRes; |
|
391
|
int ncUsed; |
|
392
|
int i, j; |
|
393
|
if( filenames_are_case_sensitive() ){ |
|
394
|
return fossil_strdup(zPath); |
|
395
|
} |
|
396
|
cchBase = strlen(zBase); |
|
397
|
cchPath = strlen(zPath); |
|
398
|
cchBuf = cchBase + cchPath + 2; /* + NULL + optional directory slash */ |
|
399
|
cchRes = cchPath + 1; /* + NULL */ |
|
400
|
zBuf = fossil_malloc(cchBuf); |
|
401
|
zRes = fossil_malloc(cchRes); |
|
402
|
ncUsed = 0; |
|
403
|
memcpy(zBuf,zBase,cchBase); |
|
404
|
if( !IS_DIRSEP(zBuf,cchBase-1) ){ |
|
405
|
zBuf[cchBase++]=L'/'; |
|
406
|
} |
|
407
|
memcpy(zBuf+cchBase,zPath,cchPath+1); |
|
408
|
i = j = cchBase; |
|
409
|
while( 1 ){ |
|
410
|
WIN32_FIND_DATAW fd; |
|
411
|
HANDLE hFind; |
|
412
|
wchar_t *wzBuf; |
|
413
|
char *zCompBuf = 0; |
|
414
|
char *zComp = &zBuf[i]; |
|
415
|
int cchComp; |
|
416
|
char chSep; |
|
417
|
int fDone; |
|
418
|
if( IS_DIRSEP(zBuf,i) ){ |
|
419
|
if( ncUsed+2>cchRes ){ /* Directory slash + NULL*/ |
|
420
|
cchRes += 32; /* Overprovisioning. */ |
|
421
|
zRes = fossil_realloc(zRes,cchRes); |
|
422
|
} |
|
423
|
zRes[ncUsed++] = zBuf[i]; |
|
424
|
i = j = i+1; |
|
425
|
continue; |
|
426
|
} |
|
427
|
NEXT_DIRSEP(zBuf,j); |
|
428
|
fDone = zBuf[j]==0; |
|
429
|
chSep = zBuf[j]; |
|
430
|
zBuf[j] = 0; /* Truncate working buffer. */ |
|
431
|
wzBuf = fossil_utf8_to_path(zBuf,0); |
|
432
|
hFind = FindFirstFileW(wzBuf,&fd); |
|
433
|
if( hFind!=INVALID_HANDLE_VALUE ){ |
|
434
|
wchar_t *wzComp = fossil_utf8_to_path(zComp,0); |
|
435
|
FindClose(hFind); |
|
436
|
/* Test fd.cFileName, not fd.cAlternateFileName (classic 8.3 format). */ |
|
437
|
if( win32_filenames_equal_nocase(wzComp,fd.cFileName) ){ |
|
438
|
zCompBuf = fossil_path_to_utf8(fd.cFileName); |
|
439
|
zComp = zCompBuf; |
|
440
|
} |
|
441
|
fossil_path_free(wzComp); |
|
442
|
} |
|
443
|
fossil_path_free(wzBuf); |
|
444
|
cchComp = strlen(zComp); |
|
445
|
if( ncUsed+cchComp+1>cchRes ){ /* Current component + NULL */ |
|
446
|
cchRes += cchComp + 32; /* Overprovisioning. */ |
|
447
|
zRes = fossil_realloc(zRes,cchRes); |
|
448
|
} |
|
449
|
memcpy(zRes+ncUsed,zComp,cchComp); |
|
450
|
ncUsed += cchComp; |
|
451
|
if( zCompBuf ){ |
|
452
|
fossil_path_free(zCompBuf); |
|
453
|
} |
|
454
|
if( fDone ){ |
|
455
|
zRes[ncUsed] = 0; |
|
456
|
break; |
|
457
|
} |
|
458
|
zBuf[j] = chSep; /* Undo working buffer truncation. */ |
|
459
|
i = j; |
|
460
|
} |
|
461
|
fossil_free(zBuf); |
|
462
|
return zRes; |
|
463
|
} |
|
464
|
|
|
465
|
/* Return the unique identifier (UID) for a file, made up of the file identifier |
|
466
|
** (equal to "inode" for Unix-style file systems) plus the volume serial number. |
|
467
|
** Call the GetFileInformationByHandleEx() function on Windows Vista, and resort |
|
468
|
** to the GetFileInformationByHandle() function on Windows XP. The result string |
|
469
|
** is allocated by mprintf(), or NULL on failure. |
|
470
|
*/ |
|
471
|
char *win32_file_id( |
|
472
|
const char *zFileName |
|
473
|
){ |
|
474
|
/* ---- Data types used by dynamically loaded API functions. -------------- */ |
|
475
|
typedef struct { /* FILE_ID_INFO from <winbase.h> */ |
|
476
|
ULONGLONG VolumeSerialNumber; |
|
477
|
BYTE FileId[16]; |
|
478
|
} MY_FILE_ID_INFO; |
|
479
|
/* ---- Prototypes for dynamically loaded API functions. ------------------ */ |
|
480
|
typedef int (WINAPI *FNGETFILEINFORMATIONBYHANDLEEX) |
|
481
|
(HANDLE,int/*enum*/,MY_FILE_ID_INFO*,DWORD); |
|
482
|
/* ------------------------------------------------------------------------ */ |
|
483
|
static FNGETFILEINFORMATIONBYHANDLEEX fnGetFileInformationByHandleEx; |
|
484
|
static int loaded_fnGetFileInformationByHandleEx; |
|
485
|
wchar_t *wzFileName = fossil_utf8_to_path(zFileName,0); |
|
486
|
HANDLE hFile; |
|
487
|
char *zFileId = 0; |
|
488
|
hFile = CreateFileW( |
|
489
|
wzFileName, |
|
490
|
0, |
|
491
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
|
492
|
NULL, |
|
493
|
OPEN_EXISTING, |
|
494
|
FILE_FLAG_BACKUP_SEMANTICS, |
|
495
|
NULL); |
|
496
|
if( hFile!=INVALID_HANDLE_VALUE ){ |
|
497
|
BY_HANDLE_FILE_INFORMATION fi; |
|
498
|
MY_FILE_ID_INFO fi2; |
|
499
|
if( !loaded_fnGetFileInformationByHandleEx ){ |
|
500
|
fnGetFileInformationByHandleEx = (FNGETFILEINFORMATIONBYHANDLEEX) |
|
501
|
GetProcAddress( |
|
502
|
GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx"); |
|
503
|
loaded_fnGetFileInformationByHandleEx = 1; |
|
504
|
} |
|
505
|
if( fnGetFileInformationByHandleEx ){ |
|
506
|
if( fnGetFileInformationByHandleEx( |
|
507
|
hFile,/*FileIdInfo*/0x12,&fi2,sizeof(fi2)) ){ |
|
508
|
zFileId = mprintf( |
|
509
|
"%016llx/" |
|
510
|
"%02x%02x%02x%02x%02x%02x%02x%02x" |
|
511
|
"%02x%02x%02x%02x%02x%02x%02x%02x", |
|
512
|
fi2.VolumeSerialNumber, |
|
513
|
fi2.FileId[15], fi2.FileId[14], |
|
514
|
fi2.FileId[13], fi2.FileId[12], |
|
515
|
fi2.FileId[11], fi2.FileId[10], |
|
516
|
fi2.FileId[9], fi2.FileId[8], |
|
517
|
fi2.FileId[7], fi2.FileId[6], |
|
518
|
fi2.FileId[5], fi2.FileId[4], |
|
519
|
fi2.FileId[3], fi2.FileId[2], |
|
520
|
fi2.FileId[1], fi2.FileId[0]); |
|
521
|
} |
|
522
|
} |
|
523
|
if( zFileId==0 ){ |
|
524
|
if( GetFileInformationByHandle(hFile,&fi) ){ |
|
525
|
ULARGE_INTEGER FileId = {{ |
|
526
|
/*.LowPart = */ fi.nFileIndexLow, |
|
527
|
/*.HighPart = */ fi.nFileIndexHigh |
|
528
|
}}; |
|
529
|
zFileId = mprintf( |
|
530
|
"%08x/%016llx", |
|
531
|
fi.dwVolumeSerialNumber,(u64)FileId.QuadPart); |
|
532
|
} |
|
533
|
} |
|
534
|
CloseHandle(hFile); |
|
535
|
} |
|
536
|
fossil_path_free(wzFileName); |
|
537
|
return zFileId; |
|
538
|
} |
|
539
|
#endif /* _WIN32 -- This code is for win32 only */ |
|
540
|
|