Fossil SCM

Cherrypicked a number of commits from a "bad branch" (I somehow fouled up a merge from trunk).

sdr 2014-09-23 00:15 winsymlink
Commit 2d75e87b760c0a991fc3e4e8151d001bfef998e6
+6 -1
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
11141114
*/
11151115
if( isSelected ){
11161116
int mPerm;
11171117
11181118
mPerm = file_wd_perm(blob_str(&filename));
1119
+#if !defined(_WIN32)
11191120
isExe = ( mPerm==PERM_EXE );
1120
- isLink = ( mPerm==PERM_LNK );
1121
+#endif
1122
+#if defined(_WIN32)
1123
+ if (win32_symlinks_supported())
1124
+#endif
1125
+ isLink = ( mPerm==PERM_LNK );
11211126
}
11221127
11231128
if( isExe ){
11241129
zPerm = " x";
11251130
}else if( isLink ){
11261131
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
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
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
1114 */
1115 if( isSelected ){
1116 int mPerm;
1117
1118 mPerm = file_wd_perm(blob_str(&filename));
1119 #if !defined(_WIN32)
1120 isExe = ( mPerm==PERM_EXE );
1121 #endif
1122 #if defined(_WIN32)
1123 if (win32_symlinks_supported())
1124 #endif
1125 isLink = ( mPerm==PERM_LNK );
1126 }
1127
1128 if( isExe ){
1129 zPerm = " x";
1130 }else if( isLink ){
1131
+6 -1
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
11141114
*/
11151115
if( isSelected ){
11161116
int mPerm;
11171117
11181118
mPerm = file_wd_perm(blob_str(&filename));
1119
+#if !defined(_WIN32)
11191120
isExe = ( mPerm==PERM_EXE );
1120
- isLink = ( mPerm==PERM_LNK );
1121
+#endif
1122
+#if defined(_WIN32)
1123
+ if (win32_symlinks_supported())
1124
+#endif
1125
+ isLink = ( mPerm==PERM_LNK );
11211126
}
11221127
11231128
if( isExe ){
11241129
zPerm = " x";
11251130
}else if( isLink ){
11261131
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
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
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
1114 */
1115 if( isSelected ){
1116 int mPerm;
1117
1118 mPerm = file_wd_perm(blob_str(&filename));
1119 #if !defined(_WIN32)
1120 isExe = ( mPerm==PERM_EXE );
1121 #endif
1122 #if defined(_WIN32)
1123 if (win32_symlinks_supported())
1124 #endif
1125 isLink = ( mPerm==PERM_LNK );
1126 }
1127
1128 if( isExe ){
1129 zPerm = " x";
1130 }else if( isLink ){
1131
+6 -1
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
11141114
*/
11151115
if( isSelected ){
11161116
int mPerm;
11171117
11181118
mPerm = file_wd_perm(blob_str(&filename));
1119
+#if !defined(_WIN32)
11191120
isExe = ( mPerm==PERM_EXE );
1120
- isLink = ( mPerm==PERM_LNK );
1121
+#endif
1122
+#if defined(_WIN32)
1123
+ if (win32_symlinks_supported())
1124
+#endif
1125
+ isLink = ( mPerm==PERM_LNK );
11211126
}
11221127
11231128
if( isExe ){
11241129
zPerm = " x";
11251130
}else if( isLink ){
11261131
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
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
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
1114 */
1115 if( isSelected ){
1116 int mPerm;
1117
1118 mPerm = file_wd_perm(blob_str(&filename));
1119 #if !defined(_WIN32)
1120 isExe = ( mPerm==PERM_EXE );
1121 #endif
1122 #if defined(_WIN32)
1123 if (win32_symlinks_supported())
1124 #endif
1125 isLink = ( mPerm==PERM_LNK );
1126 }
1127
1128 if( isExe ){
1129 zPerm = " x";
1130 }else if( isLink ){
1131
+6 -1
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
11141114
*/
11151115
if( isSelected ){
11161116
int mPerm;
11171117
11181118
mPerm = file_wd_perm(blob_str(&filename));
1119
+#if !defined(_WIN32)
11191120
isExe = ( mPerm==PERM_EXE );
1120
- isLink = ( mPerm==PERM_LNK );
1121
+#endif
1122
+#if defined(_WIN32)
1123
+ if (win32_symlinks_supported())
1124
+#endif
1125
+ isLink = ( mPerm==PERM_LNK );
11211126
}
11221127
11231128
if( isExe ){
11241129
zPerm = " x";
11251130
}else if( isLink ){
11261131
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
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
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
1114 */
1115 if( isSelected ){
1116 int mPerm;
1117
1118 mPerm = file_wd_perm(blob_str(&filename));
1119 #if !defined(_WIN32)
1120 isExe = ( mPerm==PERM_EXE );
1121 #endif
1122 #if defined(_WIN32)
1123 if (win32_symlinks_supported())
1124 #endif
1125 isLink = ( mPerm==PERM_LNK );
1126 }
1127
1128 if( isExe ){
1129 zPerm = " x";
1130 }else if( isLink ){
1131
+6 -1
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
11141114
*/
11151115
if( isSelected ){
11161116
int mPerm;
11171117
11181118
mPerm = file_wd_perm(blob_str(&filename));
1119
+#if !defined(_WIN32)
11191120
isExe = ( mPerm==PERM_EXE );
1120
- isLink = ( mPerm==PERM_LNK );
1121
+#endif
1122
+#if defined(_WIN32)
1123
+ if (win32_symlinks_supported())
1124
+#endif
1125
+ isLink = ( mPerm==PERM_LNK );
11211126
}
11221127
11231128
if( isExe ){
11241129
zPerm = " x";
11251130
}else if( isLink ){
11261131
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
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
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
1114 */
1115 if( isSelected ){
1116 int mPerm;
1117
1118 mPerm = file_wd_perm(blob_str(&filename));
1119 #if !defined(_WIN32)
1120 isExe = ( mPerm==PERM_EXE );
1121 #endif
1122 #if defined(_WIN32)
1123 if (win32_symlinks_supported())
1124 #endif
1125 isLink = ( mPerm==PERM_LNK );
1126 }
1127
1128 if( isExe ){
1129 zPerm = " x";
1130 }else if( isLink ){
1131
+6 -1
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
11141114
*/
11151115
if( isSelected ){
11161116
int mPerm;
11171117
11181118
mPerm = file_wd_perm(blob_str(&filename));
1119
+#if !defined(_WIN32)
11191120
isExe = ( mPerm==PERM_EXE );
1120
- isLink = ( mPerm==PERM_LNK );
1121
+#endif
1122
+#if defined(_WIN32)
1123
+ if (win32_symlinks_supported())
1124
+#endif
1125
+ isLink = ( mPerm==PERM_LNK );
11211126
}
11221127
11231128
if( isExe ){
11241129
zPerm = " x";
11251130
}else if( isLink ){
11261131
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
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
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
1114 */
1115 if( isSelected ){
1116 int mPerm;
1117
1118 mPerm = file_wd_perm(blob_str(&filename));
1119 #if !defined(_WIN32)
1120 isExe = ( mPerm==PERM_EXE );
1121 #endif
1122 #if defined(_WIN32)
1123 if (win32_symlinks_supported())
1124 #endif
1125 isLink = ( mPerm==PERM_LNK );
1126 }
1127
1128 if( isExe ){
1129 zPerm = " x";
1130 }else if( isLink ){
1131
+6 -1
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
11141114
*/
11151115
if( isSelected ){
11161116
int mPerm;
11171117
11181118
mPerm = file_wd_perm(blob_str(&filename));
1119
+#if !defined(_WIN32)
11191120
isExe = ( mPerm==PERM_EXE );
1120
- isLink = ( mPerm==PERM_LNK );
1121
+#endif
1122
+#if defined(_WIN32)
1123
+ if (win32_symlinks_supported())
1124
+#endif
1125
+ isLink = ( mPerm==PERM_LNK );
11211126
}
11221127
11231128
if( isExe ){
11241129
zPerm = " x";
11251130
}else if( isLink ){
11261131
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
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
--- src/checkin.c
+++ src/checkin.c
@@ -1114,12 +1114,17 @@
1114 */
1115 if( isSelected ){
1116 int mPerm;
1117
1118 mPerm = file_wd_perm(blob_str(&filename));
1119 #if !defined(_WIN32)
1120 isExe = ( mPerm==PERM_EXE );
1121 #endif
1122 #if defined(_WIN32)
1123 if (win32_symlinks_supported())
1124 #endif
1125 isLink = ( mPerm==PERM_LNK );
1126 }
1127
1128 if( isExe ){
1129 zPerm = " x";
1130 }else if( isLink ){
1131
+50
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207207
}
208208
nName = file_simplify_name(zName, nName, 0);
209209
for(i=1; i<nName; i++){
210210
if( zName[i]=='/' ){
211211
zName[i] = 0;
212
+#if defined(_WIN32) || defined(__CYGWIN__)
213
+ /*
214
+ ** On Windows, local path looks like: C:/develop/project/file.txt
215
+ ** The if stops us from trying to create a directory of a drive letter
216
+ ** C: in this example.
217
+ */
218
+ if (!(i == 2 && zName[1] == ':')){
219
+#endif
212220
if( file_mkdir(zName, 1) ){
213221
fossil_fatal_recursive("unable to create directory %s", zName);
214222
return;
215223
}
224
+#if defined(_WIN32) || defined(__CYGWIN__)
225
+ }
226
+#endif
216227
zName[i] = '/';
217228
}
218229
}
219230
#if !defined(_WIN32)
220231
if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230241
blob_set(&content, zTargetFile);
231242
blob_write_to_file(&content, zLinkFile);
232243
blob_reset(&content);
233244
}
234245
}
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
+ else
265
+ blob_write_to_file(blob, zName);
266
+}
235267
236268
/*
237269
** Copy symbolic link from zFrom to zTo.
238270
*/
239271
void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509541
int file_delete(const char *zFilename){
510542
int rc;
511543
#ifdef _WIN32
512544
wchar_t *z = fossil_utf8_to_filename(zFilename);
513545
rc = _wunlink(z);
546
+#else
547
+ char *z = fossil_utf8_to_filename(zFilename);
548
+ rc = unlink(zFilename);
549
+#endif
550
+ fossil_filename_free(z);
551
+ return rc;
552
+}
553
+
554
+/*
555
+** Delete a link.
556
+**
557
+** Returns zero upon success.
558
+*/
559
+int link_delete(const char *zFilename){
560
+ int rc;
561
+#ifdef _WIN32
562
+ wchar_t *z = fossil_utf8_to_filename(zFilename);
563
+ rc = win32_unlink_rmdir(z);
514564
#else
515565
char *z = fossil_utf8_to_filename(zFilename);
516566
rc = unlink(zFilename);
517567
#endif
518568
fossil_filename_free(z);
519569
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
 
 
 
 
 
 
 
 
212 if( file_mkdir(zName, 1) ){
213 fossil_fatal_recursive("unable to create directory %s", zName);
214 return;
215 }
 
 
 
216 zName[i] = '/';
217 }
218 }
219 #if !defined(_WIN32)
220 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230 blob_set(&content, zTargetFile);
231 blob_write_to_file(&content, zLinkFile);
232 blob_reset(&content);
233 }
234 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
236 /*
237 ** Copy symbolic link from zFrom to zTo.
238 */
239 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509 int file_delete(const char *zFilename){
510 int rc;
511 #ifdef _WIN32
512 wchar_t *z = fossil_utf8_to_filename(zFilename);
513 rc = _wunlink(z);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514 #else
515 char *z = fossil_utf8_to_filename(zFilename);
516 rc = unlink(zFilename);
517 #endif
518 fossil_filename_free(z);
519
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
212 #if defined(_WIN32) || defined(__CYGWIN__)
213 /*
214 ** On Windows, local path looks like: C:/develop/project/file.txt
215 ** The if stops us from trying to create a directory of a drive letter
216 ** C: in this example.
217 */
218 if (!(i == 2 && zName[1] == ':')){
219 #endif
220 if( file_mkdir(zName, 1) ){
221 fossil_fatal_recursive("unable to create directory %s", zName);
222 return;
223 }
224 #if defined(_WIN32) || defined(__CYGWIN__)
225 }
226 #endif
227 zName[i] = '/';
228 }
229 }
230 #if !defined(_WIN32)
231 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
241 blob_set(&content, zTargetFile);
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 else
265 blob_write_to_file(blob, zName);
266 }
267
268 /*
269 ** Copy symbolic link from zFrom to zTo.
270 */
271 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
541 int file_delete(const char *zFilename){
542 int rc;
543 #ifdef _WIN32
544 wchar_t *z = fossil_utf8_to_filename(zFilename);
545 rc = _wunlink(z);
546 #else
547 char *z = fossil_utf8_to_filename(zFilename);
548 rc = unlink(zFilename);
549 #endif
550 fossil_filename_free(z);
551 return rc;
552 }
553
554 /*
555 ** Delete a link.
556 **
557 ** Returns zero upon success.
558 */
559 int link_delete(const char *zFilename){
560 int rc;
561 #ifdef _WIN32
562 wchar_t *z = fossil_utf8_to_filename(zFilename);
563 rc = win32_unlink_rmdir(z);
564 #else
565 char *z = fossil_utf8_to_filename(zFilename);
566 rc = unlink(zFilename);
567 #endif
568 fossil_filename_free(z);
569
+50
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207207
}
208208
nName = file_simplify_name(zName, nName, 0);
209209
for(i=1; i<nName; i++){
210210
if( zName[i]=='/' ){
211211
zName[i] = 0;
212
+#if defined(_WIN32) || defined(__CYGWIN__)
213
+ /*
214
+ ** On Windows, local path looks like: C:/develop/project/file.txt
215
+ ** The if stops us from trying to create a directory of a drive letter
216
+ ** C: in this example.
217
+ */
218
+ if (!(i == 2 && zName[1] == ':')){
219
+#endif
212220
if( file_mkdir(zName, 1) ){
213221
fossil_fatal_recursive("unable to create directory %s", zName);
214222
return;
215223
}
224
+#if defined(_WIN32) || defined(__CYGWIN__)
225
+ }
226
+#endif
216227
zName[i] = '/';
217228
}
218229
}
219230
#if !defined(_WIN32)
220231
if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230241
blob_set(&content, zTargetFile);
231242
blob_write_to_file(&content, zLinkFile);
232243
blob_reset(&content);
233244
}
234245
}
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
+ else
265
+ blob_write_to_file(blob, zName);
266
+}
235267
236268
/*
237269
** Copy symbolic link from zFrom to zTo.
238270
*/
239271
void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509541
int file_delete(const char *zFilename){
510542
int rc;
511543
#ifdef _WIN32
512544
wchar_t *z = fossil_utf8_to_filename(zFilename);
513545
rc = _wunlink(z);
546
+#else
547
+ char *z = fossil_utf8_to_filename(zFilename);
548
+ rc = unlink(zFilename);
549
+#endif
550
+ fossil_filename_free(z);
551
+ return rc;
552
+}
553
+
554
+/*
555
+** Delete a link.
556
+**
557
+** Returns zero upon success.
558
+*/
559
+int link_delete(const char *zFilename){
560
+ int rc;
561
+#ifdef _WIN32
562
+ wchar_t *z = fossil_utf8_to_filename(zFilename);
563
+ rc = win32_unlink_rmdir(z);
514564
#else
515565
char *z = fossil_utf8_to_filename(zFilename);
516566
rc = unlink(zFilename);
517567
#endif
518568
fossil_filename_free(z);
519569
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
 
 
 
 
 
 
 
 
212 if( file_mkdir(zName, 1) ){
213 fossil_fatal_recursive("unable to create directory %s", zName);
214 return;
215 }
 
 
 
216 zName[i] = '/';
217 }
218 }
219 #if !defined(_WIN32)
220 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230 blob_set(&content, zTargetFile);
231 blob_write_to_file(&content, zLinkFile);
232 blob_reset(&content);
233 }
234 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
236 /*
237 ** Copy symbolic link from zFrom to zTo.
238 */
239 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509 int file_delete(const char *zFilename){
510 int rc;
511 #ifdef _WIN32
512 wchar_t *z = fossil_utf8_to_filename(zFilename);
513 rc = _wunlink(z);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514 #else
515 char *z = fossil_utf8_to_filename(zFilename);
516 rc = unlink(zFilename);
517 #endif
518 fossil_filename_free(z);
519
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
212 #if defined(_WIN32) || defined(__CYGWIN__)
213 /*
214 ** On Windows, local path looks like: C:/develop/project/file.txt
215 ** The if stops us from trying to create a directory of a drive letter
216 ** C: in this example.
217 */
218 if (!(i == 2 && zName[1] == ':')){
219 #endif
220 if( file_mkdir(zName, 1) ){
221 fossil_fatal_recursive("unable to create directory %s", zName);
222 return;
223 }
224 #if defined(_WIN32) || defined(__CYGWIN__)
225 }
226 #endif
227 zName[i] = '/';
228 }
229 }
230 #if !defined(_WIN32)
231 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
241 blob_set(&content, zTargetFile);
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 else
265 blob_write_to_file(blob, zName);
266 }
267
268 /*
269 ** Copy symbolic link from zFrom to zTo.
270 */
271 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
541 int file_delete(const char *zFilename){
542 int rc;
543 #ifdef _WIN32
544 wchar_t *z = fossil_utf8_to_filename(zFilename);
545 rc = _wunlink(z);
546 #else
547 char *z = fossil_utf8_to_filename(zFilename);
548 rc = unlink(zFilename);
549 #endif
550 fossil_filename_free(z);
551 return rc;
552 }
553
554 /*
555 ** Delete a link.
556 **
557 ** Returns zero upon success.
558 */
559 int link_delete(const char *zFilename){
560 int rc;
561 #ifdef _WIN32
562 wchar_t *z = fossil_utf8_to_filename(zFilename);
563 rc = win32_unlink_rmdir(z);
564 #else
565 char *z = fossil_utf8_to_filename(zFilename);
566 rc = unlink(zFilename);
567 #endif
568 fossil_filename_free(z);
569
+50
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207207
}
208208
nName = file_simplify_name(zName, nName, 0);
209209
for(i=1; i<nName; i++){
210210
if( zName[i]=='/' ){
211211
zName[i] = 0;
212
+#if defined(_WIN32) || defined(__CYGWIN__)
213
+ /*
214
+ ** On Windows, local path looks like: C:/develop/project/file.txt
215
+ ** The if stops us from trying to create a directory of a drive letter
216
+ ** C: in this example.
217
+ */
218
+ if (!(i == 2 && zName[1] == ':')){
219
+#endif
212220
if( file_mkdir(zName, 1) ){
213221
fossil_fatal_recursive("unable to create directory %s", zName);
214222
return;
215223
}
224
+#if defined(_WIN32) || defined(__CYGWIN__)
225
+ }
226
+#endif
216227
zName[i] = '/';
217228
}
218229
}
219230
#if !defined(_WIN32)
220231
if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230241
blob_set(&content, zTargetFile);
231242
blob_write_to_file(&content, zLinkFile);
232243
blob_reset(&content);
233244
}
234245
}
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
+ else
265
+ blob_write_to_file(blob, zName);
266
+}
235267
236268
/*
237269
** Copy symbolic link from zFrom to zTo.
238270
*/
239271
void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509541
int file_delete(const char *zFilename){
510542
int rc;
511543
#ifdef _WIN32
512544
wchar_t *z = fossil_utf8_to_filename(zFilename);
513545
rc = _wunlink(z);
546
+#else
547
+ char *z = fossil_utf8_to_filename(zFilename);
548
+ rc = unlink(zFilename);
549
+#endif
550
+ fossil_filename_free(z);
551
+ return rc;
552
+}
553
+
554
+/*
555
+** Delete a link.
556
+**
557
+** Returns zero upon success.
558
+*/
559
+int link_delete(const char *zFilename){
560
+ int rc;
561
+#ifdef _WIN32
562
+ wchar_t *z = fossil_utf8_to_filename(zFilename);
563
+ rc = win32_unlink_rmdir(z);
514564
#else
515565
char *z = fossil_utf8_to_filename(zFilename);
516566
rc = unlink(zFilename);
517567
#endif
518568
fossil_filename_free(z);
519569
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
 
 
 
 
 
 
 
 
212 if( file_mkdir(zName, 1) ){
213 fossil_fatal_recursive("unable to create directory %s", zName);
214 return;
215 }
 
 
 
216 zName[i] = '/';
217 }
218 }
219 #if !defined(_WIN32)
220 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230 blob_set(&content, zTargetFile);
231 blob_write_to_file(&content, zLinkFile);
232 blob_reset(&content);
233 }
234 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
236 /*
237 ** Copy symbolic link from zFrom to zTo.
238 */
239 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509 int file_delete(const char *zFilename){
510 int rc;
511 #ifdef _WIN32
512 wchar_t *z = fossil_utf8_to_filename(zFilename);
513 rc = _wunlink(z);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514 #else
515 char *z = fossil_utf8_to_filename(zFilename);
516 rc = unlink(zFilename);
517 #endif
518 fossil_filename_free(z);
519
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
212 #if defined(_WIN32) || defined(__CYGWIN__)
213 /*
214 ** On Windows, local path looks like: C:/develop/project/file.txt
215 ** The if stops us from trying to create a directory of a drive letter
216 ** C: in this example.
217 */
218 if (!(i == 2 && zName[1] == ':')){
219 #endif
220 if( file_mkdir(zName, 1) ){
221 fossil_fatal_recursive("unable to create directory %s", zName);
222 return;
223 }
224 #if defined(_WIN32) || defined(__CYGWIN__)
225 }
226 #endif
227 zName[i] = '/';
228 }
229 }
230 #if !defined(_WIN32)
231 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
241 blob_set(&content, zTargetFile);
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 else
265 blob_write_to_file(blob, zName);
266 }
267
268 /*
269 ** Copy symbolic link from zFrom to zTo.
270 */
271 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
541 int file_delete(const char *zFilename){
542 int rc;
543 #ifdef _WIN32
544 wchar_t *z = fossil_utf8_to_filename(zFilename);
545 rc = _wunlink(z);
546 #else
547 char *z = fossil_utf8_to_filename(zFilename);
548 rc = unlink(zFilename);
549 #endif
550 fossil_filename_free(z);
551 return rc;
552 }
553
554 /*
555 ** Delete a link.
556 **
557 ** Returns zero upon success.
558 */
559 int link_delete(const char *zFilename){
560 int rc;
561 #ifdef _WIN32
562 wchar_t *z = fossil_utf8_to_filename(zFilename);
563 rc = win32_unlink_rmdir(z);
564 #else
565 char *z = fossil_utf8_to_filename(zFilename);
566 rc = unlink(zFilename);
567 #endif
568 fossil_filename_free(z);
569
+50
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207207
}
208208
nName = file_simplify_name(zName, nName, 0);
209209
for(i=1; i<nName; i++){
210210
if( zName[i]=='/' ){
211211
zName[i] = 0;
212
+#if defined(_WIN32) || defined(__CYGWIN__)
213
+ /*
214
+ ** On Windows, local path looks like: C:/develop/project/file.txt
215
+ ** The if stops us from trying to create a directory of a drive letter
216
+ ** C: in this example.
217
+ */
218
+ if (!(i == 2 && zName[1] == ':')){
219
+#endif
212220
if( file_mkdir(zName, 1) ){
213221
fossil_fatal_recursive("unable to create directory %s", zName);
214222
return;
215223
}
224
+#if defined(_WIN32) || defined(__CYGWIN__)
225
+ }
226
+#endif
216227
zName[i] = '/';
217228
}
218229
}
219230
#if !defined(_WIN32)
220231
if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230241
blob_set(&content, zTargetFile);
231242
blob_write_to_file(&content, zLinkFile);
232243
blob_reset(&content);
233244
}
234245
}
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
+ else
265
+ blob_write_to_file(blob, zName);
266
+}
235267
236268
/*
237269
** Copy symbolic link from zFrom to zTo.
238270
*/
239271
void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509541
int file_delete(const char *zFilename){
510542
int rc;
511543
#ifdef _WIN32
512544
wchar_t *z = fossil_utf8_to_filename(zFilename);
513545
rc = _wunlink(z);
546
+#else
547
+ char *z = fossil_utf8_to_filename(zFilename);
548
+ rc = unlink(zFilename);
549
+#endif
550
+ fossil_filename_free(z);
551
+ return rc;
552
+}
553
+
554
+/*
555
+** Delete a link.
556
+**
557
+** Returns zero upon success.
558
+*/
559
+int link_delete(const char *zFilename){
560
+ int rc;
561
+#ifdef _WIN32
562
+ wchar_t *z = fossil_utf8_to_filename(zFilename);
563
+ rc = win32_unlink_rmdir(z);
514564
#else
515565
char *z = fossil_utf8_to_filename(zFilename);
516566
rc = unlink(zFilename);
517567
#endif
518568
fossil_filename_free(z);
519569
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
 
 
 
 
 
 
 
 
212 if( file_mkdir(zName, 1) ){
213 fossil_fatal_recursive("unable to create directory %s", zName);
214 return;
215 }
 
 
 
216 zName[i] = '/';
217 }
218 }
219 #if !defined(_WIN32)
220 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230 blob_set(&content, zTargetFile);
231 blob_write_to_file(&content, zLinkFile);
232 blob_reset(&content);
233 }
234 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
236 /*
237 ** Copy symbolic link from zFrom to zTo.
238 */
239 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509 int file_delete(const char *zFilename){
510 int rc;
511 #ifdef _WIN32
512 wchar_t *z = fossil_utf8_to_filename(zFilename);
513 rc = _wunlink(z);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514 #else
515 char *z = fossil_utf8_to_filename(zFilename);
516 rc = unlink(zFilename);
517 #endif
518 fossil_filename_free(z);
519
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
212 #if defined(_WIN32) || defined(__CYGWIN__)
213 /*
214 ** On Windows, local path looks like: C:/develop/project/file.txt
215 ** The if stops us from trying to create a directory of a drive letter
216 ** C: in this example.
217 */
218 if (!(i == 2 && zName[1] == ':')){
219 #endif
220 if( file_mkdir(zName, 1) ){
221 fossil_fatal_recursive("unable to create directory %s", zName);
222 return;
223 }
224 #if defined(_WIN32) || defined(__CYGWIN__)
225 }
226 #endif
227 zName[i] = '/';
228 }
229 }
230 #if !defined(_WIN32)
231 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
241 blob_set(&content, zTargetFile);
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 else
265 blob_write_to_file(blob, zName);
266 }
267
268 /*
269 ** Copy symbolic link from zFrom to zTo.
270 */
271 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
541 int file_delete(const char *zFilename){
542 int rc;
543 #ifdef _WIN32
544 wchar_t *z = fossil_utf8_to_filename(zFilename);
545 rc = _wunlink(z);
546 #else
547 char *z = fossil_utf8_to_filename(zFilename);
548 rc = unlink(zFilename);
549 #endif
550 fossil_filename_free(z);
551 return rc;
552 }
553
554 /*
555 ** Delete a link.
556 **
557 ** Returns zero upon success.
558 */
559 int link_delete(const char *zFilename){
560 int rc;
561 #ifdef _WIN32
562 wchar_t *z = fossil_utf8_to_filename(zFilename);
563 rc = win32_unlink_rmdir(z);
564 #else
565 char *z = fossil_utf8_to_filename(zFilename);
566 rc = unlink(zFilename);
567 #endif
568 fossil_filename_free(z);
569
+50
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207207
}
208208
nName = file_simplify_name(zName, nName, 0);
209209
for(i=1; i<nName; i++){
210210
if( zName[i]=='/' ){
211211
zName[i] = 0;
212
+#if defined(_WIN32) || defined(__CYGWIN__)
213
+ /*
214
+ ** On Windows, local path looks like: C:/develop/project/file.txt
215
+ ** The if stops us from trying to create a directory of a drive letter
216
+ ** C: in this example.
217
+ */
218
+ if (!(i == 2 && zName[1] == ':')){
219
+#endif
212220
if( file_mkdir(zName, 1) ){
213221
fossil_fatal_recursive("unable to create directory %s", zName);
214222
return;
215223
}
224
+#if defined(_WIN32) || defined(__CYGWIN__)
225
+ }
226
+#endif
216227
zName[i] = '/';
217228
}
218229
}
219230
#if !defined(_WIN32)
220231
if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230241
blob_set(&content, zTargetFile);
231242
blob_write_to_file(&content, zLinkFile);
232243
blob_reset(&content);
233244
}
234245
}
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
+ else
265
+ blob_write_to_file(blob, zName);
266
+}
235267
236268
/*
237269
** Copy symbolic link from zFrom to zTo.
238270
*/
239271
void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509541
int file_delete(const char *zFilename){
510542
int rc;
511543
#ifdef _WIN32
512544
wchar_t *z = fossil_utf8_to_filename(zFilename);
513545
rc = _wunlink(z);
546
+#else
547
+ char *z = fossil_utf8_to_filename(zFilename);
548
+ rc = unlink(zFilename);
549
+#endif
550
+ fossil_filename_free(z);
551
+ return rc;
552
+}
553
+
554
+/*
555
+** Delete a link.
556
+**
557
+** Returns zero upon success.
558
+*/
559
+int link_delete(const char *zFilename){
560
+ int rc;
561
+#ifdef _WIN32
562
+ wchar_t *z = fossil_utf8_to_filename(zFilename);
563
+ rc = win32_unlink_rmdir(z);
514564
#else
515565
char *z = fossil_utf8_to_filename(zFilename);
516566
rc = unlink(zFilename);
517567
#endif
518568
fossil_filename_free(z);
519569
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
 
 
 
 
 
 
 
 
212 if( file_mkdir(zName, 1) ){
213 fossil_fatal_recursive("unable to create directory %s", zName);
214 return;
215 }
 
 
 
216 zName[i] = '/';
217 }
218 }
219 #if !defined(_WIN32)
220 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230 blob_set(&content, zTargetFile);
231 blob_write_to_file(&content, zLinkFile);
232 blob_reset(&content);
233 }
234 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
236 /*
237 ** Copy symbolic link from zFrom to zTo.
238 */
239 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509 int file_delete(const char *zFilename){
510 int rc;
511 #ifdef _WIN32
512 wchar_t *z = fossil_utf8_to_filename(zFilename);
513 rc = _wunlink(z);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514 #else
515 char *z = fossil_utf8_to_filename(zFilename);
516 rc = unlink(zFilename);
517 #endif
518 fossil_filename_free(z);
519
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
212 #if defined(_WIN32) || defined(__CYGWIN__)
213 /*
214 ** On Windows, local path looks like: C:/develop/project/file.txt
215 ** The if stops us from trying to create a directory of a drive letter
216 ** C: in this example.
217 */
218 if (!(i == 2 && zName[1] == ':')){
219 #endif
220 if( file_mkdir(zName, 1) ){
221 fossil_fatal_recursive("unable to create directory %s", zName);
222 return;
223 }
224 #if defined(_WIN32) || defined(__CYGWIN__)
225 }
226 #endif
227 zName[i] = '/';
228 }
229 }
230 #if !defined(_WIN32)
231 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
241 blob_set(&content, zTargetFile);
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 else
265 blob_write_to_file(blob, zName);
266 }
267
268 /*
269 ** Copy symbolic link from zFrom to zTo.
270 */
271 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
541 int file_delete(const char *zFilename){
542 int rc;
543 #ifdef _WIN32
544 wchar_t *z = fossil_utf8_to_filename(zFilename);
545 rc = _wunlink(z);
546 #else
547 char *z = fossil_utf8_to_filename(zFilename);
548 rc = unlink(zFilename);
549 #endif
550 fossil_filename_free(z);
551 return rc;
552 }
553
554 /*
555 ** Delete a link.
556 **
557 ** Returns zero upon success.
558 */
559 int link_delete(const char *zFilename){
560 int rc;
561 #ifdef _WIN32
562 wchar_t *z = fossil_utf8_to_filename(zFilename);
563 rc = win32_unlink_rmdir(z);
564 #else
565 char *z = fossil_utf8_to_filename(zFilename);
566 rc = unlink(zFilename);
567 #endif
568 fossil_filename_free(z);
569
+50
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207207
}
208208
nName = file_simplify_name(zName, nName, 0);
209209
for(i=1; i<nName; i++){
210210
if( zName[i]=='/' ){
211211
zName[i] = 0;
212
+#if defined(_WIN32) || defined(__CYGWIN__)
213
+ /*
214
+ ** On Windows, local path looks like: C:/develop/project/file.txt
215
+ ** The if stops us from trying to create a directory of a drive letter
216
+ ** C: in this example.
217
+ */
218
+ if (!(i == 2 && zName[1] == ':')){
219
+#endif
212220
if( file_mkdir(zName, 1) ){
213221
fossil_fatal_recursive("unable to create directory %s", zName);
214222
return;
215223
}
224
+#if defined(_WIN32) || defined(__CYGWIN__)
225
+ }
226
+#endif
216227
zName[i] = '/';
217228
}
218229
}
219230
#if !defined(_WIN32)
220231
if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230241
blob_set(&content, zTargetFile);
231242
blob_write_to_file(&content, zLinkFile);
232243
blob_reset(&content);
233244
}
234245
}
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
+ else
265
+ blob_write_to_file(blob, zName);
266
+}
235267
236268
/*
237269
** Copy symbolic link from zFrom to zTo.
238270
*/
239271
void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509541
int file_delete(const char *zFilename){
510542
int rc;
511543
#ifdef _WIN32
512544
wchar_t *z = fossil_utf8_to_filename(zFilename);
513545
rc = _wunlink(z);
546
+#else
547
+ char *z = fossil_utf8_to_filename(zFilename);
548
+ rc = unlink(zFilename);
549
+#endif
550
+ fossil_filename_free(z);
551
+ return rc;
552
+}
553
+
554
+/*
555
+** Delete a link.
556
+**
557
+** Returns zero upon success.
558
+*/
559
+int link_delete(const char *zFilename){
560
+ int rc;
561
+#ifdef _WIN32
562
+ wchar_t *z = fossil_utf8_to_filename(zFilename);
563
+ rc = win32_unlink_rmdir(z);
514564
#else
515565
char *z = fossil_utf8_to_filename(zFilename);
516566
rc = unlink(zFilename);
517567
#endif
518568
fossil_filename_free(z);
519569
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
 
 
 
 
 
 
 
 
212 if( file_mkdir(zName, 1) ){
213 fossil_fatal_recursive("unable to create directory %s", zName);
214 return;
215 }
 
 
 
216 zName[i] = '/';
217 }
218 }
219 #if !defined(_WIN32)
220 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230 blob_set(&content, zTargetFile);
231 blob_write_to_file(&content, zLinkFile);
232 blob_reset(&content);
233 }
234 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
236 /*
237 ** Copy symbolic link from zFrom to zTo.
238 */
239 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509 int file_delete(const char *zFilename){
510 int rc;
511 #ifdef _WIN32
512 wchar_t *z = fossil_utf8_to_filename(zFilename);
513 rc = _wunlink(z);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514 #else
515 char *z = fossil_utf8_to_filename(zFilename);
516 rc = unlink(zFilename);
517 #endif
518 fossil_filename_free(z);
519
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
212 #if defined(_WIN32) || defined(__CYGWIN__)
213 /*
214 ** On Windows, local path looks like: C:/develop/project/file.txt
215 ** The if stops us from trying to create a directory of a drive letter
216 ** C: in this example.
217 */
218 if (!(i == 2 && zName[1] == ':')){
219 #endif
220 if( file_mkdir(zName, 1) ){
221 fossil_fatal_recursive("unable to create directory %s", zName);
222 return;
223 }
224 #if defined(_WIN32) || defined(__CYGWIN__)
225 }
226 #endif
227 zName[i] = '/';
228 }
229 }
230 #if !defined(_WIN32)
231 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
241 blob_set(&content, zTargetFile);
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 else
265 blob_write_to_file(blob, zName);
266 }
267
268 /*
269 ** Copy symbolic link from zFrom to zTo.
270 */
271 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
541 int file_delete(const char *zFilename){
542 int rc;
543 #ifdef _WIN32
544 wchar_t *z = fossil_utf8_to_filename(zFilename);
545 rc = _wunlink(z);
546 #else
547 char *z = fossil_utf8_to_filename(zFilename);
548 rc = unlink(zFilename);
549 #endif
550 fossil_filename_free(z);
551 return rc;
552 }
553
554 /*
555 ** Delete a link.
556 **
557 ** Returns zero upon success.
558 */
559 int link_delete(const char *zFilename){
560 int rc;
561 #ifdef _WIN32
562 wchar_t *z = fossil_utf8_to_filename(zFilename);
563 rc = win32_unlink_rmdir(z);
564 #else
565 char *z = fossil_utf8_to_filename(zFilename);
566 rc = unlink(zFilename);
567 #endif
568 fossil_filename_free(z);
569
+50
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207207
}
208208
nName = file_simplify_name(zName, nName, 0);
209209
for(i=1; i<nName; i++){
210210
if( zName[i]=='/' ){
211211
zName[i] = 0;
212
+#if defined(_WIN32) || defined(__CYGWIN__)
213
+ /*
214
+ ** On Windows, local path looks like: C:/develop/project/file.txt
215
+ ** The if stops us from trying to create a directory of a drive letter
216
+ ** C: in this example.
217
+ */
218
+ if (!(i == 2 && zName[1] == ':')){
219
+#endif
212220
if( file_mkdir(zName, 1) ){
213221
fossil_fatal_recursive("unable to create directory %s", zName);
214222
return;
215223
}
224
+#if defined(_WIN32) || defined(__CYGWIN__)
225
+ }
226
+#endif
216227
zName[i] = '/';
217228
}
218229
}
219230
#if !defined(_WIN32)
220231
if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230241
blob_set(&content, zTargetFile);
231242
blob_write_to_file(&content, zLinkFile);
232243
blob_reset(&content);
233244
}
234245
}
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
+ else
265
+ blob_write_to_file(blob, zName);
266
+}
235267
236268
/*
237269
** Copy symbolic link from zFrom to zTo.
238270
*/
239271
void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509541
int file_delete(const char *zFilename){
510542
int rc;
511543
#ifdef _WIN32
512544
wchar_t *z = fossil_utf8_to_filename(zFilename);
513545
rc = _wunlink(z);
546
+#else
547
+ char *z = fossil_utf8_to_filename(zFilename);
548
+ rc = unlink(zFilename);
549
+#endif
550
+ fossil_filename_free(z);
551
+ return rc;
552
+}
553
+
554
+/*
555
+** Delete a link.
556
+**
557
+** Returns zero upon success.
558
+*/
559
+int link_delete(const char *zFilename){
560
+ int rc;
561
+#ifdef _WIN32
562
+ wchar_t *z = fossil_utf8_to_filename(zFilename);
563
+ rc = win32_unlink_rmdir(z);
514564
#else
515565
char *z = fossil_utf8_to_filename(zFilename);
516566
rc = unlink(zFilename);
517567
#endif
518568
fossil_filename_free(z);
519569
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
 
 
 
 
 
 
 
 
212 if( file_mkdir(zName, 1) ){
213 fossil_fatal_recursive("unable to create directory %s", zName);
214 return;
215 }
 
 
 
216 zName[i] = '/';
217 }
218 }
219 #if !defined(_WIN32)
220 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
230 blob_set(&content, zTargetFile);
231 blob_write_to_file(&content, zLinkFile);
232 blob_reset(&content);
233 }
234 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
236 /*
237 ** Copy symbolic link from zFrom to zTo.
238 */
239 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
509 int file_delete(const char *zFilename){
510 int rc;
511 #ifdef _WIN32
512 wchar_t *z = fossil_utf8_to_filename(zFilename);
513 rc = _wunlink(z);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514 #else
515 char *z = fossil_utf8_to_filename(zFilename);
516 rc = unlink(zFilename);
517 #endif
518 fossil_filename_free(z);
519
--- src/file.c
+++ src/file.c
@@ -207,14 +207,25 @@
207 }
208 nName = file_simplify_name(zName, nName, 0);
209 for(i=1; i<nName; i++){
210 if( zName[i]=='/' ){
211 zName[i] = 0;
212 #if defined(_WIN32) || defined(__CYGWIN__)
213 /*
214 ** On Windows, local path looks like: C:/develop/project/file.txt
215 ** The if stops us from trying to create a directory of a drive letter
216 ** C: in this example.
217 */
218 if (!(i == 2 && zName[1] == ':')){
219 #endif
220 if( file_mkdir(zName, 1) ){
221 fossil_fatal_recursive("unable to create directory %s", zName);
222 return;
223 }
224 #if defined(_WIN32) || defined(__CYGWIN__)
225 }
226 #endif
227 zName[i] = '/';
228 }
229 }
230 #if !defined(_WIN32)
231 if( symlink(zTargetFile, zName)!=0 )
@@ -230,10 +241,31 @@
241 blob_set(&content, zTargetFile);
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 else
265 blob_write_to_file(blob, zName);
266 }
267
268 /*
269 ** Copy symbolic link from zFrom to zTo.
270 */
271 void symlink_copy(const char *zFrom, const char *zTo){
@@ -509,10 +541,28 @@
541 int file_delete(const char *zFilename){
542 int rc;
543 #ifdef _WIN32
544 wchar_t *z = fossil_utf8_to_filename(zFilename);
545 rc = _wunlink(z);
546 #else
547 char *z = fossil_utf8_to_filename(zFilename);
548 rc = unlink(zFilename);
549 #endif
550 fossil_filename_free(z);
551 return rc;
552 }
553
554 /*
555 ** Delete a link.
556 **
557 ** Returns zero upon success.
558 */
559 int link_delete(const char *zFilename){
560 int rc;
561 #ifdef _WIN32
562 wchar_t *z = fossil_utf8_to_filename(zFilename);
563 rc = win32_unlink_rmdir(z);
564 #else
565 char *z = fossil_utf8_to_filename(zFilename);
566 rc = unlink(zFilename);
567 #endif
568 fossil_filename_free(z);
569
+1 -8
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241241
blob_read_from_file(&disk, zOPath);
242242
}
243243
content_get(rid, &a);
244244
blob_delta_apply(&a, &delta, &b);
245245
if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246
- if( isLink || isNewLink ){
247
- file_delete(zNPath);
248
- }
249
- if( isLink ){
250
- symlink_create(blob_str(&b), zNPath);
251
- }else{
252
- blob_write_to_file(&b, zNPath);
253
- }
246
+ create_symlink_or_file(1, isLink, isNewLink, &b, zNPath);
254247
file_wd_setexe(zNPath, isExec);
255248
fossil_print("UPDATE %s\n", zNew);
256249
}else{
257250
int rc;
258251
if( isLink || isNewLink ){
259252
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241 blob_read_from_file(&disk, zOPath);
242 }
243 content_get(rid, &a);
244 blob_delta_apply(&a, &delta, &b);
245 if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246 if( isLink || isNewLink ){
247 file_delete(zNPath);
248 }
249 if( isLink ){
250 symlink_create(blob_str(&b), zNPath);
251 }else{
252 blob_write_to_file(&b, zNPath);
253 }
254 file_wd_setexe(zNPath, isExec);
255 fossil_print("UPDATE %s\n", zNew);
256 }else{
257 int rc;
258 if( isLink || isNewLink ){
259
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241 blob_read_from_file(&disk, zOPath);
242 }
243 content_get(rid, &a);
244 blob_delta_apply(&a, &delta, &b);
245 if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246 create_symlink_or_file(1, isLink, isNewLink, &b, zNPath);
 
 
 
 
 
 
 
247 file_wd_setexe(zNPath, isExec);
248 fossil_print("UPDATE %s\n", zNew);
249 }else{
250 int rc;
251 if( isLink || isNewLink ){
252
+1 -8
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241241
blob_read_from_file(&disk, zOPath);
242242
}
243243
content_get(rid, &a);
244244
blob_delta_apply(&a, &delta, &b);
245245
if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246
- if( isLink || isNewLink ){
247
- file_delete(zNPath);
248
- }
249
- if( isLink ){
250
- symlink_create(blob_str(&b), zNPath);
251
- }else{
252
- blob_write_to_file(&b, zNPath);
253
- }
246
+ create_symlink_or_file(1, isLink, isNewLink, &b, zNPath);
254247
file_wd_setexe(zNPath, isExec);
255248
fossil_print("UPDATE %s\n", zNew);
256249
}else{
257250
int rc;
258251
if( isLink || isNewLink ){
259252
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241 blob_read_from_file(&disk, zOPath);
242 }
243 content_get(rid, &a);
244 blob_delta_apply(&a, &delta, &b);
245 if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246 if( isLink || isNewLink ){
247 file_delete(zNPath);
248 }
249 if( isLink ){
250 symlink_create(blob_str(&b), zNPath);
251 }else{
252 blob_write_to_file(&b, zNPath);
253 }
254 file_wd_setexe(zNPath, isExec);
255 fossil_print("UPDATE %s\n", zNew);
256 }else{
257 int rc;
258 if( isLink || isNewLink ){
259
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241 blob_read_from_file(&disk, zOPath);
242 }
243 content_get(rid, &a);
244 blob_delta_apply(&a, &delta, &b);
245 if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246 create_symlink_or_file(1, isLink, isNewLink, &b, zNPath);
 
 
 
 
 
 
 
247 file_wd_setexe(zNPath, isExec);
248 fossil_print("UPDATE %s\n", zNew);
249 }else{
250 int rc;
251 if( isLink || isNewLink ){
252
+1 -8
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241241
blob_read_from_file(&disk, zOPath);
242242
}
243243
content_get(rid, &a);
244244
blob_delta_apply(&a, &delta, &b);
245245
if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246
- if( isLink || isNewLink ){
247
- file_delete(zNPath);
248
- }
249
- if( isLink ){
250
- symlink_create(blob_str(&b), zNPath);
251
- }else{
252
- blob_write_to_file(&b, zNPath);
253
- }
246
+ create_symlink_or_file(1, isLink, isNewLink, &b, zNPath);
254247
file_wd_setexe(zNPath, isExec);
255248
fossil_print("UPDATE %s\n", zNew);
256249
}else{
257250
int rc;
258251
if( isLink || isNewLink ){
259252
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241 blob_read_from_file(&disk, zOPath);
242 }
243 content_get(rid, &a);
244 blob_delta_apply(&a, &delta, &b);
245 if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246 if( isLink || isNewLink ){
247 file_delete(zNPath);
248 }
249 if( isLink ){
250 symlink_create(blob_str(&b), zNPath);
251 }else{
252 blob_write_to_file(&b, zNPath);
253 }
254 file_wd_setexe(zNPath, isExec);
255 fossil_print("UPDATE %s\n", zNew);
256 }else{
257 int rc;
258 if( isLink || isNewLink ){
259
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241 blob_read_from_file(&disk, zOPath);
242 }
243 content_get(rid, &a);
244 blob_delta_apply(&a, &delta, &b);
245 if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246 create_symlink_or_file(1, isLink, isNewLink, &b, zNPath);
 
 
 
 
 
 
 
247 file_wd_setexe(zNPath, isExec);
248 fossil_print("UPDATE %s\n", zNew);
249 }else{
250 int rc;
251 if( isLink || isNewLink ){
252
+1 -8
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241241
blob_read_from_file(&disk, zOPath);
242242
}
243243
content_get(rid, &a);
244244
blob_delta_apply(&a, &delta, &b);
245245
if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246
- if( isLink || isNewLink ){
247
- file_delete(zNPath);
248
- }
249
- if( isLink ){
250
- symlink_create(blob_str(&b), zNPath);
251
- }else{
252
- blob_write_to_file(&b, zNPath);
253
- }
246
+ create_symlink_or_file(1, isLink, isNewLink, &b, zNPath);
254247
file_wd_setexe(zNPath, isExec);
255248
fossil_print("UPDATE %s\n", zNew);
256249
}else{
257250
int rc;
258251
if( isLink || isNewLink ){
259252
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241 blob_read_from_file(&disk, zOPath);
242 }
243 content_get(rid, &a);
244 blob_delta_apply(&a, &delta, &b);
245 if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246 if( isLink || isNewLink ){
247 file_delete(zNPath);
248 }
249 if( isLink ){
250 symlink_create(blob_str(&b), zNPath);
251 }else{
252 blob_write_to_file(&b, zNPath);
253 }
254 file_wd_setexe(zNPath, isExec);
255 fossil_print("UPDATE %s\n", zNew);
256 }else{
257 int rc;
258 if( isLink || isNewLink ){
259
--- src/stash.c
+++ src/stash.c
@@ -241,18 +241,11 @@
241 blob_read_from_file(&disk, zOPath);
242 }
243 content_get(rid, &a);
244 blob_delta_apply(&a, &delta, &b);
245 if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
246 create_symlink_or_file(1, isLink, isNewLink, &b, zNPath);
 
 
 
 
 
 
 
247 file_wd_setexe(zNPath, isExec);
248 fossil_print("UPDATE %s\n", zNew);
249 }else{
250 int rc;
251 if( isLink || isNewLink ){
252
+1 -8
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
7070
if( new_exists ){
7171
fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
7272
}else{
7373
fossil_print("NEW %s\n", zPathname);
7474
}
75
- if( new_exists && (new_link || old_link) ){
76
- file_delete(zFullname);
77
- }
78
- if( old_link ){
79
- symlink_create(blob_str(&new), zFullname);
80
- }else{
81
- blob_write_to_file(&new, zFullname);
82
- }
75
+ create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname);
8376
file_wd_setexe(zFullname, old_exe);
8477
}else{
8578
fossil_print("DELETE %s\n", zPathname);
8679
file_delete(zFullname);
8780
}
8881
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
70 if( new_exists ){
71 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
72 }else{
73 fossil_print("NEW %s\n", zPathname);
74 }
75 if( new_exists && (new_link || old_link) ){
76 file_delete(zFullname);
77 }
78 if( old_link ){
79 symlink_create(blob_str(&new), zFullname);
80 }else{
81 blob_write_to_file(&new, zFullname);
82 }
83 file_wd_setexe(zFullname, old_exe);
84 }else{
85 fossil_print("DELETE %s\n", zPathname);
86 file_delete(zFullname);
87 }
88
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
70 if( new_exists ){
71 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
72 }else{
73 fossil_print("NEW %s\n", zPathname);
74 }
75 create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname);
 
 
 
 
 
 
 
