Fossil SCM

Work in progress for windows symlink support.

scott 2014-09-15 01:49 trunk
Commit de8035cca6ebd69c783bcf42d46447623910a964
4 files changed +10 -7 +3 -5 +20 -12 +211 -12
+10 -7
--- src/blob.c
+++ src/blob.c
@@ -746,32 +746,35 @@
746746
}
747747
return got;
748748
}
749749
750750
/*
751
-** Reads symlink destination path and puts int into blob.
751
+** Reads symlink destination path and puts it into blob.
752752
** Any prior content of the blob is discarded, not freed.
753753
**
754754
** Returns length of destination path.
755755
**
756
-** On windows, zeros blob and returns 0.
756
+** On windows, zeros blob and returns 0 if symlinks are not supported.
757757
*/
758758
int blob_read_link(Blob *pBlob, const char *zFilename){
759
-#if !defined(_WIN32)
760759
char zBuf[1024];
760
+#if !defined(_WIN32)
761761
ssize_t len = readlink(zFilename, zBuf, 1023);
762762
if( len < 0 ){
763763
fossil_fatal("cannot read symbolic link %s", zFilename);
764764
}
765
+#else
766
+ ssize_t len = win32_readlink(zFilename, zBuf, 1023);
767
+ if( len < 0 ){
768
+ blob_zero(pBlob);
769
+ return 0;
770
+ }
771
+#endif
765772
zBuf[len] = 0; /* null-terminate */
766773
blob_zero(pBlob);
767774
blob_appendf(pBlob, "%s", zBuf);
768775
return len;
769
-#else
770
- blob_zero(pBlob);
771
- return 0;
772
-#endif
773776
}
774777
775778
776779
/*
777780
** Write the content of a blob into a file.
778781
--- src/blob.c
+++ src/blob.c
@@ -746,32 +746,35 @@
746 }
747 return got;
748 }
749
750 /*
751 ** Reads symlink destination path and puts int into blob.
752 ** Any prior content of the blob is discarded, not freed.
753 **
754 ** Returns length of destination path.
755 **
756 ** On windows, zeros blob and returns 0.
757 */
758 int blob_read_link(Blob *pBlob, const char *zFilename){
759 #if !defined(_WIN32)
760 char zBuf[1024];
 
761 ssize_t len = readlink(zFilename, zBuf, 1023);
762 if( len < 0 ){
763 fossil_fatal("cannot read symbolic link %s", zFilename);
764 }
 
 
 
 
 
 
 
765 zBuf[len] = 0; /* null-terminate */
766 blob_zero(pBlob);
767 blob_appendf(pBlob, "%s", zBuf);
768 return len;
769 #else
770 blob_zero(pBlob);
771 return 0;
772 #endif
773 }
774
775
776 /*
777 ** Write the content of a blob into a file.
778
--- src/blob.c
+++ src/blob.c
@@ -746,32 +746,35 @@
746 }
747 return got;
748 }
749
750 /*
751 ** Reads symlink destination path and puts it into blob.
752 ** Any prior content of the blob is discarded, not freed.
753 **
754 ** Returns length of destination path.
755 **
756 ** On windows, zeros blob and returns 0 if symlinks are not supported.
757 */
758 int blob_read_link(Blob *pBlob, const char *zFilename){
 
759 char zBuf[1024];
760 #if !defined(_WIN32)
761 ssize_t len = readlink(zFilename, zBuf, 1023);
762 if( len < 0 ){
763 fossil_fatal("cannot read symbolic link %s", zFilename);
764 }
765 #else
766 ssize_t len = win32_readlink(zFilename, zBuf, 1023);
767 if( len < 0 ){
768 blob_zero(pBlob);
769 return 0;
770 }
771 #endif
772 zBuf[len] = 0; /* null-terminate */
773 blob_zero(pBlob);
774 blob_appendf(pBlob, "%s", zBuf);
775 return len;
 
 
 
 
776 }
777
778
779 /*
780 ** Write the content of a blob into a file.
781
+3 -5
--- src/checkin.c
+++ src/checkin.c
@@ -1106,24 +1106,22 @@
11061106
int cmp;
11071107
11081108
blob_resize(&filename, nBasename);
11091109
blob_append(&filename, zName, -1);
11101110
1111
-#if !defined(_WIN32)
1112
- /* For unix, extract the "executable" and "symlink" permissions
1113
- ** directly from the filesystem. On windows, permissions are
1114
- ** unchanged from the original. However, only do this if the file
1111
+ /* Extract the "executable" and "symlink" permissions
1112
+ ** directly from the filesystem. However, only do this if the file
11151113
** itself is actually selected to be part of this check-in.
11161114
*/
11171115
if( isSelected ){
11181116
int mPerm;
11191117
11201118
mPerm = file_wd_perm(blob_str(&filename));
11211119
isExe = ( mPerm==PERM_EXE );
11221120
isLink = ( mPerm==PERM_LNK );
11231121
}
1124
-#endif
1122
+
11251123
if( isExe ){
11261124
zPerm = " x";
11271125
}else if( isLink ){
11281126
zPerm = " l"; /* note: symlinks don't have executable bit on unix */
11291127
}else{
11301128
--- src/checkin.c
+++ src/checkin.c
@@ -1106,24 +1106,22 @@
1106 int cmp;
1107
1108 blob_resize(&filename, nBasename);
1109 blob_append(&filename, zName, -1);
1110
1111 #if !defined(_WIN32)
1112 /* For unix, extract the "executable" and "symlink" permissions
1113 ** directly from the filesystem. On windows, permissions are
1114 ** unchanged from the original. However, only do this if the file
1115 ** itself is actually selected to be part of this check-in.
1116 */
1117 if( isSelected ){
1118 int mPerm;
1119
1120 mPerm = file_wd_perm(blob_str(&filename));
1121 isExe = ( mPerm==PERM_EXE );
1122 isLink = ( mPerm==PERM_LNK );
1123 }
1124 #endif
1125 if( isExe ){
1126 zPerm = " x";
1127 }else if( isLink ){
1128 zPerm = " l"; /* note: symlinks don't have executable bit on unix */
1129 }else{
1130
--- src/checkin.c
+++ src/checkin.c
@@ -1106,24 +1106,22 @@
1106 int cmp;
1107
1108 blob_resize(&filename, nBasename);
1109 blob_append(&filename, zName, -1);
1110
1111 /* Extract the "executable" and "symlink" permissions
1112 ** directly from the filesystem. However, only do this if the file
 
 
1113 ** itself is actually selected to be part of this check-in.
1114 */
1115 if( isSelected ){
1116 int mPerm;
1117
1118 mPerm = file_wd_perm(blob_str(&filename));
1119 isExe = ( mPerm==PERM_EXE );
1120 isLink = ( mPerm==PERM_LNK );
1121 }
1122
1123 if( isExe ){
1124 zPerm = " x";
1125 }else if( isLink ){
1126 zPerm = " l"; /* note: symlinks don't have executable bit on unix */
1127 }else{
1128
+20 -12
--- src/file.c
+++ src/file.c
@@ -65,14 +65,14 @@
6565
#if !defined(_WIN32) || !(defined(__MSVCRT__) || defined(_MSC_VER))
6666
# define fossilStat stat
6767
#endif
6868
6969
/*
70
-** On Windows S_ISLNK always returns FALSE.
70
+** On Windows S_ISLNK can be true or false.
7171
*/
7272
#if !defined(S_ISLNK)
73
-# define S_ISLNK(x) (0)
73
+# define S_ISLNK(x) ((x)==S_IFLNK)
7474
#endif
7575
static int fileStatValid = 0;
7676
static struct fossilStat fileStat;
7777
7878
/*
@@ -88,11 +88,15 @@
8888
rc = lstat(zMbcs, buf);
8989
}else{
9090
rc = stat(zMbcs, buf);
9191
}
9292
#else
93
- rc = win32_stat(zMbcs, buf, isWd);
93
+ if( isWd && g.allowSymlinks ){
94
+ rc = win32_lstat(zMbcs, buf);
95
+ }else{
96
+ rc = win32_stat(zMbcs, buf);
97
+ }
9498
#endif
9599
fossil_filename_free(zMbcs);
96100
return rc;
97101
}
98102
@@ -184,11 +188,15 @@
184188
**
185189
** Arguments: target file (symlink will point to it), link file
186190
**/
187191
void symlink_create(const char *zTargetFile, const char *zLinkFile){
188192
#if !defined(_WIN32)
189
- if( g.allowSymlinks ){
193
+ int symlinks_supported = 1;
194
+#else
195
+ int symlinks_supported = win32_symlinks_supported(zLinkFile);
196
+#endif
197
+ if( symlinks_supported && g.allowSymlinks ){
190198
int i, nName;
191199
char *zName, zBuf[1000];
192200
193201
nName = strlen(zLinkFile);
194202
if( nName>=sizeof(zBuf) ){
@@ -206,17 +214,20 @@
206214
return;
207215
}
208216
zName[i] = '/';
209217
}
210218
}
211
- if( symlink(zTargetFile, zName)!=0 ){
219
+#if !defined(_WIN32)
220
+ if( symlink(zTargetFile, zName)!=0 )
221
+#else
222
+ if( win32_symlink(zTargetFile, zName)!=0 )
223
+#endif
224
+ {
212225
fossil_fatal_recursive("unable to create symlink \"%s\"", zName);
213226
}
214227
if( zName!=zBuf ) free(zName);
215
- }else
216
-#endif
217
- {
228
+ }else{
218229
Blob content;
219230
blob_set(&content, zTargetFile);
220231
blob_write_to_file(&content, zLinkFile);
221232
blob_reset(&content);
222233
}
@@ -243,22 +254,19 @@
243254
#if defined(_WIN32)
244255
# ifndef S_IXUSR
245256
# define S_IXUSR _S_IEXEC
246257
# endif
247258
if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 )
248
- return PERM_EXE;
249
- else
250
- return PERM_REG;
251259
#else
252260
if( S_ISREG(fileStat.st_mode) &&
253261
((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0 )
262
+#endif
254263
return PERM_EXE;
255264
else if( g.allowSymlinks && S_ISLNK(fileStat.st_mode) )
256265
return PERM_LNK;
257266
else
258267
return PERM_REG;
259
-#endif
260268
}
261269
262270
/*
263271
** Return TRUE if the named file is an executable. Return false
264272
** for directories, devices, fifos, symlinks, etc.
265273
--- src/file.c
+++ src/file.c
@@ -65,14 +65,14 @@
65 #if !defined(_WIN32) || !(defined(__MSVCRT__) || defined(_MSC_VER))
66 # define fossilStat stat
67 #endif
68
69 /*
70 ** On Windows S_ISLNK always returns FALSE.
71 */
72 #if !defined(S_ISLNK)
73 # define S_ISLNK(x) (0)
74 #endif
75 static int fileStatValid = 0;
76 static struct fossilStat fileStat;
77
78 /*
@@ -88,11 +88,15 @@
88 rc = lstat(zMbcs, buf);
89 }else{
90 rc = stat(zMbcs, buf);
91 }
92 #else
93 rc = win32_stat(zMbcs, buf, isWd);
 
 
 
 
94 #endif
95 fossil_filename_free(zMbcs);
96 return rc;
97 }
98
@@ -184,11 +188,15 @@
184 **
185 ** Arguments: target file (symlink will point to it), link file
186 **/
187 void symlink_create(const char *zTargetFile, const char *zLinkFile){
188 #if !defined(_WIN32)
189 if( g.allowSymlinks ){
 
 
 
 
190 int i, nName;
191 char *zName, zBuf[1000];
192
193 nName = strlen(zLinkFile);
194 if( nName>=sizeof(zBuf) ){
@@ -206,17 +214,20 @@
206 return;
207 }
208 zName[i] = '/';
209 }
210 }
211 if( symlink(zTargetFile, zName)!=0 ){
 
 
 
 
 
212 fossil_fatal_recursive("unable to create symlink \"%s\"", zName);
213 }
214 if( zName!=zBuf ) free(zName);
215 }else
216 #endif
217 {
218 Blob content;
219 blob_set(&content, zTargetFile);
220 blob_write_to_file(&content, zLinkFile);
221 blob_reset(&content);
222 }
@@ -243,22 +254,19 @@
243 #if defined(_WIN32)
244 # ifndef S_IXUSR
245 # define S_IXUSR _S_IEXEC
246 # endif
247 if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 )
248 return PERM_EXE;
249 else
250 return PERM_REG;
251 #else
252 if( S_ISREG(fileStat.st_mode) &&
253 ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0 )
 
254 return PERM_EXE;
255 else if( g.allowSymlinks && S_ISLNK(fileStat.st_mode) )
256 return PERM_LNK;
257 else
258 return PERM_REG;
259 #endif
260 }
261
262 /*
263 ** Return TRUE if the named file is an executable. Return false
264 ** for directories, devices, fifos, symlinks, etc.
265
--- src/file.c
+++ src/file.c
@@ -65,14 +65,14 @@
65 #if !defined(_WIN32) || !(defined(__MSVCRT__) || defined(_MSC_VER))
66 # define fossilStat stat
67 #endif
68
69 /*
70 ** On Windows S_ISLNK can be true or false.
71 */
72 #if !defined(S_ISLNK)
73 # define S_ISLNK(x) ((x)==S_IFLNK)
74 #endif
75 static int fileStatValid = 0;
76 static struct fossilStat fileStat;
77
78 /*
@@ -88,11 +88,15 @@
88 rc = lstat(zMbcs, buf);
89 }else{
90 rc = stat(zMbcs, buf);
91 }
92 #else
93 if( isWd && g.allowSymlinks ){
94 rc = win32_lstat(zMbcs, buf);
95 }else{
96 rc = win32_stat(zMbcs, buf);
97 }
98 #endif
99 fossil_filename_free(zMbcs);
100 return rc;
101 }
102
@@ -184,11 +188,15 @@
188 **
189 ** Arguments: target file (symlink will point to it), link file
190 **/
191 void symlink_create(const char *zTargetFile, const char *zLinkFile){
192 #if !defined(_WIN32)
193 int symlinks_supported = 1;
194 #else
195 int symlinks_supported = win32_symlinks_supported(zLinkFile);
196 #endif
197 if( symlinks_supported && g.allowSymlinks ){
198 int i, nName;
199 char *zName, zBuf[1000];
200
201 nName = strlen(zLinkFile);
202 if( nName>=sizeof(zBuf) ){
@@ -206,17 +214,20 @@
214 return;
215 }
216 zName[i] = '/';
217 }
218 }
219 #if !defined(_WIN32)
220 if( symlink(zTargetFile, zName)!=0 )
221 #else
222 if( win32_symlink(zTargetFile, zName)!=0 )
223 #endif
224 {
225 fossil_fatal_recursive("unable to create symlink \"%s\"", zName);
226 }
227 if( zName!=zBuf ) free(zName);
228 }else{
 
 
229 Blob content;
230 blob_set(&content, zTargetFile);
231 blob_write_to_file(&content, zLinkFile);
232 blob_reset(&content);
233 }
@@ -243,22 +254,19 @@
254 #if defined(_WIN32)
255 # ifndef S_IXUSR
256 # define S_IXUSR _S_IEXEC
257 # endif
258 if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 )
 
 
 
