Fossil SCM

Cleaning up some source comments & such. Also modified a memory block to be on the heap instead of the stack.

sdr 2014-09-21 02:05 bad-winsymlink
Commit 30ff96e7a5c4ff78e69655bfac0c9282bfef33d5
2 files changed +12 +99 -38
+12
--- src/file.c
+++ src/file.c
@@ -242,10 +242,22 @@
242242
blob_write_to_file(&content, zLinkFile);
243243
blob_reset(&content);
244244
}
245245
}
246246
247
+/*
248
+** This code is used in multiple places around fossil. Rather than having
249
+** four or five slightly different cut & paste chunks of code, this function
250
+** does the task of deleting a (possible) link (if necessary) and then
251
+** either creating a link or a file based on input parameters.
252
+** mayNeedDelete is true if we might need to call link_delete, false otherwise.
253
+** needLink is true if we need to create a symlink, false otherwise.
254
+** mayBeLink is true if zName might be a symlink based on the state of the
255
+** checkout and/or file system, false otherwise.
256
+** blob is the binary data to either write as a symlink or as a file.
257
+** zName is the name of the file or symlink to write.
258
+*/
247259
void create_symlink_or_file(int mayNeedDelete, int needLink, int mayBeLink, Blob* blob, const char* zName){
248260
if (mayNeedDelete && (needLink || mayBeLink))
249261
link_delete(zName);
250262
if (needLink)
251263
symlink_create(blob_str(blob), zName);
252264
--- src/file.c
+++ src/file.c
@@ -242,10 +242,22 @@
242 blob_write_to_file(&content, zLinkFile);
243 blob_reset(&content);
244 }
245 }
246
 
 
 
 
 
 
 
 
 
 
 
 
247 void create_symlink_or_file(int mayNeedDelete, int needLink, int mayBeLink, Blob* blob, const char* zName){
248 if (mayNeedDelete && (needLink || mayBeLink))
249 link_delete(zName);
250 if (needLink)
251 symlink_create(blob_str(blob), zName);
252
--- src/file.c
+++ src/file.c
@@ -242,10 +242,22 @@
242 blob_write_to_file(&content, zLinkFile);
243 blob_reset(&content);
244 }
245 }
246
247 /*
248 ** This code is used in multiple places around fossil. Rather than having
249 ** four or five slightly different cut & paste chunks of code, this function
250 ** does the task of deleting a (possible) link (if necessary) and then
251 ** either creating a link or a file based on input parameters.
252 ** mayNeedDelete is true if we might need to call link_delete, false otherwise.
253 ** needLink is true if we need to create a symlink, false otherwise.
254 ** mayBeLink is true if zName might be a symlink based on the state of the
255 ** checkout and/or file system, false otherwise.
256 ** blob is the binary data to either write as a symlink or as a file.
257 ** zName is the name of the file or symlink to write.
258 */
259 void create_symlink_or_file(int mayNeedDelete, int needLink, int mayBeLink, Blob* blob, const char* zName){
260 if (mayNeedDelete && (needLink || mayBeLink))
261 link_delete(zName);
262 if (needLink)
263 symlink_create(blob_str(blob), zName);
264
+99 -38
--- src/winfile.c
+++ src/winfile.c
@@ -28,48 +28,72 @@
2828
2929
#ifndef LABEL_SECURITY_INFORMATION
3030
# define LABEL_SECURITY_INFORMATION (0x00000010L)
3131
#endif
3232
33
-/* copy & paste from ntifs.h */
33
+/* a couple defines to make the borrowed struct below compile */
34
+#define _ANONYMOUS_UNION
35
+#define DUMMYUNIONNAME
36
+
37
+/*
38
+** this structure copied on 20 Sept 2014 from
39
+** https://reactos-mirror.googlecode.com/svn-history/r54752/branches/usb-bringup/include/ddk/ntifs.h
40
+** which is a public domain file from the ReactOS DDK package.
41
+*/
42
+
3443
typedef struct _REPARSE_DATA_BUFFER {
35
- ULONG ReparseTag;
44
+ ULONG ReparseTag;
3645
USHORT ReparseDataLength;
3746
USHORT Reserved;
38
- union {
47
+ _ANONYMOUS_UNION union {
3948
struct {
4049
USHORT SubstituteNameOffset;
4150
USHORT SubstituteNameLength;
4251
USHORT PrintNameOffset;
4352
USHORT PrintNameLength;
44
- ULONG Flags;
45
- WCHAR PathBuffer[1];
53
+ ULONG Flags;
54
+ WCHAR PathBuffer[1];
4655
} SymbolicLinkReparseBuffer;
4756
struct {
4857
USHORT SubstituteNameOffset;
4958
USHORT SubstituteNameLength;
5059
USHORT PrintNameOffset;
5160
USHORT PrintNameLength;
52
- WCHAR PathBuffer[1];
61
+ WCHAR PathBuffer[1];
5362
} MountPointReparseBuffer;
5463
struct {
5564
UCHAR DataBuffer[1];
5665
} GenericReparseBuffer;
57
- };
66
+ } DUMMYUNIONNAME;
5867
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
5968
6069
#define LINK_BUFFER_SIZE 1024
6170
71
+/*
72
+** Fill stat buf with information received from GetFileAttributesExW().
73
+** Does not follow symbolic links, returning instead information about
74
+** the link itself.
75
+** Returns 0 on success, 1 on failure.
76
+*/
6277
int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){
6378
WIN32_FILE_ATTRIBUTE_DATA attr;
6479
int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr);
6580
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));
81
+ ssize_t tlen = 0; /* assume it is not a symbolic link */
82
+
83
+ /* if it is a reparse point it *might* be a symbolic link */
84
+ /* so defer to win32_readlink to actually check */
85
+ if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){
86
+ char *tname = fossil_filename_to_utf8(zFilename);
87
+ char tlink[LINK_BUFFER_SIZE];
88
+ tlen = win32_readlink(tname, tlink, sizeof(tlink));
89
+ fossil_filename_free(tname);
90
+ }
91
+
6992
ULARGE_INTEGER ull;
7093
94
+ /* if a link was retrieved, it is a symlink, otherwise a dir or file */
7195
buf->st_mode = (tlen > 0) ? S_IFLNK :
7296
((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
7397
S_IFDIR : S_IFREG);
7498
7599
buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow;
@@ -80,40 +104,47 @@
80104
}
81105
return !rc;
82106
}
83107
84108
/*
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
-**
109
+** Fill stat buf with information received from win32_lstat().
110
+** If a symbolic link is found, follow it and return information about
111
+** the target, repeating until an actual target is found.
112
+** Limit the number of loop iterations so as to avoid an infinite loop
113
+** due to circular links. This should never happen because
114
+** GetFinalPathNameByHandleW() should always preclude that need, but being
115
+** prepared to loop seems prudent, or at least not harmful.
116
+** Returns 0 on success, 1 on failure.
88117
*/
89118
int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){
90119
int rc;
91120
HANDLE file;
92121
wchar_t nextFilename[LINK_BUFFER_SIZE];
93122
DWORD len;
123
+ int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */
94124
95
- while (1){
125
+ while (iterationsRemaining-- > 0){
96126
rc = win32_lstat(zFilename, buf);
127
+
97128
/* exit on error or not link */
98129
if ((rc != 0) || (buf->st_mode != S_IFLNK))
99130
break;
100131
101132
/* it is a link, so open the linked file */
102133
file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
103134
if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){
104
- rc = -1;
135
+ rc = 1;
105136
break;
106137
}
107138
108139
/* get the final path name and close the handle */
109140
len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0);
110141
CloseHandle(file);
111142
112143
/* if any problems getting the final path name error so exit */
113144
if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){
114
- rc = -1;
145
+ rc = 1;
115146
break;
116147
}
117148
118149
/* prepare to try again just in case we have a chain to follow */
119150
/* this shouldn't happen, but just trying to be safe */
@@ -121,10 +152,15 @@
121152
}
122153
123154
return rc;
124155
}
125156
157
+/*
158
+** An implementation of a posix-like readlink function for win32.
159
+** Copies the target of a symbolic link to buf if possible.
160
+** Returns the length of the link copied to buf on success, -1 on failure.
161
+*/
126162
ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){
127163
/* assume we're going to fail */
128164
ssize_t rv = -1;
129165
130166
/* does path reference a reparse point? */
@@ -137,61 +173,79 @@
137173
FILE_FLAG_OPEN_REPARSE_POINT, NULL);
138174
if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){
139175
140176
/* use DeviceIoControl to get the reparse point data */
141177
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;
178
+ int data_size = sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t);
179
+ REPARSE_DATA_BUFFER* data = fossil_malloc(data_size);
180
+ DWORD data_used;
181
+
182
+ data->ReparseTag = IO_REPARSE_TAG_SYMLINK;
183
+ data->ReparseDataLength = 0;
184
+ data->Reserved = 0;
151185
152186
int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0,
153
- &u, sizeof(u), &bytes, NULL);
187
+ data, data_size, &data_used, NULL);
154188
155189
/* did the reparse point data fit into the desired buffer? */
156
- if (rc && (bytes < sizeof(u))){
190
+ if (rc && (data_used < data_size)){
157191
/* it fit, so setup the print name for further processing */
158192
USHORT
159
- offset = u.data.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t),
160
- length = u.data.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t);
193
+ offset = data->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t),
194
+ length = data->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t);
161195
char *temp;
162
- u.data.SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0;
196
+ data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0;
163197
164198
/* convert the filename to utf8, copy it, and discard the converted copy */
165
- temp = fossil_filename_to_utf8(u.data.SymbolicLinkReparseBuffer.PathBuffer + offset);
199
+ temp = fossil_filename_to_utf8(data->SymbolicLinkReparseBuffer.PathBuffer + offset);
166200
rv = strlen(temp);
167201
if (rv >= bufsiz)
168202
rv = bufsiz;
169203
memcpy(buf, temp, rv);
170204
fossil_filename_free(temp);
171205
}
206
+
207
+ fossil_free(data);
172208
173209
/* all done, close the reparse point */
174210
CloseHandle(file);
175211
}
176212
}
177213
178214
return rv;
179215
}
180216
217
+/*
218
+** Either unlink a file or remove a directory on win32 systems.
219
+** To delete a symlink on a posix system, you simply unlink the entry.
220
+** Unfortunately for our purposes, win32 differentiates between symlinks for
221
+** files and for directories. Thus you must unlink a file symlink or rmdir a
222
+** directory symlink. This is a convenience function used when we know we're
223
+** deleting a symlink of some type.
224
+** Returns 0 on success, 1 on failure.
225
+*/
181226
int win32_unlink_rmdir(const wchar_t *zFilename){
182
- int rc = -1;
227
+ int rc = 0;
183228
fossilStat stat;
184229
if (win32_stat(zFilename, &stat) == 0){
185230
if (stat.st_mode == S_IFDIR)
186
- rc = RemoveDirectoryW(zFilename) ? 0 : -1;
231
+ rc = RemoveDirectoryW(zFilename);
187232
else
188
- rc = DeleteFileW(zFilename) ? 0 : -1;
233
+ rc = DeleteFileW(zFilename);
189234
}
190
- return rc;
235
+ return !rc;
191236
}
192237
238
+/*
239
+** An implementation of a posix-like symlink function for win32.
240
+** Attempts to create a file or directory symlink based on the target.
241
+** Defaults to a file symlink if the target does not exist / can't be checked.
242
+** Finally, if the symlink cannot be created for whatever reason (perhaps
243
+** newpath is on a network share or a FAT derived file system), default to
244
+** creation of a text file with the context of the link.
245
+** Returns 0 on success, 1 on failure.
246
+*/
193247
int win32_symlink(const char *oldpath, const char *newpath){
194248
fossilStat stat;
195249
int created = 0;
196250
DWORD flags = 0;
197251
wchar_t *zMbcs;
@@ -207,12 +261,11 @@
207261
/* remove newpath before creating the symlink */
208262
zMbcs = fossil_utf8_to_filename(newpath);
209263
win32_unlink_rmdir(zMbcs);
210264
fossil_filename_free(zMbcs);
211265
212
- if (CreateSymbolicLink(newpath, oldpath, flags))
213
- created = 1;
266
+ created = CreateSymbolicLink(newpath, oldpath, flags);
214267
215268
/* if the symlink was not created, create a plain text file */
216269
if (!created){
217270
Blob content;
218271
blob_set(&content, oldpath);
@@ -219,13 +272,21 @@
219272
blob_write_to_file(&content, newpath);
220273
blob_reset(&content);
221274
created = 1;
222275
}
223276
224
- return created ? 0 : -1;
277
+ return !created;
225278
}
226279
280
+/*
281
+** Check if symlinks are potentially supported on the current OS.
282
+** Theoretically this code should work on any NT based version of windows
283
+** but I have no way of testing that. The initial check for
284
+** IsWindowsVistaOrGreater() should in theory eliminate any system prior to
285
+** Windows Vista, but I have no way to test that at this time.
286
+** Return 1 if supported, 0 if not.
287
+*/
227288
int win32_symlinks_supported(){
228289
TOKEN_PRIVILEGES tp;
229290
LUID luid;
230291
HANDLE process, token;
231292
DWORD status;
232293
--- src/winfile.c
+++ src/winfile.c
@@ -28,48 +28,72 @@
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;
@@ -80,40 +104,47 @@
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 */
@@ -121,10 +152,15 @@
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? */
@@ -137,61 +173,79 @@
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_unlink_rmdir(const wchar_t *zFilename){
182 int rc = -1;
183 fossilStat stat;
184 if (win32_stat(zFilename, &stat) == 0){
185 if (stat.st_mode == S_IFDIR)
186 rc = RemoveDirectoryW(zFilename) ? 0 : -1;
187 else
188 rc = DeleteFileW(zFilename) ? 0 : -1;
189 }
190 return rc;
191 }
192
 
 
 
 
 
 
 
 
 
193 int win32_symlink(const char *oldpath, const char *newpath){
194 fossilStat stat;
195 int created = 0;
196 DWORD flags = 0;
197 wchar_t *zMbcs;
@@ -207,12 +261,11 @@
207 /* remove newpath before creating the symlink */
208 zMbcs = fossil_utf8_to_filename(newpath);
209 win32_unlink_rmdir(zMbcs);
210 fossil_filename_free(zMbcs);
211
212 if (CreateSymbolicLink(newpath, oldpath, flags))
213 created = 1;
214
215 /* if the symlink was not created, create a plain text file */
216 if (!created){
217 Blob content;
218 blob_set(&content, oldpath);
@@ -219,13 +272,21 @@
219 blob_write_to_file(&content, newpath);
220 blob_reset(&content);
221 created = 1;
222 }
223
224 return created ? 0 : -1;
225 }
226
 
 
 
 
 
 
 
 
227 int win32_symlinks_supported(){
228 TOKEN_PRIVILEGES tp;
229 LUID luid;
230 HANDLE process, token;
231 DWORD status;
232
--- src/winfile.c
+++ src/winfile.c
@@ -28,48 +28,72 @@
28
29 #ifndef LABEL_SECURITY_INFORMATION
30 # define LABEL_SECURITY_INFORMATION (0x00000010L)
31 #endif
32
33 /* a couple defines to make the borrowed struct below compile */
34 #define _ANONYMOUS_UNION
35 #define DUMMYUNIONNAME
36
37 /*
38 ** this structure copied on 20 Sept 2014 from
39 ** https://reactos-mirror.googlecode.com/svn-history/r54752/branches/usb-bringup/include/ddk/ntifs.h
40 ** which is a public domain file from the ReactOS DDK package.
41 */
42
43 typedef struct _REPARSE_DATA_BUFFER {
44 ULONG ReparseTag;
45 USHORT ReparseDataLength;
46 USHORT Reserved;
47 _ANONYMOUS_UNION union {
48 struct {
49 USHORT SubstituteNameOffset;
50 USHORT SubstituteNameLength;
51 USHORT PrintNameOffset;
52 USHORT PrintNameLength;
53 ULONG Flags;
54 WCHAR PathBuffer[1];
55 } SymbolicLinkReparseBuffer;
56 struct {
57 USHORT SubstituteNameOffset;
58 USHORT SubstituteNameLength;
59 USHORT PrintNameOffset;
60 USHORT PrintNameLength;
61 WCHAR PathBuffer[1];
62 } MountPointReparseBuffer;
63 struct {
64 UCHAR DataBuffer[1];
65 } GenericReparseBuffer;
66 } DUMMYUNIONNAME;
67 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
68
69 #define LINK_BUFFER_SIZE 1024
70
71 /*
72 ** Fill stat buf with information received from GetFileAttributesExW().
73 ** Does not follow symbolic links, returning instead information about
74 ** the link itself.
75 ** Returns 0 on success, 1 on failure.
76 */
77 int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){
78 WIN32_FILE_ATTRIBUTE_DATA attr;
79 int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr);
80 if( rc ){
81 ssize_t tlen = 0; /* assume it is not a symbolic link */
82
83 /* if it is a reparse point it *might* be a symbolic link */
84 /* so defer to win32_readlink to actually check */
85 if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){
86 char *tname = fossil_filename_to_utf8(zFilename);
87 char tlink[LINK_BUFFER_SIZE];
88 tlen = win32_readlink(tname, tlink, sizeof(tlink));
89 fossil_filename_free(tname);
90 }
91
92 ULARGE_INTEGER ull;
93
94 /* if a link was retrieved, it is a symlink, otherwise a dir or file */
95 buf->st_mode = (tlen > 0) ? S_IFLNK :
96 ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
97 S_IFDIR : S_IFREG);
98
99 buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow;
@@ -80,40 +104,47 @@
104 }
105 return !rc;
106 }
107
108 /*
109 ** Fill stat buf with information received from win32_lstat().
110 ** If a symbolic link is found, follow it and return information about
111 ** the target, repeating until an actual target is found.
112 ** Limit the number of loop iterations so as to avoid an infinite loop
113 ** due to circular links. This should never happen because
114 ** GetFinalPathNameByHandleW() should always preclude that need, but being
115 ** prepared to loop seems prudent, or at least not harmful.
116 ** Returns 0 on success, 1 on failure.
117 */
118 int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){
119 int rc;
120 HANDLE file;
121 wchar_t nextFilename[LINK_BUFFER_SIZE];
122 DWORD len;
123 int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */
124
125 while (iterationsRemaining-- > 0){
126 rc = win32_lstat(zFilename, buf);
127
128 /* exit on error or not link */
129 if ((rc != 0) || (buf->st_mode != S_IFLNK))
130 break;
131
132 /* it is a link, so open the linked file */
133 file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
134 if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){
135 rc = 1;
136 break;
137 }
138
139 /* get the final path name and close the handle */
140 len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0);
141 CloseHandle(file);
142
143 /* if any problems getting the final path name error so exit */
144 if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){
145 rc = 1;
146 break;
147 }
148
149 /* prepare to try again just in case we have a chain to follow */
150 /* this shouldn't happen, but just trying to be safe */
@@ -121,10 +152,15 @@
152 }
153
154 return rc;
155 }
156
157 /*
158 ** An implementation of a posix-like readlink function for win32.
159 ** Copies the target of a symbolic link to buf if possible.
160 ** Returns the length of the link copied to buf on success, -1 on failure.
161 */
162 ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){
163 /* assume we're going to fail */
164 ssize_t rv = -1;
165
166 /* does path reference a reparse point? */
@@ -137,61 +173,79 @@
173 FILE_FLAG_OPEN_REPARSE_POINT, NULL);
174 if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){
175
176 /* use DeviceIoControl to get the reparse point data */
177
178 int data_size = sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t);
179 REPARSE_DATA_BUFFER* data = fossil_malloc(data_size);
180 DWORD data_used;
181
182 data->ReparseTag = IO_REPARSE_TAG_SYMLINK;
183 data->ReparseDataLength = 0;
184 data->Reserved = 0;
 
 
185
186 int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0,
187 data, data_size, &data_used, NULL);
188
189 /* did the reparse point data fit into the desired buffer? */
190 if (rc && (data_used < data_size)){
191 /* it fit, so setup the print name for further processing */
192 USHORT
193 offset = data->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t),
194 length = data->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t);
195 char *temp;
196 data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0;
197
198 /* convert the filename to utf8, copy it, and discard the converted copy */
199 temp = fossil_filename_to_utf8(data->SymbolicLinkReparseBuffer.PathBuffer + offset);
200 rv = strlen(temp);
201 if (rv >= bufsiz)
202 rv = bufsiz;
203 memcpy(buf, temp, rv);
204 fossil_filename_free(temp);
205 }
206
207 fossil_free(data);
208
209 /* all done, close the reparse point */
210 CloseHandle(file);
211 }
212 }
213
214 return rv;
215 }
216
217 /*
218 ** Either unlink a file or remove a directory on win32 systems.
219 ** To delete a symlink on a posix system, you simply unlink the entry.
220 ** Unfortunately for our purposes, win32 differentiates between symlinks for
221 ** files and for directories. Thus you must unlink a file symlink or rmdir a
222 ** directory symlink. This is a convenience function used when we know we're
223 ** deleting a symlink of some type.
224 ** Returns 0 on success, 1 on failure.
225 */
226 int win32_unlink_rmdir(const wchar_t *zFilename){
227 int rc = 0;
228 fossilStat stat;
229 if (win32_stat(zFilename, &stat) == 0){
230 if (stat.st_mode == S_IFDIR)
231 rc = RemoveDirectoryW(zFilename);
232 else
233 rc = DeleteFileW(zFilename);
234 }
235 return !rc;
236 }
237
238 /*
239 ** An implementation of a posix-like symlink function for win32.
240 ** Attempts to create a file or directory symlink based on the target.
241 ** Defaults to a file symlink if the target does not exist / can't be checked.
242 ** Finally, if the symlink cannot be created for whatever reason (perhaps
243 ** newpath is on a network share or a FAT derived file system), default to
244 ** creation of a text file with the context of the link.
245 ** Returns 0 on success, 1 on failure.
246 */
247 int win32_symlink(const char *oldpath, const char *newpath){
248 fossilStat stat;
249 int created = 0;
250 DWORD flags = 0;
251 wchar_t *zMbcs;
@@ -207,12 +261,11 @@
261 /* remove newpath before creating the symlink */
262 zMbcs = fossil_utf8_to_filename(newpath);
263 win32_unlink_rmdir(zMbcs);
264 fossil_filename_free(zMbcs);
265
266 created = CreateSymbolicLink(newpath, oldpath, flags);
 
267
268 /* if the symlink was not created, create a plain text file */
269 if (!created){
270 Blob content;
271 blob_set(&content, oldpath);
@@ -219,13 +272,21 @@
272 blob_write_to_file(&content, newpath);
273 blob_reset(&content);
274 created = 1;
275 }
276
277 return !created;
278 }
279
280 /*
281 ** Check if symlinks are potentially supported on the current OS.
282 ** Theoretically this code should work on any NT based version of windows
283 ** but I have no way of testing that. The initial check for
284 ** IsWindowsVistaOrGreater() should in theory eliminate any system prior to
285 ** Windows Vista, but I have no way to test that at this time.
286 ** Return 1 if supported, 0 if not.
287 */
288 int win32_symlinks_supported(){
289 TOKEN_PRIVILEGES tp;
290 LUID luid;
291 HANDLE process, token;
292 DWORD status;
293

Keyboard Shortcuts

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