76 file_wd_setexe(zFullname, old_exe);
77 }else{
78 fossil_print("DELETE %s\n", zPathname);
79 file_delete(zFullname);
80 }
81
+1 -8
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
7070
if( new_exists ){
7171
fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
7272
}else{
7373
fossil_print("NEW %s\n", zPathname);
7474
}
75
- if( new_exists && (new_link || old_link) ){
76
- file_delete(zFullname);
77
- }
78
- if( old_link ){
79
- symlink_create(blob_str(&new), zFullname);
80
- }else{
81
- blob_write_to_file(&new, zFullname);
82
- }
75
+ create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname);
8376
file_wd_setexe(zFullname, old_exe);
8477
}else{
8578
fossil_print("DELETE %s\n", zPathname);
8679
file_delete(zFullname);
8780
}
8881
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
70 if( new_exists ){
71 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
72 }else{
73 fossil_print("NEW %s\n", zPathname);
74 }
75 if( new_exists && (new_link || old_link) ){
76 file_delete(zFullname);
77 }
78 if( old_link ){
79 symlink_create(blob_str(&new), zFullname);
80 }else{
81 blob_write_to_file(&new, zFullname);
82 }
83 file_wd_setexe(zFullname, old_exe);
84 }else{
85 fossil_print("DELETE %s\n", zPathname);
86 file_delete(zFullname);
87 }
88
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
70 if( new_exists ){
71 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
72 }else{
73 fossil_print("NEW %s\n", zPathname);
74 }
75 create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname);
 
 
 
 
 
 
 