259 #else
260 if( S_ISREG(fileStat.st_mode) &&
261 ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0 )
262 #endif
263 return PERM_EXE;
264 else if( g.allowSymlinks && S_ISLNK(fileStat.st_mode) )
265 return PERM_LNK;
266 else
267 return PERM_REG;
 
268 }
269
270 /*
271 ** Return TRUE if the named file is an executable. Return false
272 ** for directories, devices, fifos, symlinks, etc.
273
+211 -12
--- src/winfile.c
+++ src/winfile.c
@@ -21,34 +21,233 @@
2121
#include "config.h"
2222
#ifdef _WIN32
2323
/* This code is for win32 only */
2424
#include <sys/stat.h>
2525
#include <windows.h>
26
+#include <versionhelpers.h>
2627
#include "winfile.h"
2728
2829
#ifndef LABEL_SECURITY_INFORMATION
2930
# define LABEL_SECURITY_INFORMATION (0x00000010L)
3031
#endif
32
+
33
+/* copy & paste from ntifs.h */
34
+typedef struct _REPARSE_DATA_BUFFER {
35
+ ULONG ReparseTag;
36
+ USHORT ReparseDataLength;
37
+ USHORT Reserved;
38
+ union {
39
+ struct {
40
+ USHORT SubstituteNameOffset;
41
+ USHORT SubstituteNameLength;
42
+ USHORT PrintNameOffset;
43
+ USHORT PrintNameLength;
44
+ ULONG Flags;
45
+ WCHAR PathBuffer[1];
46
+ } SymbolicLinkReparseBuffer;
47
+ struct {
48
+ USHORT SubstituteNameOffset;
49
+ USHORT SubstituteNameLength;
50
+ USHORT PrintNameOffset;
51
+ USHORT PrintNameLength;
52
+ WCHAR PathBuffer[1];
53
+ } MountPointReparseBuffer;
54
+ struct {
55
+ UCHAR DataBuffer[1];
56
+ } GenericReparseBuffer;
57
+ };
58
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
59
+
60
+#define LINK_BUFFER_SIZE 1024
61
+
62
+int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){
63
+ WIN32_FILE_ATTRIBUTE_DATA attr;
64
+ int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr);
65
+ if( rc ){
66
+ char *tname = fossil_filename_to_utf8(zFilename);
67
+ char tlink[LINK_BUFFER_SIZE];
68
+ ssize_t tlen = win32_readlink(tname, tlink, sizeof(tlink));
69
+ ULARGE_INTEGER ull;
70
+
71
+ buf->st_mode = (tlen > 0) ? S_IFLNK :
72
+ ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
73
+ S_IFDIR : S_IFREG);
74
+
75
+ buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow;
76
+
77
+ ull.LowPart = attr.ftLastWriteTime.dwLowDateTime;
78
+ ull.HighPart = attr.ftLastWriteTime.dwHighDateTime;
79
+ buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL;
80
+ }
81
+ return !rc;
82
+}
3183
3284
/*
3385
** Fill stat buf with information received from stat() or lstat().
3486
** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on.
3587
**
3688
*/
37
-int win32_stat(const wchar_t *zFilename, struct fossilStat *buf, int isWd){
89
+int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){
90
+ int rc;
91
+ HANDLE file;
92
+ wchar_t nextFilename[LINK_BUFFER_SIZE];
93
+ DWORD len;
94
+
95
+ while (1){
96
+ rc = win32_lstat(zFilename, buf);
97
+ /* exit on error or not link */
98
+ if ((rc != 0) || (buf->st_mode != S_IFLNK))
99
+ break;
100
+
101
+ /* it is a link, so open the linked file */
102
+ file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
103
+ if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){
104
+ rc = -1;
105
+ break;
106
+ }
107
+
108
+ /* get the final path name and close the handle */
109
+ len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0);
110
+ CloseHandle(file);
111
+
112
+ /* if any problems getting the final path name error so exit */
113
+ if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){
114
+ rc = -1;
115
+ break;
116
+ }
117
+
118
+ /* prepare to try again just in case we have a chain to follow */
119
+ /* this shouldn't happen, but just trying to be safe */
120
+ zFilename = nextFilename;
121
+ }
122
+
123
+ return rc;
124
+}
125
+
126
+ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){
127
+ /* assume we're going to fail */
128
+ ssize_t rv = -1;
129
+
130
+ /* does path reference a reparse point? */
38131
WIN32_FILE_ATTRIBUTE_DATA attr;
39
- int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr);
40
- if( rc ){
41
- ULARGE_INTEGER ull;
42
- ull.LowPart = attr.ftLastWriteTime.dwLowDateTime;
43
- ull.HighPart = attr.ftLastWriteTime.dwHighDateTime;
44
- buf->st_mode = (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
45
- S_IFDIR : S_IFREG;
46
- buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow;
47
- buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL;
48
- }
49
- return !rc;
132
+ int rc = GetFileAttributesEx(path, GetFileExInfoStandard, &attr);
133
+ if (rc && (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)){
134
+
135
+ /* since it is a reparse point, open it */
136
+ HANDLE file = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
137
+ FILE_FLAG_OPEN_REPARSE_POINT, NULL);
138
+ if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){
139
+
140
+ /* use DeviceIoControl to get the reparse point data */
141
+
142
+ union {
143
+ REPARSE_DATA_BUFFER data;
144
+ char buffer[sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t)];
145
+ } u;
146
+ DWORD bytes;
147
+
148
+ u.data.ReparseTag = IO_REPARSE_TAG_SYMLINK;
149
+ u.data.ReparseDataLength = 0;
150
+ u.data.Reserved = 0;
151
+
152
+ int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0,
153
+ &u, sizeof(u), &bytes, NULL);
154
+
155
+ /* did the reparse point data fit into the desired buffer? */
156
+ if (rc && (bytes < sizeof(u))){
157
+ /* it fit, so setup the print name for further processing */
158
+ USHORT
159
+ offset = u.data.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t),
160
+ length = u.data.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t);
161
+ char *temp;
162
+ u.data.SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0;
163
+
164
+ /* convert the filename to utf8, copy it, and discard the converted copy */
165
+ temp = fossil_filename_to_utf8(u.data.SymbolicLinkReparseBuffer.PathBuffer + offset);
166
+ rv = strlen(temp);
167
+ if (rv >= bufsiz)
168
+ rv = bufsiz;
169
+ memcpy(buf, temp, rv);
170
+ fossil_filename_free(temp);
171
+ }
172
+
173
+ /* all done, close the reparse point */
174
+ CloseHandle(file);
175
+ }
176
+ }
177
+
178
+ return rv;
179
+}
180
+
181
+int win32_symlink(const char *oldpath, const char *newpath){
182
+ fossilStat stat;
183
+ int created = 0;
184
+ DWORD flags = 0;
185
+ wchar_t *zMbcs;
186
+
187
+ /* does oldpath exist? is it a dir or a file? */
188
+ zMbcs = fossil_utf8_to_filename(oldpath);
189
+ if (win32_stat(zMbcs, &stat) == 0){
190
+ if (stat.st_mode == S_IFDIR)
191
+ flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
192
+ DeleteFile(newpath);
193
+ if (CreateSymbolicLink(newpath, oldpath, flags))
194
+ created = 1;
195
+ }
196
+ fossil_filename_free(zMbcs);
197
+
198
+ /* if the symlink was not created, create a plain text file */
199
+ if (!created){
200
+ Blob content;
201
+ blob_set(&content, oldpath);
202
+ blob_write_to_file(&content, newpath);
203
+ blob_reset(&content);
204
+ created = 1;
205
+ }
206
+
207
+ return created ? 0 : -1;
208
+}
209
+
210
+int win32_symlinks_supported(){
211
+ TOKEN_PRIVILEGES tp;
212
+ LUID luid;
213
+ HANDLE process, token;
214
+ DWORD status;
215
+
216
+ /* symlinks only supported on vista or greater */
217
+ if (!IsWindowsVistaOrGreater())
218
+ return 0;
219
+
220
+ /* next we need to check to see if the privilege is available */
221
+
222
+ /* can't check privilege if we can't lookup its value */
223
+ if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid))
224
+ return 0;
225
+
226
+ /* can't check privilege if we can't open the process token */
227
+ process = GetCurrentProcess();
228
+ if (!OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token))
229
+ return 0;
230
+
231
+ /* by this point, we have a process token and the privilege value */
232
+ /* try to enable the privilege then close the token */
233
+
234
+ tp.PrivilegeCount = 1;
235
+ tp.Privileges[0].Luid = luid;
236
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
237
+
238
+ AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
239
+ status = GetLastError();
240
+
241
+ CloseHandle(token);
242
+
243
+ /* any error means we failed to enable the privilege, symlinks not supported */
244
+ if (status != ERROR_SUCCESS)
245
+ return 0;
246
+
247
+ /* we made it this far, symlinks must be supported */
248
+ return 1;
50249
}
51250
52251
/*
53252
** Wrapper around the access() system call. This code was copied from Tcl
54253
** 8.6 and then modified.
55254
--- src/winfile.c
+++ src/winfile.c
@@ -21,34 +21,233 @@
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 isWd is TRUE and allow-symlinks setting is on.
35 **
36 */
37 int win32_stat(const wchar_t *zFilename, struct fossilStat *buf, int isWd){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38 WIN32_FILE_ATTRIBUTE_DATA attr;
39 int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr);
40 if( rc ){
41 ULARGE_INTEGER ull;
42 ull.LowPart = attr.ftLastWriteTime.dwLowDateTime;
43 ull.HighPart = attr.ftLastWriteTime.dwHighDateTime;
44 buf->st_mode = (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
45 S_IFDIR : S_IFREG;
46 buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow;
47 buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL;
48 }
49 return !rc;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50 }
51
52 /*
53 ** Wrapper around the access() system call. This code was copied from Tcl
54 ** 8.6 and then modified.
55
--- src/winfile.c
+++ src/winfile.c
@@ -21,34 +21,233 @@
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 <versionhelpers.h>
27 #include "winfile.h"
28
29 #ifndef LABEL_SECURITY_INFORMATION
30 # define LABEL_SECURITY_INFORMATION (0x00000010L)
31 #endif
32
33 /* copy & paste from ntifs.h */
34 typedef struct _REPARSE_DATA_BUFFER {
35 ULONG ReparseTag;
36 USHORT ReparseDataLength;
37 USHORT Reserved;
38 union {
39 struct {
40 USHORT SubstituteNameOffset;
41 USHORT SubstituteNameLength;
42 USHORT PrintNameOffset;
43 USHORT PrintNameLength;
44 ULONG Flags;
45 WCHAR PathBuffer[1];
46 } SymbolicLinkReparseBuffer;
47 struct {
48 USHORT SubstituteNameOffset;
49 USHORT SubstituteNameLength;
50 USHORT PrintNameOffset;
51 USHORT PrintNameLength;
52 WCHAR PathBuffer[1];
53 } MountPointReparseBuffer;
54 struct {
55 UCHAR DataBuffer[1];
56 } GenericReparseBuffer;
57 };
58 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
59
60 #define LINK_BUFFER_SIZE 1024
61
62 int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){
63 WIN32_FILE_ATTRIBUTE_DATA attr;
64 int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr);
65 if( rc ){
66 char *tname = fossil_filename_to_utf8(zFilename);
67 char tlink[LINK_BUFFER_SIZE];
68 ssize_t tlen = win32_readlink(tname, tlink, sizeof(tlink));
69 ULARGE_INTEGER ull;
70
71 buf->st_mode = (tlen > 0) ? S_IFLNK :
72 ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
73 S_IFDIR : S_IFREG);
74
75 buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow;
76
77 ull.LowPart = attr.ftLastWriteTime.dwLowDateTime;
78 ull.HighPart = attr.ftLastWriteTime.dwHighDateTime;
79 buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL;
80 }
81 return !rc;
82 }
83
84 /*
85 ** Fill stat buf with information received from stat() or lstat().
86 ** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on.
87 **
88 */
89 int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){
90 int rc;
91 HANDLE file;
92 wchar_t nextFilename[LINK_BUFFER_SIZE];
93 DWORD len;
94
95 while (1){
96 rc = win32_lstat(zFilename, buf);
97 /* exit on error or not link */
98 if ((rc != 0) || (buf->st_mode != S_IFLNK))
99 break;
100
101 /* it is a link, so open the linked file */
102 file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
103 if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){
104 rc = -1;
105 break;
106 }
107
108 /* get the final path name and close the handle */
109 len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0);
110 CloseHandle(file);
111
112 /* if any problems getting the final path name error so exit */
113 if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){
114 rc = -1;
115 break;
116 }
117
118 /* prepare to try again just in case we have a chain to follow */
119 /* this shouldn't happen, but just trying to be safe */
120 zFilename = nextFilename;
121 }
122
123 return rc;
124 }
125
126 ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){
127 /* assume we're going to fail */
128 ssize_t rv = -1;
129
130 /* does path reference a reparse point? */
131 WIN32_FILE_ATTRIBUTE_DATA attr;
132 int rc = GetFileAttributesEx(path, GetFileExInfoStandard, &attr);
133 if (rc && (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)){
134
135 /* since it is a reparse point, open it */
136 HANDLE file = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
137 FILE_FLAG_OPEN_REPARSE_POINT, NULL);
138 if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){
139
140 /* use DeviceIoControl to get the reparse point data */
141
142 union {
143 REPARSE_DATA_BUFFER data;
144 char buffer[sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t)];
145 } u;
146 DWORD bytes;
147
148 u.data.ReparseTag = IO_REPARSE_TAG_SYMLINK;
149 u.data.ReparseDataLength = 0;
150 u.data.Reserved = 0;
151
152 int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0,
153 &u, sizeof(u), &bytes, NULL);
154
155 /* did the reparse point data fit into the desired buffer? */
156 if (rc && (bytes < sizeof(u))){
157 /* it fit, so setup the print name for further processing */
158 USHORT
159 offset = u.data.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t),
160 length = u.data.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t);
161 char *temp;
162 u.data.SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0;
163
164 /* convert the filename to utf8, copy it, and discard the converted copy */
165 temp = fossil_filename_to_utf8(u.data.SymbolicLinkReparseBuffer.PathBuffer + offset);
166 rv = strlen(temp);
167 if (rv >= bufsiz)
168 rv = bufsiz;
169 memcpy(buf, temp, rv);
170 fossil_filename_free(temp);
171 }
172
173 /* all done, close the reparse point */
174 CloseHandle(file);
175 }
176 }
177
178 return rv;
179 }
180
181 int win32_symlink(const char *oldpath, const char *newpath){
182 fossilStat stat;
183 int created = 0;
184 DWORD flags = 0;
185 wchar_t *zMbcs;
186
187 /* does oldpath exist? is it a dir or a file? */
188 zMbcs = fossil_utf8_to_filename(oldpath);
189 if (win32_stat(zMbcs, &stat) == 0){
190 if (stat.st_mode == S_IFDIR)
191 flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
192 DeleteFile(newpath);
193 if (CreateSymbolicLink(newpath, oldpath, flags))
194 created = 1;
195 }
196 fossil_filename_free(zMbcs);
197
198 /* if the symlink was not created, create a plain text file */
199 if (!created){
200 Blob content;
201 blob_set(&content, oldpath);
202 blob_write_to_file(&content, newpath);
203 blob_reset(&content);
204 created = 1;
205 }
206
207 return created ? 0 : -1;
208 }
209
210 int win32_symlinks_supported(){
211 TOKEN_PRIVILEGES tp;
212 LUID luid;
213 HANDLE process, token;
214 DWORD status;
215
216 /* symlinks only supported on vista or greater */
217 if (!IsWindowsVistaOrGreater())
218 return 0;
219
220 /* next we need to check to see if the privilege is available */
221
222 /* can't check privilege if we can't lookup its value */
223 if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid))
224 return 0;
225
226 /* can't check privilege if we can't open the process token */
227 process = GetCurrentProcess();
228 if (!OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token))
229 return 0;
230
231 /* by this point, we have a process token and the privilege value */
232 /* try to enable the privilege then close the token */
233
234 tp.PrivilegeCount = 1;
235 tp.Privileges[0].Luid = luid;
236 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
237
238 AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
239 status = GetLastError();
240
241 CloseHandle(token);
242
243 /* any error means we failed to enable the privilege, symlinks not supported */
244 if (status != ERROR_SUCCESS)
245 return 0;
246
247 /* we made it this far, symlinks must be supported */
248 return 1;
249 }
250
251 /*
252 ** Wrapper around the access() system call. This code was copied from Tcl
253 ** 8.6 and then modified.
254

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button