76 file_wd_setexe(zFullname, old_exe);
77 }else{
78 fossil_print("DELETE %s\n", zPathname);
79 file_delete(zFullname);
80 }
81
+1 -8
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
7070
if( new_exists ){
7171
fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
7272
}else{
7373
fossil_print("NEW %s\n", zPathname);
7474
}
75
- if( new_exists && (new_link || old_link) ){
76
- file_delete(zFullname);
77
- }
78
- if( old_link ){
79
- symlink_create(blob_str(&new), zFullname);
80
- }else{
81
- blob_write_to_file(&new, zFullname);
82
- }
75
+ create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname);
8376
file_wd_setexe(zFullname, old_exe);
8477
}else{
8578
fossil_print("DELETE %s\n", zPathname);
8679
file_delete(zFullname);
8780
}
8881
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
70 if( new_exists ){
71 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
72 }else{
73 fossil_print("NEW %s\n", zPathname);
74 }
75 if( new_exists && (new_link || old_link) ){
76 file_delete(zFullname);
77 }
78 if( old_link ){
79 symlink_create(blob_str(&new), zFullname);
80 }else{
81 blob_write_to_file(&new, zFullname);
82 }
83 file_wd_setexe(zFullname, old_exe);
84 }else{
85 fossil_print("DELETE %s\n", zPathname);
86 file_delete(zFullname);
87 }
88
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
70 if( new_exists ){
71 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
72 }else{
73 fossil_print("NEW %s\n", zPathname);
74 }
75 create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname);
 
 
 
 
 
 
 
76 file_wd_setexe(zFullname, old_exe);
77 }else{
78 fossil_print("DELETE %s\n", zPathname);
79 file_delete(zFullname);
80 }
81
+1 -8
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
7070
if( new_exists ){
7171
fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
7272
}else{
7373
fossil_print("NEW %s\n", zPathname);
7474
}
75
- if( new_exists && (new_link || old_link) ){
76
- file_delete(zFullname);
77
- }
78
- if( old_link ){
79
- symlink_create(blob_str(&new), zFullname);
80
- }else{
81
- blob_write_to_file(&new, zFullname);
82
- }
75
+ create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname);
8376
file_wd_setexe(zFullname, old_exe);
8477
}else{
8578
fossil_print("DELETE %s\n", zPathname);
8679
file_delete(zFullname);
8780
}
8881
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
70 if( new_exists ){
71 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
72 }else{
73 fossil_print("NEW %s\n", zPathname);
74 }
75 if( new_exists && (new_link || old_link) ){
76 file_delete(zFullname);
77 }
78 if( old_link ){
79 symlink_create(blob_str(&new), zFullname);
80 }else{
81 blob_write_to_file(&new, zFullname);
82 }
83 file_wd_setexe(zFullname, old_exe);
84 }else{
85 fossil_print("DELETE %s\n", zPathname);
86 file_delete(zFullname);
87 }
88
--- src/undo.c
+++ src/undo.c
@@ -70,18 +70,11 @@
70 if( new_exists ){
71 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
72 }else{
73 fossil_print("NEW %s\n", zPathname);
74 }
75 create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname);
 
 
 
 
 
 
 
76 file_wd_setexe(zFullname, old_exe);
77 }else{
78 fossil_print("DELETE %s\n", zPathname);
79 file_delete(zFullname);
80 }
81
+2 -9
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766766
db_multi_exec(
767767
"DELETE FROM vmerge;"
768768
"INSERT OR IGNORE INTO torevert "
769769
" SELECT pathname"
770770
" FROM vfile "
771
- " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
771
+ " WHERE chnged OR deleted OR rid=0 OR pathname!=origname OR islink;"
772772
);
773773
}
774774
db_multi_exec(
775775
"INSERT OR IGNORE INTO torevert"
776776
" SELECT origname"
@@ -808,18 +808,11 @@
808808
zFile, zFile
809809
);
810810
}else{
811811
sqlite3_int64 mtime;
812812
undo_save(zFile);
813
- if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
814
- file_delete(zFull);
815
- }
816
- if( isLink ){
817
- symlink_create(blob_str(&record), zFull);
818
- }else{
819
- blob_write_to_file(&record, zFull);
820
- }
813
+ create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull);
821814
file_wd_setexe(zFull, isExe);
822815
fossil_print("REVERTED: %s\n", zFile);
823816
mtime = file_wd_mtime(zFull);
824817
db_multi_exec(
825818
"UPDATE vfile"
826819
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766 db_multi_exec(
767 "DELETE FROM vmerge;"
768 "INSERT OR IGNORE INTO torevert "
769 " SELECT pathname"
770 " FROM vfile "
771 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
772 );
773 }
774 db_multi_exec(
775 "INSERT OR IGNORE INTO torevert"
776 " SELECT origname"
@@ -808,18 +808,11 @@
808 zFile, zFile
809 );
810 }else{
811 sqlite3_int64 mtime;
812 undo_save(zFile);
813 if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
814 file_delete(zFull);
815 }
816 if( isLink ){
817 symlink_create(blob_str(&record), zFull);
818 }else{
819 blob_write_to_file(&record, zFull);
820 }
821 file_wd_setexe(zFull, isExe);
822 fossil_print("REVERTED: %s\n", zFile);
823 mtime = file_wd_mtime(zFull);
824 db_multi_exec(
825 "UPDATE vfile"
826
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766 db_multi_exec(
767 "DELETE FROM vmerge;"
768 "INSERT OR IGNORE INTO torevert "
769 " SELECT pathname"
770 " FROM vfile "
771 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname OR islink;"
772 );
773 }
774 db_multi_exec(
775 "INSERT OR IGNORE INTO torevert"
776 " SELECT origname"
@@ -808,18 +808,11 @@
808 zFile, zFile
809 );
810 }else{
811 sqlite3_int64 mtime;
812 undo_save(zFile);
813 create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull);
 
 
 
 
 
 
 
814 file_wd_setexe(zFull, isExe);
815 fossil_print("REVERTED: %s\n", zFile);
816 mtime = file_wd_mtime(zFull);
817 db_multi_exec(
818 "UPDATE vfile"
819
+2 -9
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766766
db_multi_exec(
767767
"DELETE FROM vmerge;"
768768
"INSERT OR IGNORE INTO torevert "
769769
" SELECT pathname"
770770
" FROM vfile "
771
- " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
771
+ " WHERE chnged OR deleted OR rid=0 OR pathname!=origname OR islink;"
772772
);
773773
}
774774
db_multi_exec(
775775
"INSERT OR IGNORE INTO torevert"
776776
" SELECT origname"
@@ -808,18 +808,11 @@
808808
zFile, zFile
809809
);
810810
}else{
811811
sqlite3_int64 mtime;
812812
undo_save(zFile);
813
- if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
814
- file_delete(zFull);
815
- }
816
- if( isLink ){
817
- symlink_create(blob_str(&record), zFull);
818
- }else{
819
- blob_write_to_file(&record, zFull);
820
- }
813
+ create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull);
821814
file_wd_setexe(zFull, isExe);
822815
fossil_print("REVERTED: %s\n", zFile);
823816
mtime = file_wd_mtime(zFull);
824817
db_multi_exec(
825818
"UPDATE vfile"
826819
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766 db_multi_exec(
767 "DELETE FROM vmerge;"
768 "INSERT OR IGNORE INTO torevert "
769 " SELECT pathname"
770 " FROM vfile "
771 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
772 );
773 }
774 db_multi_exec(
775 "INSERT OR IGNORE INTO torevert"
776 " SELECT origname"
@@ -808,18 +808,11 @@
808 zFile, zFile
809 );
810 }else{
811 sqlite3_int64 mtime;
812 undo_save(zFile);
813 if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
814 file_delete(zFull);
815 }
816 if( isLink ){
817 symlink_create(blob_str(&record), zFull);
818 }else{
819 blob_write_to_file(&record, zFull);
820 }
821 file_wd_setexe(zFull, isExe);
822 fossil_print("REVERTED: %s\n", zFile);
823 mtime = file_wd_mtime(zFull);
824 db_multi_exec(
825 "UPDATE vfile"
826
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766 db_multi_exec(
767 "DELETE FROM vmerge;"
768 "INSERT OR IGNORE INTO torevert "
769 " SELECT pathname"
770 " FROM vfile "
771 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname OR islink;"
772 );
773 }
774 db_multi_exec(
775 "INSERT OR IGNORE INTO torevert"
776 " SELECT origname"
@@ -808,18 +808,11 @@
808 zFile, zFile
809 );
810 }else{
811 sqlite3_int64 mtime;
812 undo_save(zFile);
813 create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull);
 
 
 
 
 
 
 
814 file_wd_setexe(zFull, isExe);
815 fossil_print("REVERTED: %s\n", zFile);
816 mtime = file_wd_mtime(zFull);
817 db_multi_exec(
818 "UPDATE vfile"
819
+2 -9
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766766
db_multi_exec(
767767
"DELETE FROM vmerge;"
768768
"INSERT OR IGNORE INTO torevert "
769769
" SELECT pathname"
770770
" FROM vfile "
771
- " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
771
+ " WHERE chnged OR deleted OR rid=0 OR pathname!=origname OR islink;"
772772
);
773773
}
774774
db_multi_exec(
775775
"INSERT OR IGNORE INTO torevert"
776776
" SELECT origname"
@@ -808,18 +808,11 @@
808808
zFile, zFile
809809
);
810810
}else{
811811
sqlite3_int64 mtime;
812812
undo_save(zFile);
813
- if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
814
- file_delete(zFull);
815
- }
816
- if( isLink ){
817
- symlink_create(blob_str(&record), zFull);
818
- }else{
819
- blob_write_to_file(&record, zFull);
820
- }
813
+ create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull);
821814
file_wd_setexe(zFull, isExe);
822815
fossil_print("REVERTED: %s\n", zFile);
823816
mtime = file_wd_mtime(zFull);
824817
db_multi_exec(
825818
"UPDATE vfile"
826819
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766 db_multi_exec(
767 "DELETE FROM vmerge;"
768 "INSERT OR IGNORE INTO torevert "
769 " SELECT pathname"
770 " FROM vfile "
771 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
772 );
773 }
774 db_multi_exec(
775 "INSERT OR IGNORE INTO torevert"
776 " SELECT origname"
@@ -808,18 +808,11 @@
808 zFile, zFile
809 );
810 }else{
811 sqlite3_int64 mtime;
812 undo_save(zFile);
813 if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
814 file_delete(zFull);
815 }
816 if( isLink ){
817 symlink_create(blob_str(&record), zFull);
818 }else{
819 blob_write_to_file(&record, zFull);
820 }
821 file_wd_setexe(zFull, isExe);
822 fossil_print("REVERTED: %s\n", zFile);
823 mtime = file_wd_mtime(zFull);
824 db_multi_exec(
825 "UPDATE vfile"
826
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766 db_multi_exec(
767 "DELETE FROM vmerge;"
768 "INSERT OR IGNORE INTO torevert "
769 " SELECT pathname"
770 " FROM vfile "
771 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname OR islink;"
772 );
773 }
774 db_multi_exec(
775 "INSERT OR IGNORE INTO torevert"
776 " SELECT origname"
@@ -808,18 +808,11 @@
808 zFile, zFile
809 );
810 }else{
811 sqlite3_int64 mtime;
812 undo_save(zFile);
813 create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull);
 
 
 
 
 
 
 
814 file_wd_setexe(zFull, isExe);
815 fossil_print("REVERTED: %s\n", zFile);
816 mtime = file_wd_mtime(zFull);
817 db_multi_exec(
818 "UPDATE vfile"
819
+2 -9
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766766
db_multi_exec(
767767
"DELETE FROM vmerge;"
768768
"INSERT OR IGNORE INTO torevert "
769769
" SELECT pathname"
770770
" FROM vfile "
771
- " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
771
+ " WHERE chnged OR deleted OR rid=0 OR pathname!=origname OR islink;"
772772
);
773773
}
774774
db_multi_exec(
775775
"INSERT OR IGNORE INTO torevert"
776776
" SELECT origname"
@@ -808,18 +808,11 @@
808808
zFile, zFile
809809
);
810810
}else{
811811
sqlite3_int64 mtime;
812812
undo_save(zFile);
813
- if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
814
- file_delete(zFull);
815
- }
816
- if( isLink ){
817
- symlink_create(blob_str(&record), zFull);
818
- }else{
819
- blob_write_to_file(&record, zFull);
820
- }
813
+ create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull);
821814
file_wd_setexe(zFull, isExe);
822815
fossil_print("REVERTED: %s\n", zFile);
823816
mtime = file_wd_mtime(zFull);
824817
db_multi_exec(
825818
"UPDATE vfile"
826819
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766 db_multi_exec(
767 "DELETE FROM vmerge;"
768 "INSERT OR IGNORE INTO torevert "
769 " SELECT pathname"
770 " FROM vfile "
771 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
772 );
773 }
774 db_multi_exec(
775 "INSERT OR IGNORE INTO torevert"
776 " SELECT origname"
@@ -808,18 +808,11 @@
808 zFile, zFile
809 );
810 }else{
811 sqlite3_int64 mtime;
812 undo_save(zFile);
813 if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
814 file_delete(zFull);
815 }
816 if( isLink ){
817 symlink_create(blob_str(&record), zFull);
818 }else{
819 blob_write_to_file(&record, zFull);
820 }
821 file_wd_setexe(zFull, isExe);
822 fossil_print("REVERTED: %s\n", zFile);
823 mtime = file_wd_mtime(zFull);
824 db_multi_exec(
825 "UPDATE vfile"
826
--- src/update.c
+++ src/update.c
@@ -766,11 +766,11 @@
766 db_multi_exec(
767 "DELETE FROM vmerge;"
768 "INSERT OR IGNORE INTO torevert "
769 " SELECT pathname"
770 " FROM vfile "
771 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname OR islink;"
772 );
773 }
774 db_multi_exec(
775 "INSERT OR IGNORE INTO torevert"
776 " SELECT origname"
@@ -808,18 +808,11 @@
808 zFile, zFile
809 );
810 }else{
811 sqlite3_int64 mtime;
812 undo_save(zFile);
813 create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull);
 
 
 
 
 
 
 
814 file_wd_setexe(zFull, isExe);
815 fossil_print("REVERTED: %s\n", zFile);
816 mtime = file_wd_mtime(zFull);
817 db_multi_exec(
818 "UPDATE vfile"
819
+1 -8
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320320
if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321321
if( file_wd_isdir(zName) == 1 ){
322322
/*TODO(dchest): remove directories? */
323323
fossil_fatal("%s is directory, cannot overwrite\n", zName);
324324
}
325
- if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
326
- file_delete(zName);
327
- }
328
- if( isLink ){
329
- symlink_create(blob_str(&content), zName);
330
- }else{
331
- blob_write_to_file(&content, zName);
332
- }
325
+ create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName);
333326
file_wd_setexe(zName, isExe);
334327
blob_reset(&content);
335328
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
336329
file_wd_mtime(zName), id);
337330
}
338331
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321 if( file_wd_isdir(zName) == 1 ){
322 /*TODO(dchest): remove directories? */
323 fossil_fatal("%s is directory, cannot overwrite\n", zName);
324 }
325 if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
326 file_delete(zName);
327 }
328 if( isLink ){
329 symlink_create(blob_str(&content), zName);
330 }else{
331 blob_write_to_file(&content, zName);
332 }
333 file_wd_setexe(zName, isExe);
334 blob_reset(&content);
335 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
336 file_wd_mtime(zName), id);
337 }
338
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321 if( file_wd_isdir(zName) == 1 ){
322 /*TODO(dchest): remove directories? */
323 fossil_fatal("%s is directory, cannot overwrite\n", zName);
324 }
325 create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName);
 
 
 
 
 
 
 
326 file_wd_setexe(zName, isExe);
327 blob_reset(&content);
328 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
329 file_wd_mtime(zName), id);
330 }
331
+1 -8
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320320
if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321321
if( file_wd_isdir(zName) == 1 ){
322322
/*TODO(dchest): remove directories? */
323323
fossil_fatal("%s is directory, cannot overwrite\n", zName);
324324
}
325
- if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
326
- file_delete(zName);
327
- }
328
- if( isLink ){
329
- symlink_create(blob_str(&content), zName);
330
- }else{
331
- blob_write_to_file(&content, zName);
332
- }
325
+ create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName);
333326
file_wd_setexe(zName, isExe);
334327
blob_reset(&content);
335328
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
336329
file_wd_mtime(zName), id);
337330
}
338331
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321 if( file_wd_isdir(zName) == 1 ){
322 /*TODO(dchest): remove directories? */
323 fossil_fatal("%s is directory, cannot overwrite\n", zName);
324 }
325 if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
326 file_delete(zName);
327 }
328 if( isLink ){
329 symlink_create(blob_str(&content), zName);
330 }else{
331 blob_write_to_file(&content, zName);
332 }
333 file_wd_setexe(zName, isExe);
334 blob_reset(&content);
335 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
336 file_wd_mtime(zName), id);
337 }
338
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321 if( file_wd_isdir(zName) == 1 ){
322 /*TODO(dchest): remove directories? */
323 fossil_fatal("%s is directory, cannot overwrite\n", zName);
324 }
325 create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName);
 
 
 
 
 
 
 
326 file_wd_setexe(zName, isExe);
327 blob_reset(&content);
328 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
329 file_wd_mtime(zName), id);
330 }
331
+1 -8
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320320
if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321321
if( file_wd_isdir(zName) == 1 ){
322322
/*TODO(dchest): remove directories? */
323323
fossil_fatal("%s is directory, cannot overwrite\n", zName);
324324
}
325
- if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
326
- file_delete(zName);
327
- }
328
- if( isLink ){
329
- symlink_create(blob_str(&content), zName);
330
- }else{
331
- blob_write_to_file(&content, zName);
332
- }
325
+ create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName);
333326
file_wd_setexe(zName, isExe);
334327
blob_reset(&content);
335328
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
336329
file_wd_mtime(zName), id);
337330
}
338331
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321 if( file_wd_isdir(zName) == 1 ){
322 /*TODO(dchest): remove directories? */
323 fossil_fatal("%s is directory, cannot overwrite\n", zName);
324 }
325 if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
326 file_delete(zName);
327 }
328 if( isLink ){
329 symlink_create(blob_str(&content), zName);
330 }else{
331 blob_write_to_file(&content, zName);
332 }
333 file_wd_setexe(zName, isExe);
334 blob_reset(&content);
335 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
336 file_wd_mtime(zName), id);
337 }
338
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321 if( file_wd_isdir(zName) == 1 ){
322 /*TODO(dchest): remove directories? */
323 fossil_fatal("%s is directory, cannot overwrite\n", zName);
324 }
325 create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName);
 
 
 
 
 
 
 
326 file_wd_setexe(zName, isExe);
327 blob_reset(&content);
328 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
329 file_wd_mtime(zName), id);
330 }
331
+1 -8
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320320
if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321321
if( file_wd_isdir(zName) == 1 ){
322322
/*TODO(dchest): remove directories? */
323323
fossil_fatal("%s is directory, cannot overwrite\n", zName);
324324
}
325
- if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
326
- file_delete(zName);
327
- }
328
- if( isLink ){
329
- symlink_create(blob_str(&content), zName);
330
- }else{
331
- blob_write_to_file(&content, zName);
332
- }
325
+ create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName);
333326
file_wd_setexe(zName, isExe);
334327
blob_reset(&content);
335328
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
336329
file_wd_mtime(zName), id);
337330
}
338331
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321 if( file_wd_isdir(zName) == 1 ){
322 /*TODO(dchest): remove directories? */
323 fossil_fatal("%s is directory, cannot overwrite\n", zName);
324 }
325 if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
326 file_delete(zName);
327 }
328 if( isLink ){
329 symlink_create(blob_str(&content), zName);
330 }else{
331 blob_write_to_file(&content, zName);
332 }
333 file_wd_setexe(zName, isExe);
334 blob_reset(&content);
335 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
336 file_wd_mtime(zName), id);
337 }
338
--- src/vfile.c
+++ src/vfile.c
@@ -320,18 +320,11 @@
320 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
321 if( file_wd_isdir(zName) == 1 ){
322 /*TODO(dchest): remove directories? */
323 fossil_fatal("%s is directory, cannot overwrite\n", zName);
324 }
325 create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName);
 
 
 
 
 
 
 
326 file_wd_setexe(zName, isExe);
327 blob_reset(&content);
328 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
329 file_wd_mtime(zName), id);
330 }
331
+113 -35
--- 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,49 +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
+*/
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
+*/
181247
int win32_symlink(const char *oldpath, const char *newpath){
182248
fossilStat stat;
183249
int created = 0;
184250
DWORD flags = 0;
185251
wchar_t *zMbcs;
@@ -187,10 +253,12 @@
187253
/* does oldpath exist? is it a dir or a file? */
188254
zMbcs = fossil_utf8_to_filename(oldpath);
189255
if (win32_stat(zMbcs, &stat) == 0){
190256
if (stat.st_mode == S_IFDIR)
191257
flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
192
- DeleteFile(newpath);
193
- if (CreateSymbolicLink(newpath, oldpath, flags))
194
- created = 1;
195258
}
196259
fossil_filename_free(zMbcs);
260
+
261
+ /* remove newpath before creating the symlink */
262
+ zMbcs = fossil_utf8_to_filename(newpath);
263
+ win32_unlink_rmdir(zMbcs);
264
+ fossil_filename_free(zMbcs);
@@ -197,5 +265,7 @@
265
+
266
+ created = CreateSymbolicLink(newpath, oldpath, flags);
197267
198268
/* if the symlink was not created, create a plain text file */
199269
if (!created){
200270
Blob content;
201271
blob_set(&content, oldpath);
@@ -202,13 +272,21 @@
202272
blob_write_to_file(&content, newpath);
203273
blob_reset(&content);
204274
created = 1;
205275
}
206276
207
- return created ? 0 : -1;
277
+ return !created;
208278
}
209279
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
+*/
210288
int win32_symlinks_supported(){
211289
TOKEN_PRIVILEGES tp;
212290
LUID luid;
213291
HANDLE process, token;
214292
DWORD status;
215293
--- 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,49 +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_symlink(const char *oldpath, const char *newpath){
182 fossilStat stat;
183 int created = 0;
184 DWORD flags = 0;
185 wchar_t *zMbcs;
@@ -187,10 +253,12 @@
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,5 +265,7 @@
 
 
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,13 +272,21 @@
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
--- 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,49 +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;
@@ -187,10 +253,12 @@
253 /* does oldpath exist? is it a dir or a file? */
254 zMbcs = fossil_utf8_to_filename(oldpath);
255 if (win32_stat(zMbcs, &stat) == 0){
256 if (stat.st_mode == S_IFDIR)
257 flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
 
 
 
258 }
259 fossil_filename_free(zMbcs);
260
261 /* remove newpath before creating the symlink */
262 zMbcs = fossil_utf8_to_filename(newpath);
263 win32_unlink_rmdir(zMbcs);
264 fossil_filename_free(zMbcs);
@@ -197,5 +265,7 @@
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);
@@ -202,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
+113 -35
--- 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,49 +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
+*/
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
+*/
181247
int win32_symlink(const char *oldpath, const char *newpath){
182248
fossilStat stat;
183249
int created = 0;
184250
DWORD flags = 0;
185251
wchar_t *zMbcs;
@@ -187,10 +253,12 @@
187253
/* does oldpath exist? is it a dir or a file? */
188254
zMbcs = fossil_utf8_to_filename(oldpath);
189255
if (win32_stat(zMbcs, &stat) == 0){
190256
if (stat.st_mode == S_IFDIR)
191257
flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
192
- DeleteFile(newpath);
193
- if (CreateSymbolicLink(newpath, oldpath, flags))
194
- created = 1;
195258
}
196259
fossil_filename_free(zMbcs);
260
+
261
+ /* remove newpath before creating the symlink */
262
+ zMbcs = fossil_utf8_to_filename(newpath);
263
+ win32_unlink_rmdir(zMbcs);
264
+ fossil_filename_free(zMbcs);
@@ -197,5 +265,7 @@
265
+
266
+ created = CreateSymbolicLink(newpath, oldpath, flags);
197267
198268
/* if the symlink was not created, create a plain text file */
199269
if (!created){
200270
Blob content;
201271
blob_set(&content, oldpath);
@@ -202,13 +272,21 @@
202272
blob_write_to_file(&content, newpath);
203273
blob_reset(&content);
204274
created = 1;
205275
}
206276
207
- return created ? 0 : -1;
277
+ return !created;
208278
}
209279
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
+*/
210288
int win32_symlinks_supported(){
211289
TOKEN_PRIVILEGES tp;
212290
LUID luid;
213291
HANDLE process, token;
214292
DWORD status;
215293
--- 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,49 +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_symlink(const char *oldpath, const char *newpath){
182 fossilStat stat;
183 int created = 0;
184 DWORD flags = 0;
185 wchar_t *zMbcs;
@@ -187,10 +253,12 @@
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,5 +265,7 @@
 
 
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,13 +272,21 @@
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
--- 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,49 +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;
@@ -187,10 +253,12 @@
253 /* does oldpath exist? is it a dir or a file? */
254 zMbcs = fossil_utf8_to_filename(oldpath);
255 if (win32_stat(zMbcs, &stat) == 0){
256 if (stat.st_mode == S_IFDIR)
257 flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
 
 
 
258 }
259 fossil_filename_free(zMbcs);
260
261 /* remove newpath before creating the symlink */
262 zMbcs = fossil_utf8_to_filename(newpath);
263 win32_unlink_rmdir(zMbcs);
264 fossil_filename_free(zMbcs);
@@ -197,5 +265,7 @@
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);
@@ -202,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
+113 -35
--- 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,49 +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
+*/
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
+*/
181247
int win32_symlink(const char *oldpath, const char *newpath){
182248
fossilStat stat;
183249
int created = 0;
184250
DWORD flags = 0;
185251
wchar_t *zMbcs;
@@ -187,10 +253,12 @@
187253
/* does oldpath exist? is it a dir or a file? */
188254
zMbcs = fossil_utf8_to_filename(oldpath);
189255
if (win32_stat(zMbcs, &stat) == 0){
190256
if (stat.st_mode == S_IFDIR)
191257
flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
192
- DeleteFile(newpath);
193
- if (CreateSymbolicLink(newpath, oldpath, flags))
194
- created = 1;
195258
}
196259
fossil_filename_free(zMbcs);
260
+
261
+ /* remove newpath before creating the symlink */
262
+ zMbcs = fossil_utf8_to_filename(newpath);
263
+ win32_unlink_rmdir(zMbcs);
264
+ fossil_filename_free(zMbcs);
@@ -197,5 +265,7 @@
265
+
266
+ created = CreateSymbolicLink(newpath, oldpath, flags);
197267
198268
/* if the symlink was not created, create a plain text file */
199269
if (!created){
200270
Blob content;
201271
blob_set(&content, oldpath);
@@ -202,13 +272,21 @@
202272
blob_write_to_file(&content, newpath);
203273
blob_reset(&content);
204274
created = 1;
205275
}
206276
207
- return created ? 0 : -1;
277
+ return !created;
208278
}
209279
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
+*/
210288
int win32_symlinks_supported(){
211289
TOKEN_PRIVILEGES tp;
212290
LUID luid;
213291
HANDLE process, token;
214292
DWORD status;
215293
--- 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,49 +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_symlink(const char *oldpath, const char *newpath){
182 fossilStat stat;
183 int created = 0;
184 DWORD flags = 0;
185 wchar_t *zMbcs;
@@ -187,10 +253,12 @@
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,5 +265,7 @@
 
 
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,13 +272,21 @@
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
--- 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,49 +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;
@@ -187,10 +253,12 @@
253 /* does oldpath exist? is it a dir or a file? */
254 zMbcs = fossil_utf8_to_filename(oldpath);
255 if (win32_stat(zMbcs, &stat) == 0){
256 if (stat.st_mode == S_IFDIR)
257 flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
 
 
 
258 }
259 fossil_filename_free(zMbcs);
260
261 /* remove newpath before creating the symlink */
262 zMbcs = fossil_utf8_to_filename(newpath);
263 win32_unlink_rmdir(zMbcs);
264 fossil_filename_free(zMbcs);
@@ -197,5 +265,7 @@
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);
@@ -202,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
+113 -35
--- 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,49 +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
+*/
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
+*/
181247
int win32_symlink(const char *oldpath, const char *newpath){
182248
fossilStat stat;
183249
int created = 0;
184250
DWORD flags = 0;
185251
wchar_t *zMbcs;
@@ -187,10 +253,12 @@
187253
/* does oldpath exist? is it a dir or a file? */
188254
zMbcs = fossil_utf8_to_filename(oldpath);
189255
if (win32_stat(zMbcs, &stat) == 0){
190256
if (stat.st_mode == S_IFDIR)
191257
flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
192
- DeleteFile(newpath);
193
- if (CreateSymbolicLink(newpath, oldpath, flags))
194
- created = 1;
195258
}
196259
fossil_filename_free(zMbcs);
260
+
261
+ /* remove newpath before creating the symlink */
262
+ zMbcs = fossil_utf8_to_filename(newpath);
263
+ win32_unlink_rmdir(zMbcs);
264
+ fossil_filename_free(zMbcs);
@@ -197,5 +265,7 @@
265
+
266
+ created = CreateSymbolicLink(newpath, oldpath, flags);
197267
198268
/* if the symlink was not created, create a plain text file */
199269
if (!created){
200270
Blob content;
201271
blob_set(&content, oldpath);
@@ -202,13 +272,21 @@
202272
blob_write_to_file(&content, newpath);
203273
blob_reset(&content);
204274
created = 1;
205275
}
206276
207
- return created ? 0 : -1;
277
+ return !created;
208278
}
209279
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
+*/
210288
int win32_symlinks_supported(){
211289
TOKEN_PRIVILEGES tp;
212290
LUID luid;
213291
HANDLE process, token;
214292
DWORD status;
215293
--- 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,49 +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_symlink(const char *oldpath, const char *newpath){
182 fossilStat stat;
183 int created = 0;
184 DWORD flags = 0;
185 wchar_t *zMbcs;
@@ -187,10 +253,12 @@
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,5 +265,7 @@
 
 
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,13 +272,21 @@
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
--- 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,49 +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;
@@ -187,10 +253,12 @@
253 /* does oldpath exist? is it a dir or a file? */
254 zMbcs = fossil_utf8_to_filename(oldpath);
255 if (win32_stat(zMbcs, &stat) == 0){
256 if (stat.st_mode == S_IFDIR)
257 flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
 
 
 
258 }
259 fossil_filename_free(zMbcs);
260
261 /* remove newpath before creating the symlink */
262 zMbcs = fossil_utf8_to_filename(newpath);
263 win32_unlink_rmdir(zMbcs);
264 fossil_filename_free(zMbcs);
@@ -197,5 +265,7 @@
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);
@@ -202,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
+113 -35
--- 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,49 +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
+*/
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
+*/
181247
int win32_symlink(const char *oldpath, const char *newpath){
182248
fossilStat stat;
183249
int created = 0;
184250
DWORD flags = 0;
185251
wchar_t *zMbcs;
@@ -187,10 +253,12 @@
187253
/* does oldpath exist? is it a dir or a file? */
188254
zMbcs = fossil_utf8_to_filename(oldpath);
189255
if (win32_stat(zMbcs, &stat) == 0){
190256
if (stat.st_mode == S_IFDIR)
191257
flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
192
- DeleteFile(newpath);
193
- if (CreateSymbolicLink(newpath, oldpath, flags))
194
- created = 1;
195258
}
196259
fossil_filename_free(zMbcs);
260
+
261
+ /* remove newpath before creating the symlink */
262
+ zMbcs = fossil_utf8_to_filename(newpath);
263
+ win32_unlink_rmdir(zMbcs);
264
+ fossil_filename_free(zMbcs);
@@ -197,5 +265,7 @@
265
+
266
+ created = CreateSymbolicLink(newpath, oldpath, flags);
197267
198268
/* if the symlink was not created, create a plain text file */
199269
if (!created){
200270
Blob content;
201271
blob_set(&content, oldpath);
@@ -202,13 +272,21 @@
202272
blob_write_to_file(&content, newpath);
203273
blob_reset(&content);
204274
created = 1;
205275
}
206276
207
- return created ? 0 : -1;
277
+ return !created;
208278
}
209279
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
+*/
210288
int win32_symlinks_supported(){
211289
TOKEN_PRIVILEGES tp;
212290
LUID luid;
213291
HANDLE process, token;
214292
DWORD status;
215293
--- 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,49 +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_symlink(const char *oldpath, const char *newpath){
182 fossilStat stat;
183 int created = 0;
184 DWORD flags = 0;
185 wchar_t *zMbcs;
@@ -187,10 +253,12 @@
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,5 +265,7 @@
 
 
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,13 +272,21 @@
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
--- 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,49 +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;
@@ -187,10 +253,12 @@
253 /* does oldpath exist? is it a dir or a file? */
254 zMbcs = fossil_utf8_to_filename(oldpath);
255 if (win32_stat(zMbcs, &stat) == 0){
256 if (stat.st_mode == S_IFDIR)
257 flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
 
 
 
258 }
259 fossil_filename_free(zMbcs);
260
261 /* remove newpath before creating the symlink */
262 zMbcs = fossil_utf8_to_filename(newpath);
263 win32_unlink_rmdir(zMbcs);
264 fossil_filename_free(zMbcs);
@@ -197,5 +265,7 @@
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);
@@ -202,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