Fossil SCM
Cherrypicked a number of commits from a "bad branch" (I somehow fouled up a merge from trunk).
Commit
2d75e87b760c0a991fc3e4e8151d001bfef998e6
Parent
de8035cca6ebd69…
35 files changed
+6
-1
+6
-1
+6
-1
+6
-1
+6
-1
+6
-1
+6
-1
+50
+50
+50
+50
+50
+50
+50
+1
-8
+1
-8
+1
-8
+1
-8
+1
-8
+1
-8
+1
-8
+1
-8
+2
-9
+2
-9
+2
-9
+2
-9
+1
-8
+1
-8
+1
-8
+1
-8
+113
-35
+113
-35
+113
-35
+113
-35
+113
-35
~
src/checkin.c
~
src/checkin.c
~
src/checkin.c
~
src/checkin.c
~
src/checkin.c
~
src/checkin.c
~
src/checkin.c
~
src/file.c
~
src/file.c
~
src/file.c
~
src/file.c
~
src/file.c
~
src/file.c
~
src/file.c
~
src/stash.c
~
src/stash.c
~
src/stash.c
~
src/stash.c
~
src/undo.c
~
src/undo.c
~
src/undo.c
~
src/undo.c
~
src/update.c
~
src/update.c
~
src/update.c
~
src/update.c
~
src/vfile.c
~
src/vfile.c
~
src/vfile.c
~
src/vfile.c
~
src/winfile.c
~
src/winfile.c
~
src/winfile.c
~
src/winfile.c
~
src/winfile.c
+6
-1
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -1114,12 +1114,17 @@ | ||
| 1114 | 1114 | */ |
| 1115 | 1115 | if( isSelected ){ |
| 1116 | 1116 | int mPerm; |
| 1117 | 1117 | |
| 1118 | 1118 | mPerm = file_wd_perm(blob_str(&filename)); |
| 1119 | +#if !defined(_WIN32) | |
| 1119 | 1120 | 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 ); | |
| 1121 | 1126 | } |
| 1122 | 1127 | |
| 1123 | 1128 | if( isExe ){ |
| 1124 | 1129 | zPerm = " x"; |
| 1125 | 1130 | }else if( isLink ){ |
| 1126 | 1131 |
| --- 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 @@ | ||
| 1114 | 1114 | */ |
| 1115 | 1115 | if( isSelected ){ |
| 1116 | 1116 | int mPerm; |
| 1117 | 1117 | |
| 1118 | 1118 | mPerm = file_wd_perm(blob_str(&filename)); |
| 1119 | +#if !defined(_WIN32) | |
| 1119 | 1120 | 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 ); | |
| 1121 | 1126 | } |
| 1122 | 1127 | |
| 1123 | 1128 | if( isExe ){ |
| 1124 | 1129 | zPerm = " x"; |
| 1125 | 1130 | }else if( isLink ){ |
| 1126 | 1131 |
| --- 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 @@ | ||
| 1114 | 1114 | */ |
| 1115 | 1115 | if( isSelected ){ |
| 1116 | 1116 | int mPerm; |
| 1117 | 1117 | |
| 1118 | 1118 | mPerm = file_wd_perm(blob_str(&filename)); |
| 1119 | +#if !defined(_WIN32) | |
| 1119 | 1120 | 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 ); | |
| 1121 | 1126 | } |
| 1122 | 1127 | |
| 1123 | 1128 | if( isExe ){ |
| 1124 | 1129 | zPerm = " x"; |
| 1125 | 1130 | }else if( isLink ){ |
| 1126 | 1131 |
| --- 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 @@ | ||
| 1114 | 1114 | */ |
| 1115 | 1115 | if( isSelected ){ |
| 1116 | 1116 | int mPerm; |
| 1117 | 1117 | |
| 1118 | 1118 | mPerm = file_wd_perm(blob_str(&filename)); |
| 1119 | +#if !defined(_WIN32) | |
| 1119 | 1120 | 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 ); | |
| 1121 | 1126 | } |
| 1122 | 1127 | |
| 1123 | 1128 | if( isExe ){ |
| 1124 | 1129 | zPerm = " x"; |
| 1125 | 1130 | }else if( isLink ){ |
| 1126 | 1131 |
| --- 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 @@ | ||
| 1114 | 1114 | */ |
| 1115 | 1115 | if( isSelected ){ |
| 1116 | 1116 | int mPerm; |
| 1117 | 1117 | |
| 1118 | 1118 | mPerm = file_wd_perm(blob_str(&filename)); |
| 1119 | +#if !defined(_WIN32) | |
| 1119 | 1120 | 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 ); | |
| 1121 | 1126 | } |
| 1122 | 1127 | |
| 1123 | 1128 | if( isExe ){ |
| 1124 | 1129 | zPerm = " x"; |
| 1125 | 1130 | }else if( isLink ){ |
| 1126 | 1131 |
| --- 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 @@ | ||
| 1114 | 1114 | */ |
| 1115 | 1115 | if( isSelected ){ |
| 1116 | 1116 | int mPerm; |
| 1117 | 1117 | |
| 1118 | 1118 | mPerm = file_wd_perm(blob_str(&filename)); |
| 1119 | +#if !defined(_WIN32) | |
| 1119 | 1120 | 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 ); | |
| 1121 | 1126 | } |
| 1122 | 1127 | |
| 1123 | 1128 | if( isExe ){ |
| 1124 | 1129 | zPerm = " x"; |
| 1125 | 1130 | }else if( isLink ){ |
| 1126 | 1131 |
| --- 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 @@ | ||
| 1114 | 1114 | */ |
| 1115 | 1115 | if( isSelected ){ |
| 1116 | 1116 | int mPerm; |
| 1117 | 1117 | |
| 1118 | 1118 | mPerm = file_wd_perm(blob_str(&filename)); |
| 1119 | +#if !defined(_WIN32) | |
| 1119 | 1120 | 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 ); | |
| 1121 | 1126 | } |
| 1122 | 1127 | |
| 1123 | 1128 | if( isExe ){ |
| 1124 | 1129 | zPerm = " x"; |
| 1125 | 1130 | }else if( isLink ){ |
| 1126 | 1131 |
| --- 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 @@ | ||
| 207 | 207 | } |
| 208 | 208 | nName = file_simplify_name(zName, nName, 0); |
| 209 | 209 | for(i=1; i<nName; i++){ |
| 210 | 210 | if( zName[i]=='/' ){ |
| 211 | 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 | |
| 212 | 220 | if( file_mkdir(zName, 1) ){ |
| 213 | 221 | fossil_fatal_recursive("unable to create directory %s", zName); |
| 214 | 222 | return; |
| 215 | 223 | } |
| 224 | +#if defined(_WIN32) || defined(__CYGWIN__) | |
| 225 | + } | |
| 226 | +#endif | |
| 216 | 227 | zName[i] = '/'; |
| 217 | 228 | } |
| 218 | 229 | } |
| 219 | 230 | #if !defined(_WIN32) |
| 220 | 231 | if( symlink(zTargetFile, zName)!=0 ) |
| @@ -230,10 +241,31 @@ | ||
| 230 | 241 | blob_set(&content, zTargetFile); |
| 231 | 242 | blob_write_to_file(&content, zLinkFile); |
| 232 | 243 | blob_reset(&content); |
| 233 | 244 | } |
| 234 | 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 | +} | |
| 235 | 267 | |
| 236 | 268 | /* |
| 237 | 269 | ** Copy symbolic link from zFrom to zTo. |
| 238 | 270 | */ |
| 239 | 271 | void symlink_copy(const char *zFrom, const char *zTo){ |
| @@ -509,10 +541,28 @@ | ||
| 509 | 541 | int file_delete(const char *zFilename){ |
| 510 | 542 | int rc; |
| 511 | 543 | #ifdef _WIN32 |
| 512 | 544 | wchar_t *z = fossil_utf8_to_filename(zFilename); |
| 513 | 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); | |
| 514 | 564 | #else |
| 515 | 565 | char *z = fossil_utf8_to_filename(zFilename); |
| 516 | 566 | rc = unlink(zFilename); |
| 517 | 567 | #endif |
| 518 | 568 | fossil_filename_free(z); |
| 519 | 569 |
| --- 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 @@ | ||
| 207 | 207 | } |
| 208 | 208 | nName = file_simplify_name(zName, nName, 0); |
| 209 | 209 | for(i=1; i<nName; i++){ |
| 210 | 210 | if( zName[i]=='/' ){ |
| 211 | 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 | |
| 212 | 220 | if( file_mkdir(zName, 1) ){ |
| 213 | 221 | fossil_fatal_recursive("unable to create directory %s", zName); |
| 214 | 222 | return; |
| 215 | 223 | } |
| 224 | +#if defined(_WIN32) || defined(__CYGWIN__) | |
| 225 | + } | |
| 226 | +#endif | |
| 216 | 227 | zName[i] = '/'; |
| 217 | 228 | } |
| 218 | 229 | } |
| 219 | 230 | #if !defined(_WIN32) |
| 220 | 231 | if( symlink(zTargetFile, zName)!=0 ) |
| @@ -230,10 +241,31 @@ | ||
| 230 | 241 | blob_set(&content, zTargetFile); |
| 231 | 242 | blob_write_to_file(&content, zLinkFile); |
| 232 | 243 | blob_reset(&content); |
| 233 | 244 | } |
| 234 | 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 | +} | |
| 235 | 267 | |
| 236 | 268 | /* |
| 237 | 269 | ** Copy symbolic link from zFrom to zTo. |
| 238 | 270 | */ |
| 239 | 271 | void symlink_copy(const char *zFrom, const char *zTo){ |
| @@ -509,10 +541,28 @@ | ||
| 509 | 541 | int file_delete(const char *zFilename){ |
| 510 | 542 | int rc; |
| 511 | 543 | #ifdef _WIN32 |
| 512 | 544 | wchar_t *z = fossil_utf8_to_filename(zFilename); |
| 513 | 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); | |
| 514 | 564 | #else |
| 515 | 565 | char *z = fossil_utf8_to_filename(zFilename); |
| 516 | 566 | rc = unlink(zFilename); |
| 517 | 567 | #endif |
| 518 | 568 | fossil_filename_free(z); |
| 519 | 569 |
| --- 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 @@ | ||
| 207 | 207 | } |
| 208 | 208 | nName = file_simplify_name(zName, nName, 0); |
| 209 | 209 | for(i=1; i<nName; i++){ |
| 210 | 210 | if( zName[i]=='/' ){ |
| 211 | 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 | |
| 212 | 220 | if( file_mkdir(zName, 1) ){ |
| 213 | 221 | fossil_fatal_recursive("unable to create directory %s", zName); |
| 214 | 222 | return; |
| 215 | 223 | } |
| 224 | +#if defined(_WIN32) || defined(__CYGWIN__) | |
| 225 | + } | |
| 226 | +#endif | |
| 216 | 227 | zName[i] = '/'; |
| 217 | 228 | } |
| 218 | 229 | } |
| 219 | 230 | #if !defined(_WIN32) |
| 220 | 231 | if( symlink(zTargetFile, zName)!=0 ) |
| @@ -230,10 +241,31 @@ | ||
| 230 | 241 | blob_set(&content, zTargetFile); |
| 231 | 242 | blob_write_to_file(&content, zLinkFile); |
| 232 | 243 | blob_reset(&content); |
| 233 | 244 | } |
| 234 | 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 | +} | |
| 235 | 267 | |
| 236 | 268 | /* |
| 237 | 269 | ** Copy symbolic link from zFrom to zTo. |
| 238 | 270 | */ |
| 239 | 271 | void symlink_copy(const char *zFrom, const char *zTo){ |
| @@ -509,10 +541,28 @@ | ||
| 509 | 541 | int file_delete(const char *zFilename){ |
| 510 | 542 | int rc; |
| 511 | 543 | #ifdef _WIN32 |
| 512 | 544 | wchar_t *z = fossil_utf8_to_filename(zFilename); |
| 513 | 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); | |
| 514 | 564 | #else |
| 515 | 565 | char *z = fossil_utf8_to_filename(zFilename); |
| 516 | 566 | rc = unlink(zFilename); |
| 517 | 567 | #endif |
| 518 | 568 | fossil_filename_free(z); |
| 519 | 569 |
| --- 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 @@ | ||
| 207 | 207 | } |
| 208 | 208 | nName = file_simplify_name(zName, nName, 0); |
| 209 | 209 | for(i=1; i<nName; i++){ |
| 210 | 210 | if( zName[i]=='/' ){ |
| 211 | 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 | |
| 212 | 220 | if( file_mkdir(zName, 1) ){ |
| 213 | 221 | fossil_fatal_recursive("unable to create directory %s", zName); |
| 214 | 222 | return; |
| 215 | 223 | } |
| 224 | +#if defined(_WIN32) || defined(__CYGWIN__) | |
| 225 | + } | |
| 226 | +#endif | |
| 216 | 227 | zName[i] = '/'; |
| 217 | 228 | } |
| 218 | 229 | } |
| 219 | 230 | #if !defined(_WIN32) |
| 220 | 231 | if( symlink(zTargetFile, zName)!=0 ) |
| @@ -230,10 +241,31 @@ | ||
| 230 | 241 | blob_set(&content, zTargetFile); |
| 231 | 242 | blob_write_to_file(&content, zLinkFile); |
| 232 | 243 | blob_reset(&content); |
| 233 | 244 | } |
| 234 | 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 | +} | |
| 235 | 267 | |
| 236 | 268 | /* |
| 237 | 269 | ** Copy symbolic link from zFrom to zTo. |
| 238 | 270 | */ |
| 239 | 271 | void symlink_copy(const char *zFrom, const char *zTo){ |
| @@ -509,10 +541,28 @@ | ||
| 509 | 541 | int file_delete(const char *zFilename){ |
| 510 | 542 | int rc; |
| 511 | 543 | #ifdef _WIN32 |
| 512 | 544 | wchar_t *z = fossil_utf8_to_filename(zFilename); |
| 513 | 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); | |
| 514 | 564 | #else |
| 515 | 565 | char *z = fossil_utf8_to_filename(zFilename); |
| 516 | 566 | rc = unlink(zFilename); |
| 517 | 567 | #endif |
| 518 | 568 | fossil_filename_free(z); |
| 519 | 569 |
| --- 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 @@ | ||
| 207 | 207 | } |
| 208 | 208 | nName = file_simplify_name(zName, nName, 0); |
| 209 | 209 | for(i=1; i<nName; i++){ |
| 210 | 210 | if( zName[i]=='/' ){ |
| 211 | 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 | |
| 212 | 220 | if( file_mkdir(zName, 1) ){ |
| 213 | 221 | fossil_fatal_recursive("unable to create directory %s", zName); |
| 214 | 222 | return; |
| 215 | 223 | } |
| 224 | +#if defined(_WIN32) || defined(__CYGWIN__) | |
| 225 | + } | |
| 226 | +#endif | |
| 216 | 227 | zName[i] = '/'; |
| 217 | 228 | } |
| 218 | 229 | } |
| 219 | 230 | #if !defined(_WIN32) |
| 220 | 231 | if( symlink(zTargetFile, zName)!=0 ) |
| @@ -230,10 +241,31 @@ | ||
| 230 | 241 | blob_set(&content, zTargetFile); |
| 231 | 242 | blob_write_to_file(&content, zLinkFile); |
| 232 | 243 | blob_reset(&content); |
| 233 | 244 | } |
| 234 | 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 | +} | |
| 235 | 267 | |
| 236 | 268 | /* |
| 237 | 269 | ** Copy symbolic link from zFrom to zTo. |
| 238 | 270 | */ |
| 239 | 271 | void symlink_copy(const char *zFrom, const char *zTo){ |
| @@ -509,10 +541,28 @@ | ||
| 509 | 541 | int file_delete(const char *zFilename){ |
| 510 | 542 | int rc; |
| 511 | 543 | #ifdef _WIN32 |
| 512 | 544 | wchar_t *z = fossil_utf8_to_filename(zFilename); |
| 513 | 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); | |
| 514 | 564 | #else |
| 515 | 565 | char *z = fossil_utf8_to_filename(zFilename); |
| 516 | 566 | rc = unlink(zFilename); |
| 517 | 567 | #endif |
| 518 | 568 | fossil_filename_free(z); |
| 519 | 569 |
| --- 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 @@ | ||
| 207 | 207 | } |
| 208 | 208 | nName = file_simplify_name(zName, nName, 0); |
| 209 | 209 | for(i=1; i<nName; i++){ |
| 210 | 210 | if( zName[i]=='/' ){ |
| 211 | 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 | |
| 212 | 220 | if( file_mkdir(zName, 1) ){ |
| 213 | 221 | fossil_fatal_recursive("unable to create directory %s", zName); |
| 214 | 222 | return; |
| 215 | 223 | } |
| 224 | +#if defined(_WIN32) || defined(__CYGWIN__) | |
| 225 | + } | |
| 226 | +#endif | |
| 216 | 227 | zName[i] = '/'; |
| 217 | 228 | } |
| 218 | 229 | } |
| 219 | 230 | #if !defined(_WIN32) |
| 220 | 231 | if( symlink(zTargetFile, zName)!=0 ) |
| @@ -230,10 +241,31 @@ | ||
| 230 | 241 | blob_set(&content, zTargetFile); |
| 231 | 242 | blob_write_to_file(&content, zLinkFile); |
| 232 | 243 | blob_reset(&content); |
| 233 | 244 | } |
| 234 | 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 | +} | |
| 235 | 267 | |
| 236 | 268 | /* |
| 237 | 269 | ** Copy symbolic link from zFrom to zTo. |
| 238 | 270 | */ |
| 239 | 271 | void symlink_copy(const char *zFrom, const char *zTo){ |
| @@ -509,10 +541,28 @@ | ||
| 509 | 541 | int file_delete(const char *zFilename){ |
| 510 | 542 | int rc; |
| 511 | 543 | #ifdef _WIN32 |
| 512 | 544 | wchar_t *z = fossil_utf8_to_filename(zFilename); |
| 513 | 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); | |
| 514 | 564 | #else |
| 515 | 565 | char *z = fossil_utf8_to_filename(zFilename); |
| 516 | 566 | rc = unlink(zFilename); |
| 517 | 567 | #endif |
| 518 | 568 | fossil_filename_free(z); |
| 519 | 569 |
| --- 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 @@ | ||
| 207 | 207 | } |
| 208 | 208 | nName = file_simplify_name(zName, nName, 0); |
| 209 | 209 | for(i=1; i<nName; i++){ |
| 210 | 210 | if( zName[i]=='/' ){ |
| 211 | 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 | |
| 212 | 220 | if( file_mkdir(zName, 1) ){ |
| 213 | 221 | fossil_fatal_recursive("unable to create directory %s", zName); |
| 214 | 222 | return; |
| 215 | 223 | } |
| 224 | +#if defined(_WIN32) || defined(__CYGWIN__) | |
| 225 | + } | |
| 226 | +#endif | |
| 216 | 227 | zName[i] = '/'; |
| 217 | 228 | } |
| 218 | 229 | } |
| 219 | 230 | #if !defined(_WIN32) |
| 220 | 231 | if( symlink(zTargetFile, zName)!=0 ) |
| @@ -230,10 +241,31 @@ | ||
| 230 | 241 | blob_set(&content, zTargetFile); |
| 231 | 242 | blob_write_to_file(&content, zLinkFile); |
| 232 | 243 | blob_reset(&content); |
| 233 | 244 | } |
| 234 | 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 | +} | |
| 235 | 267 | |
| 236 | 268 | /* |
| 237 | 269 | ** Copy symbolic link from zFrom to zTo. |
| 238 | 270 | */ |
| 239 | 271 | void symlink_copy(const char *zFrom, const char *zTo){ |
| @@ -509,10 +541,28 @@ | ||
| 509 | 541 | int file_delete(const char *zFilename){ |
| 510 | 542 | int rc; |
| 511 | 543 | #ifdef _WIN32 |
| 512 | 544 | wchar_t *z = fossil_utf8_to_filename(zFilename); |
| 513 | 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); | |
| 514 | 564 | #else |
| 515 | 565 | char *z = fossil_utf8_to_filename(zFilename); |
| 516 | 566 | rc = unlink(zFilename); |
| 517 | 567 | #endif |
| 518 | 568 | fossil_filename_free(z); |
| 519 | 569 |
| --- 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 @@ | ||
| 241 | 241 | blob_read_from_file(&disk, zOPath); |
| 242 | 242 | } |
| 243 | 243 | content_get(rid, &a); |
| 244 | 244 | blob_delta_apply(&a, &delta, &b); |
| 245 | 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 | - } | |
| 246 | + create_symlink_or_file(1, isLink, isNewLink, &b, zNPath); | |
| 254 | 247 | file_wd_setexe(zNPath, isExec); |
| 255 | 248 | fossil_print("UPDATE %s\n", zNew); |
| 256 | 249 | }else{ |
| 257 | 250 | int rc; |
| 258 | 251 | if( isLink || isNewLink ){ |
| 259 | 252 |
| --- 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 @@ | ||
| 241 | 241 | blob_read_from_file(&disk, zOPath); |
| 242 | 242 | } |
| 243 | 243 | content_get(rid, &a); |
| 244 | 244 | blob_delta_apply(&a, &delta, &b); |
| 245 | 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 | - } | |
| 246 | + create_symlink_or_file(1, isLink, isNewLink, &b, zNPath); | |
| 254 | 247 | file_wd_setexe(zNPath, isExec); |
| 255 | 248 | fossil_print("UPDATE %s\n", zNew); |
| 256 | 249 | }else{ |
| 257 | 250 | int rc; |
| 258 | 251 | if( isLink || isNewLink ){ |
| 259 | 252 |
| --- 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 @@ | ||
| 241 | 241 | blob_read_from_file(&disk, zOPath); |
| 242 | 242 | } |
| 243 | 243 | content_get(rid, &a); |
| 244 | 244 | blob_delta_apply(&a, &delta, &b); |
| 245 | 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 | - } | |
| 246 | + create_symlink_or_file(1, isLink, isNewLink, &b, zNPath); | |
| 254 | 247 | file_wd_setexe(zNPath, isExec); |
| 255 | 248 | fossil_print("UPDATE %s\n", zNew); |
| 256 | 249 | }else{ |
| 257 | 250 | int rc; |
| 258 | 251 | if( isLink || isNewLink ){ |
| 259 | 252 |
| --- 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 @@ | ||
| 241 | 241 | blob_read_from_file(&disk, zOPath); |
| 242 | 242 | } |
| 243 | 243 | content_get(rid, &a); |
| 244 | 244 | blob_delta_apply(&a, &delta, &b); |
| 245 | 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 | - } | |
| 246 | + create_symlink_or_file(1, isLink, isNewLink, &b, zNPath); | |
| 254 | 247 | file_wd_setexe(zNPath, isExec); |
| 255 | 248 | fossil_print("UPDATE %s\n", zNew); |
| 256 | 249 | }else{ |
| 257 | 250 | int rc; |
| 258 | 251 | if( isLink || isNewLink ){ |
| 259 | 252 |
| --- 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 @@ | ||
| 70 | 70 | if( new_exists ){ |
| 71 | 71 | fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname); |
| 72 | 72 | }else{ |
| 73 | 73 | fossil_print("NEW %s\n", zPathname); |
| 74 | 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 | - } | |
| 75 | + create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname); | |
| 83 | 76 | file_wd_setexe(zFullname, old_exe); |
| 84 | 77 | }else{ |
| 85 | 78 | fossil_print("DELETE %s\n", zPathname); |
| 86 | 79 | file_delete(zFullname); |
| 87 | 80 | } |
| 88 | 81 |
| --- 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 @@ | ||
| 70 | 70 | if( new_exists ){ |
| 71 | 71 | fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname); |
| 72 | 72 | }else{ |
| 73 | 73 | fossil_print("NEW %s\n", zPathname); |
| 74 | 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 | - } | |
| 75 | + create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname); | |
| 83 | 76 | file_wd_setexe(zFullname, old_exe); |
| 84 | 77 | }else{ |
| 85 | 78 | fossil_print("DELETE %s\n", zPathname); |
| 86 | 79 | file_delete(zFullname); |
| 87 | 80 | } |
| 88 | 81 |
| --- 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 @@ | ||
| 70 | 70 | if( new_exists ){ |
| 71 | 71 | fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname); |
| 72 | 72 | }else{ |
| 73 | 73 | fossil_print("NEW %s\n", zPathname); |
| 74 | 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 | - } | |
| 75 | + create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname); | |
| 83 | 76 | file_wd_setexe(zFullname, old_exe); |
| 84 | 77 | }else{ |
| 85 | 78 | fossil_print("DELETE %s\n", zPathname); |
| 86 | 79 | file_delete(zFullname); |
| 87 | 80 | } |
| 88 | 81 |
| --- 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 @@ | ||
| 70 | 70 | if( new_exists ){ |
| 71 | 71 | fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname); |
| 72 | 72 | }else{ |
| 73 | 73 | fossil_print("NEW %s\n", zPathname); |
| 74 | 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 | - } | |
| 75 | + create_symlink_or_file(new_exists, old_link, new_link, &new, zFullname); | |
| 83 | 76 | file_wd_setexe(zFullname, old_exe); |
| 84 | 77 | }else{ |
| 85 | 78 | fossil_print("DELETE %s\n", zPathname); |
| 86 | 79 | file_delete(zFullname); |
| 87 | 80 | } |
| 88 | 81 |
| --- 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 @@ | ||
| 766 | 766 | db_multi_exec( |
| 767 | 767 | "DELETE FROM vmerge;" |
| 768 | 768 | "INSERT OR IGNORE INTO torevert " |
| 769 | 769 | " SELECT pathname" |
| 770 | 770 | " 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;" | |
| 772 | 772 | ); |
| 773 | 773 | } |
| 774 | 774 | db_multi_exec( |
| 775 | 775 | "INSERT OR IGNORE INTO torevert" |
| 776 | 776 | " SELECT origname" |
| @@ -808,18 +808,11 @@ | ||
| 808 | 808 | zFile, zFile |
| 809 | 809 | ); |
| 810 | 810 | }else{ |
| 811 | 811 | sqlite3_int64 mtime; |
| 812 | 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 | - } | |
| 813 | + create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull); | |
| 821 | 814 | file_wd_setexe(zFull, isExe); |
| 822 | 815 | fossil_print("REVERTED: %s\n", zFile); |
| 823 | 816 | mtime = file_wd_mtime(zFull); |
| 824 | 817 | db_multi_exec( |
| 825 | 818 | "UPDATE vfile" |
| 826 | 819 |
| --- 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 @@ | ||
| 766 | 766 | db_multi_exec( |
| 767 | 767 | "DELETE FROM vmerge;" |
| 768 | 768 | "INSERT OR IGNORE INTO torevert " |
| 769 | 769 | " SELECT pathname" |
| 770 | 770 | " 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;" | |
| 772 | 772 | ); |
| 773 | 773 | } |
| 774 | 774 | db_multi_exec( |
| 775 | 775 | "INSERT OR IGNORE INTO torevert" |
| 776 | 776 | " SELECT origname" |
| @@ -808,18 +808,11 @@ | ||
| 808 | 808 | zFile, zFile |
| 809 | 809 | ); |
| 810 | 810 | }else{ |
| 811 | 811 | sqlite3_int64 mtime; |
| 812 | 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 | - } | |
| 813 | + create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull); | |
| 821 | 814 | file_wd_setexe(zFull, isExe); |
| 822 | 815 | fossil_print("REVERTED: %s\n", zFile); |
| 823 | 816 | mtime = file_wd_mtime(zFull); |
| 824 | 817 | db_multi_exec( |
| 825 | 818 | "UPDATE vfile" |
| 826 | 819 |
| --- 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 @@ | ||
| 766 | 766 | db_multi_exec( |
| 767 | 767 | "DELETE FROM vmerge;" |
| 768 | 768 | "INSERT OR IGNORE INTO torevert " |
| 769 | 769 | " SELECT pathname" |
| 770 | 770 | " 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;" | |
| 772 | 772 | ); |
| 773 | 773 | } |
| 774 | 774 | db_multi_exec( |
| 775 | 775 | "INSERT OR IGNORE INTO torevert" |
| 776 | 776 | " SELECT origname" |
| @@ -808,18 +808,11 @@ | ||
| 808 | 808 | zFile, zFile |
| 809 | 809 | ); |
| 810 | 810 | }else{ |
| 811 | 811 | sqlite3_int64 mtime; |
| 812 | 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 | - } | |
| 813 | + create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull); | |
| 821 | 814 | file_wd_setexe(zFull, isExe); |
| 822 | 815 | fossil_print("REVERTED: %s\n", zFile); |
| 823 | 816 | mtime = file_wd_mtime(zFull); |
| 824 | 817 | db_multi_exec( |
| 825 | 818 | "UPDATE vfile" |
| 826 | 819 |
| --- 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 @@ | ||
| 766 | 766 | db_multi_exec( |
| 767 | 767 | "DELETE FROM vmerge;" |
| 768 | 768 | "INSERT OR IGNORE INTO torevert " |
| 769 | 769 | " SELECT pathname" |
| 770 | 770 | " 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;" | |
| 772 | 772 | ); |
| 773 | 773 | } |
| 774 | 774 | db_multi_exec( |
| 775 | 775 | "INSERT OR IGNORE INTO torevert" |
| 776 | 776 | " SELECT origname" |
| @@ -808,18 +808,11 @@ | ||
| 808 | 808 | zFile, zFile |
| 809 | 809 | ); |
| 810 | 810 | }else{ |
| 811 | 811 | sqlite3_int64 mtime; |
| 812 | 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 | - } | |
| 813 | + create_symlink_or_file(file_wd_size(zFull)>=0, isLink, file_wd_islink(zFull), &record, zFull); | |
| 821 | 814 | file_wd_setexe(zFull, isExe); |
| 822 | 815 | fossil_print("REVERTED: %s\n", zFile); |
| 823 | 816 | mtime = file_wd_mtime(zFull); |
| 824 | 817 | db_multi_exec( |
| 825 | 818 | "UPDATE vfile" |
| 826 | 819 |
| --- 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 @@ | ||
| 320 | 320 | if( verbose ) fossil_print("%s\n", &zName[nRepos]); |
| 321 | 321 | if( file_wd_isdir(zName) == 1 ){ |
| 322 | 322 | /*TODO(dchest): remove directories? */ |
| 323 | 323 | fossil_fatal("%s is directory, cannot overwrite\n", zName); |
| 324 | 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 | - } | |
| 325 | + create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName); | |
| 333 | 326 | file_wd_setexe(zName, isExe); |
| 334 | 327 | blob_reset(&content); |
| 335 | 328 | db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", |
| 336 | 329 | file_wd_mtime(zName), id); |
| 337 | 330 | } |
| 338 | 331 |
| --- 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 @@ | ||
| 320 | 320 | if( verbose ) fossil_print("%s\n", &zName[nRepos]); |
| 321 | 321 | if( file_wd_isdir(zName) == 1 ){ |
| 322 | 322 | /*TODO(dchest): remove directories? */ |
| 323 | 323 | fossil_fatal("%s is directory, cannot overwrite\n", zName); |
| 324 | 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 | - } | |
| 325 | + create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName); | |
| 333 | 326 | file_wd_setexe(zName, isExe); |
| 334 | 327 | blob_reset(&content); |
| 335 | 328 | db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", |
| 336 | 329 | file_wd_mtime(zName), id); |
| 337 | 330 | } |
| 338 | 331 |
| --- 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 @@ | ||
| 320 | 320 | if( verbose ) fossil_print("%s\n", &zName[nRepos]); |
| 321 | 321 | if( file_wd_isdir(zName) == 1 ){ |
| 322 | 322 | /*TODO(dchest): remove directories? */ |
| 323 | 323 | fossil_fatal("%s is directory, cannot overwrite\n", zName); |
| 324 | 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 | - } | |
| 325 | + create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName); | |
| 333 | 326 | file_wd_setexe(zName, isExe); |
| 334 | 327 | blob_reset(&content); |
| 335 | 328 | db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", |
| 336 | 329 | file_wd_mtime(zName), id); |
| 337 | 330 | } |
| 338 | 331 |
| --- 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 @@ | ||
| 320 | 320 | if( verbose ) fossil_print("%s\n", &zName[nRepos]); |
| 321 | 321 | if( file_wd_isdir(zName) == 1 ){ |
| 322 | 322 | /*TODO(dchest): remove directories? */ |
| 323 | 323 | fossil_fatal("%s is directory, cannot overwrite\n", zName); |
| 324 | 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 | - } | |
| 325 | + create_symlink_or_file(file_wd_size(zName)>=0, isLink, file_wd_islink(zName), &content, zName); | |
| 333 | 326 | file_wd_setexe(zName, isExe); |
| 334 | 327 | blob_reset(&content); |
| 335 | 328 | db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", |
| 336 | 329 | file_wd_mtime(zName), id); |
| 337 | 330 | } |
| 338 | 331 |
| --- 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 @@ | ||
| 28 | 28 | |
| 29 | 29 | #ifndef LABEL_SECURITY_INFORMATION |
| 30 | 30 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 31 | 31 | #endif |
| 32 | 32 | |
| 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 | + | |
| 34 | 43 | typedef struct _REPARSE_DATA_BUFFER { |
| 35 | - ULONG ReparseTag; | |
| 44 | + ULONG ReparseTag; | |
| 36 | 45 | USHORT ReparseDataLength; |
| 37 | 46 | USHORT Reserved; |
| 38 | - union { | |
| 47 | + _ANONYMOUS_UNION union { | |
| 39 | 48 | struct { |
| 40 | 49 | USHORT SubstituteNameOffset; |
| 41 | 50 | USHORT SubstituteNameLength; |
| 42 | 51 | USHORT PrintNameOffset; |
| 43 | 52 | USHORT PrintNameLength; |
| 44 | - ULONG Flags; | |
| 45 | - WCHAR PathBuffer[1]; | |
| 53 | + ULONG Flags; | |
| 54 | + WCHAR PathBuffer[1]; | |
| 46 | 55 | } SymbolicLinkReparseBuffer; |
| 47 | 56 | struct { |
| 48 | 57 | USHORT SubstituteNameOffset; |
| 49 | 58 | USHORT SubstituteNameLength; |
| 50 | 59 | USHORT PrintNameOffset; |
| 51 | 60 | USHORT PrintNameLength; |
| 52 | - WCHAR PathBuffer[1]; | |
| 61 | + WCHAR PathBuffer[1]; | |
| 53 | 62 | } MountPointReparseBuffer; |
| 54 | 63 | struct { |
| 55 | 64 | UCHAR DataBuffer[1]; |
| 56 | 65 | } GenericReparseBuffer; |
| 57 | - }; | |
| 66 | + } DUMMYUNIONNAME; | |
| 58 | 67 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 59 | 68 | |
| 60 | 69 | #define LINK_BUFFER_SIZE 1024 |
| 61 | 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 | +*/ | |
| 62 | 77 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 63 | 78 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 64 | 79 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 65 | 80 | 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 | + | |
| 69 | 92 | ULARGE_INTEGER ull; |
| 70 | 93 | |
| 94 | + /* if a link was retrieved, it is a symlink, otherwise a dir or file */ | |
| 71 | 95 | buf->st_mode = (tlen > 0) ? S_IFLNK : |
| 72 | 96 | ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 73 | 97 | S_IFDIR : S_IFREG); |
| 74 | 98 | |
| 75 | 99 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| @@ -80,40 +104,47 @@ | ||
| 80 | 104 | } |
| 81 | 105 | return !rc; |
| 82 | 106 | } |
| 83 | 107 | |
| 84 | 108 | /* |
| 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. | |
| 88 | 117 | */ |
| 89 | 118 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 90 | 119 | int rc; |
| 91 | 120 | HANDLE file; |
| 92 | 121 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 93 | 122 | DWORD len; |
| 123 | + int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ | |
| 94 | 124 | |
| 95 | - while (1){ | |
| 125 | + while (iterationsRemaining-- > 0){ | |
| 96 | 126 | rc = win32_lstat(zFilename, buf); |
| 127 | + | |
| 97 | 128 | /* exit on error or not link */ |
| 98 | 129 | if ((rc != 0) || (buf->st_mode != S_IFLNK)) |
| 99 | 130 | break; |
| 100 | 131 | |
| 101 | 132 | /* it is a link, so open the linked file */ |
| 102 | 133 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); |
| 103 | 134 | if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ |
| 104 | - rc = -1; | |
| 135 | + rc = 1; | |
| 105 | 136 | break; |
| 106 | 137 | } |
| 107 | 138 | |
| 108 | 139 | /* get the final path name and close the handle */ |
| 109 | 140 | len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 110 | 141 | CloseHandle(file); |
| 111 | 142 | |
| 112 | 143 | /* if any problems getting the final path name error so exit */ |
| 113 | 144 | if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ |
| 114 | - rc = -1; | |
| 145 | + rc = 1; | |
| 115 | 146 | break; |
| 116 | 147 | } |
| 117 | 148 | |
| 118 | 149 | /* prepare to try again just in case we have a chain to follow */ |
| 119 | 150 | /* this shouldn't happen, but just trying to be safe */ |
| @@ -121,10 +152,15 @@ | ||
| 121 | 152 | } |
| 122 | 153 | |
| 123 | 154 | return rc; |
| 124 | 155 | } |
| 125 | 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 | +*/ | |
| 126 | 162 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 127 | 163 | /* assume we're going to fail */ |
| 128 | 164 | ssize_t rv = -1; |
| 129 | 165 | |
| 130 | 166 | /* does path reference a reparse point? */ |
| @@ -137,49 +173,79 @@ | ||
| 137 | 173 | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 138 | 174 | if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ |
| 139 | 175 | |
| 140 | 176 | /* use DeviceIoControl to get the reparse point data */ |
| 141 | 177 | |
| 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; | |
| 151 | 185 | |
| 152 | 186 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 153 | - &u, sizeof(u), &bytes, NULL); | |
| 187 | + data, data_size, &data_used, NULL); | |
| 154 | 188 | |
| 155 | 189 | /* did the reparse point data fit into the desired buffer? */ |
| 156 | - if (rc && (bytes < sizeof(u))){ | |
| 190 | + if (rc && (data_used < data_size)){ | |
| 157 | 191 | /* it fit, so setup the print name for further processing */ |
| 158 | 192 | 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); | |
| 161 | 195 | char *temp; |
| 162 | - u.data.SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 196 | + data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 163 | 197 | |
| 164 | 198 | /* 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); | |
| 166 | 200 | rv = strlen(temp); |
| 167 | 201 | if (rv >= bufsiz) |
| 168 | 202 | rv = bufsiz; |
| 169 | 203 | memcpy(buf, temp, rv); |
| 170 | 204 | fossil_filename_free(temp); |
| 171 | 205 | } |
| 206 | + | |
| 207 | + fossil_free(data); | |
| 172 | 208 | |
| 173 | 209 | /* all done, close the reparse point */ |
| 174 | 210 | CloseHandle(file); |
| 175 | 211 | } |
| 176 | 212 | } |
| 177 | 213 | |
| 178 | 214 | return rv; |
| 179 | 215 | } |
| 180 | 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 | +*/ | |
| 181 | 247 | int win32_symlink(const char *oldpath, const char *newpath){ |
| 182 | 248 | fossilStat stat; |
| 183 | 249 | int created = 0; |
| 184 | 250 | DWORD flags = 0; |
| 185 | 251 | wchar_t *zMbcs; |
| @@ -187,10 +253,12 @@ | ||
| 187 | 253 | /* does oldpath exist? is it a dir or a file? */ |
| 188 | 254 | zMbcs = fossil_utf8_to_filename(oldpath); |
| 189 | 255 | if (win32_stat(zMbcs, &stat) == 0){ |
| 190 | 256 | if (stat.st_mode == S_IFDIR) |
| 191 | 257 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 192 | - DeleteFile(newpath); | |
| 193 | - if (CreateSymbolicLink(newpath, oldpath, flags)) | |
| 194 | - created = 1; | |
| 195 | 258 | } |
| 196 | 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); | |
| 197 | 267 | |
| 198 | 268 | /* if the symlink was not created, create a plain text file */ |
| 199 | 269 | if (!created){ |
| 200 | 270 | Blob content; |
| 201 | 271 | blob_set(&content, oldpath); |
| @@ -202,13 +272,21 @@ | ||
| 202 | 272 | blob_write_to_file(&content, newpath); |
| 203 | 273 | blob_reset(&content); |
| 204 | 274 | created = 1; |
| 205 | 275 | } |
| 206 | 276 | |
| 207 | - return created ? 0 : -1; | |
| 277 | + return !created; | |
| 208 | 278 | } |
| 209 | 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 | +*/ | |
| 210 | 288 | int win32_symlinks_supported(){ |
| 211 | 289 | TOKEN_PRIVILEGES tp; |
| 212 | 290 | LUID luid; |
| 213 | 291 | HANDLE process, token; |
| 214 | 292 | DWORD status; |
| 215 | 293 |
| --- 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 @@ | ||
| 28 | 28 | |
| 29 | 29 | #ifndef LABEL_SECURITY_INFORMATION |
| 30 | 30 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 31 | 31 | #endif |
| 32 | 32 | |
| 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 | + | |
| 34 | 43 | typedef struct _REPARSE_DATA_BUFFER { |
| 35 | - ULONG ReparseTag; | |
| 44 | + ULONG ReparseTag; | |
| 36 | 45 | USHORT ReparseDataLength; |
| 37 | 46 | USHORT Reserved; |
| 38 | - union { | |
| 47 | + _ANONYMOUS_UNION union { | |
| 39 | 48 | struct { |
| 40 | 49 | USHORT SubstituteNameOffset; |
| 41 | 50 | USHORT SubstituteNameLength; |
| 42 | 51 | USHORT PrintNameOffset; |
| 43 | 52 | USHORT PrintNameLength; |
| 44 | - ULONG Flags; | |
| 45 | - WCHAR PathBuffer[1]; | |
| 53 | + ULONG Flags; | |
| 54 | + WCHAR PathBuffer[1]; | |
| 46 | 55 | } SymbolicLinkReparseBuffer; |
| 47 | 56 | struct { |
| 48 | 57 | USHORT SubstituteNameOffset; |
| 49 | 58 | USHORT SubstituteNameLength; |
| 50 | 59 | USHORT PrintNameOffset; |
| 51 | 60 | USHORT PrintNameLength; |
| 52 | - WCHAR PathBuffer[1]; | |
| 61 | + WCHAR PathBuffer[1]; | |
| 53 | 62 | } MountPointReparseBuffer; |
| 54 | 63 | struct { |
| 55 | 64 | UCHAR DataBuffer[1]; |
| 56 | 65 | } GenericReparseBuffer; |
| 57 | - }; | |
| 66 | + } DUMMYUNIONNAME; | |
| 58 | 67 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 59 | 68 | |
| 60 | 69 | #define LINK_BUFFER_SIZE 1024 |
| 61 | 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 | +*/ | |
| 62 | 77 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 63 | 78 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 64 | 79 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 65 | 80 | 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 | + | |
| 69 | 92 | ULARGE_INTEGER ull; |
| 70 | 93 | |
| 94 | + /* if a link was retrieved, it is a symlink, otherwise a dir or file */ | |
| 71 | 95 | buf->st_mode = (tlen > 0) ? S_IFLNK : |
| 72 | 96 | ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 73 | 97 | S_IFDIR : S_IFREG); |
| 74 | 98 | |
| 75 | 99 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| @@ -80,40 +104,47 @@ | ||
| 80 | 104 | } |
| 81 | 105 | return !rc; |
| 82 | 106 | } |
| 83 | 107 | |
| 84 | 108 | /* |
| 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. | |
| 88 | 117 | */ |
| 89 | 118 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 90 | 119 | int rc; |
| 91 | 120 | HANDLE file; |
| 92 | 121 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 93 | 122 | DWORD len; |
| 123 | + int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ | |
| 94 | 124 | |
| 95 | - while (1){ | |
| 125 | + while (iterationsRemaining-- > 0){ | |
| 96 | 126 | rc = win32_lstat(zFilename, buf); |
| 127 | + | |
| 97 | 128 | /* exit on error or not link */ |
| 98 | 129 | if ((rc != 0) || (buf->st_mode != S_IFLNK)) |
| 99 | 130 | break; |
| 100 | 131 | |
| 101 | 132 | /* it is a link, so open the linked file */ |
| 102 | 133 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); |
| 103 | 134 | if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ |
| 104 | - rc = -1; | |
| 135 | + rc = 1; | |
| 105 | 136 | break; |
| 106 | 137 | } |
| 107 | 138 | |
| 108 | 139 | /* get the final path name and close the handle */ |
| 109 | 140 | len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 110 | 141 | CloseHandle(file); |
| 111 | 142 | |
| 112 | 143 | /* if any problems getting the final path name error so exit */ |
| 113 | 144 | if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ |
| 114 | - rc = -1; | |
| 145 | + rc = 1; | |
| 115 | 146 | break; |
| 116 | 147 | } |
| 117 | 148 | |
| 118 | 149 | /* prepare to try again just in case we have a chain to follow */ |
| 119 | 150 | /* this shouldn't happen, but just trying to be safe */ |
| @@ -121,10 +152,15 @@ | ||
| 121 | 152 | } |
| 122 | 153 | |
| 123 | 154 | return rc; |
| 124 | 155 | } |
| 125 | 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 | +*/ | |
| 126 | 162 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 127 | 163 | /* assume we're going to fail */ |
| 128 | 164 | ssize_t rv = -1; |
| 129 | 165 | |
| 130 | 166 | /* does path reference a reparse point? */ |
| @@ -137,49 +173,79 @@ | ||
| 137 | 173 | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 138 | 174 | if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ |
| 139 | 175 | |
| 140 | 176 | /* use DeviceIoControl to get the reparse point data */ |
| 141 | 177 | |
| 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; | |
| 151 | 185 | |
| 152 | 186 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 153 | - &u, sizeof(u), &bytes, NULL); | |
| 187 | + data, data_size, &data_used, NULL); | |
| 154 | 188 | |
| 155 | 189 | /* did the reparse point data fit into the desired buffer? */ |
| 156 | - if (rc && (bytes < sizeof(u))){ | |
| 190 | + if (rc && (data_used < data_size)){ | |
| 157 | 191 | /* it fit, so setup the print name for further processing */ |
| 158 | 192 | 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); | |
| 161 | 195 | char *temp; |
| 162 | - u.data.SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 196 | + data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 163 | 197 | |
| 164 | 198 | /* 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); | |
| 166 | 200 | rv = strlen(temp); |
| 167 | 201 | if (rv >= bufsiz) |
| 168 | 202 | rv = bufsiz; |
| 169 | 203 | memcpy(buf, temp, rv); |
| 170 | 204 | fossil_filename_free(temp); |
| 171 | 205 | } |
| 206 | + | |
| 207 | + fossil_free(data); | |
| 172 | 208 | |
| 173 | 209 | /* all done, close the reparse point */ |
| 174 | 210 | CloseHandle(file); |
| 175 | 211 | } |
| 176 | 212 | } |
| 177 | 213 | |
| 178 | 214 | return rv; |
| 179 | 215 | } |
| 180 | 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 | +*/ | |
| 181 | 247 | int win32_symlink(const char *oldpath, const char *newpath){ |
| 182 | 248 | fossilStat stat; |
| 183 | 249 | int created = 0; |
| 184 | 250 | DWORD flags = 0; |
| 185 | 251 | wchar_t *zMbcs; |
| @@ -187,10 +253,12 @@ | ||
| 187 | 253 | /* does oldpath exist? is it a dir or a file? */ |
| 188 | 254 | zMbcs = fossil_utf8_to_filename(oldpath); |
| 189 | 255 | if (win32_stat(zMbcs, &stat) == 0){ |
| 190 | 256 | if (stat.st_mode == S_IFDIR) |
| 191 | 257 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 192 | - DeleteFile(newpath); | |
| 193 | - if (CreateSymbolicLink(newpath, oldpath, flags)) | |
| 194 | - created = 1; | |
| 195 | 258 | } |
| 196 | 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); | |
| 197 | 267 | |
| 198 | 268 | /* if the symlink was not created, create a plain text file */ |
| 199 | 269 | if (!created){ |
| 200 | 270 | Blob content; |
| 201 | 271 | blob_set(&content, oldpath); |
| @@ -202,13 +272,21 @@ | ||
| 202 | 272 | blob_write_to_file(&content, newpath); |
| 203 | 273 | blob_reset(&content); |
| 204 | 274 | created = 1; |
| 205 | 275 | } |
| 206 | 276 | |
| 207 | - return created ? 0 : -1; | |
| 277 | + return !created; | |
| 208 | 278 | } |
| 209 | 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 | +*/ | |
| 210 | 288 | int win32_symlinks_supported(){ |
| 211 | 289 | TOKEN_PRIVILEGES tp; |
| 212 | 290 | LUID luid; |
| 213 | 291 | HANDLE process, token; |
| 214 | 292 | DWORD status; |
| 215 | 293 |
| --- 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 @@ | ||
| 28 | 28 | |
| 29 | 29 | #ifndef LABEL_SECURITY_INFORMATION |
| 30 | 30 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 31 | 31 | #endif |
| 32 | 32 | |
| 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 | + | |
| 34 | 43 | typedef struct _REPARSE_DATA_BUFFER { |
| 35 | - ULONG ReparseTag; | |
| 44 | + ULONG ReparseTag; | |
| 36 | 45 | USHORT ReparseDataLength; |
| 37 | 46 | USHORT Reserved; |
| 38 | - union { | |
| 47 | + _ANONYMOUS_UNION union { | |
| 39 | 48 | struct { |
| 40 | 49 | USHORT SubstituteNameOffset; |
| 41 | 50 | USHORT SubstituteNameLength; |
| 42 | 51 | USHORT PrintNameOffset; |
| 43 | 52 | USHORT PrintNameLength; |
| 44 | - ULONG Flags; | |
| 45 | - WCHAR PathBuffer[1]; | |
| 53 | + ULONG Flags; | |
| 54 | + WCHAR PathBuffer[1]; | |
| 46 | 55 | } SymbolicLinkReparseBuffer; |
| 47 | 56 | struct { |
| 48 | 57 | USHORT SubstituteNameOffset; |
| 49 | 58 | USHORT SubstituteNameLength; |
| 50 | 59 | USHORT PrintNameOffset; |
| 51 | 60 | USHORT PrintNameLength; |
| 52 | - WCHAR PathBuffer[1]; | |
| 61 | + WCHAR PathBuffer[1]; | |
| 53 | 62 | } MountPointReparseBuffer; |
| 54 | 63 | struct { |
| 55 | 64 | UCHAR DataBuffer[1]; |
| 56 | 65 | } GenericReparseBuffer; |
| 57 | - }; | |
| 66 | + } DUMMYUNIONNAME; | |
| 58 | 67 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 59 | 68 | |
| 60 | 69 | #define LINK_BUFFER_SIZE 1024 |
| 61 | 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 | +*/ | |
| 62 | 77 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 63 | 78 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 64 | 79 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 65 | 80 | 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 | + | |
| 69 | 92 | ULARGE_INTEGER ull; |
| 70 | 93 | |
| 94 | + /* if a link was retrieved, it is a symlink, otherwise a dir or file */ | |
| 71 | 95 | buf->st_mode = (tlen > 0) ? S_IFLNK : |
| 72 | 96 | ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 73 | 97 | S_IFDIR : S_IFREG); |
| 74 | 98 | |
| 75 | 99 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| @@ -80,40 +104,47 @@ | ||
| 80 | 104 | } |
| 81 | 105 | return !rc; |
| 82 | 106 | } |
| 83 | 107 | |
| 84 | 108 | /* |
| 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. | |
| 88 | 117 | */ |
| 89 | 118 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 90 | 119 | int rc; |
| 91 | 120 | HANDLE file; |
| 92 | 121 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 93 | 122 | DWORD len; |
| 123 | + int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ | |
| 94 | 124 | |
| 95 | - while (1){ | |
| 125 | + while (iterationsRemaining-- > 0){ | |
| 96 | 126 | rc = win32_lstat(zFilename, buf); |
| 127 | + | |
| 97 | 128 | /* exit on error or not link */ |
| 98 | 129 | if ((rc != 0) || (buf->st_mode != S_IFLNK)) |
| 99 | 130 | break; |
| 100 | 131 | |
| 101 | 132 | /* it is a link, so open the linked file */ |
| 102 | 133 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); |
| 103 | 134 | if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ |
| 104 | - rc = -1; | |
| 135 | + rc = 1; | |
| 105 | 136 | break; |
| 106 | 137 | } |
| 107 | 138 | |
| 108 | 139 | /* get the final path name and close the handle */ |
| 109 | 140 | len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 110 | 141 | CloseHandle(file); |
| 111 | 142 | |
| 112 | 143 | /* if any problems getting the final path name error so exit */ |
| 113 | 144 | if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ |
| 114 | - rc = -1; | |
| 145 | + rc = 1; | |
| 115 | 146 | break; |
| 116 | 147 | } |
| 117 | 148 | |
| 118 | 149 | /* prepare to try again just in case we have a chain to follow */ |
| 119 | 150 | /* this shouldn't happen, but just trying to be safe */ |
| @@ -121,10 +152,15 @@ | ||
| 121 | 152 | } |
| 122 | 153 | |
| 123 | 154 | return rc; |
| 124 | 155 | } |
| 125 | 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 | +*/ | |
| 126 | 162 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 127 | 163 | /* assume we're going to fail */ |
| 128 | 164 | ssize_t rv = -1; |
| 129 | 165 | |
| 130 | 166 | /* does path reference a reparse point? */ |
| @@ -137,49 +173,79 @@ | ||
| 137 | 173 | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 138 | 174 | if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ |
| 139 | 175 | |
| 140 | 176 | /* use DeviceIoControl to get the reparse point data */ |
| 141 | 177 | |
| 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; | |
| 151 | 185 | |
| 152 | 186 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 153 | - &u, sizeof(u), &bytes, NULL); | |
| 187 | + data, data_size, &data_used, NULL); | |
| 154 | 188 | |
| 155 | 189 | /* did the reparse point data fit into the desired buffer? */ |
| 156 | - if (rc && (bytes < sizeof(u))){ | |
| 190 | + if (rc && (data_used < data_size)){ | |
| 157 | 191 | /* it fit, so setup the print name for further processing */ |
| 158 | 192 | 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); | |
| 161 | 195 | char *temp; |
| 162 | - u.data.SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 196 | + data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 163 | 197 | |
| 164 | 198 | /* 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); | |
| 166 | 200 | rv = strlen(temp); |
| 167 | 201 | if (rv >= bufsiz) |
| 168 | 202 | rv = bufsiz; |
| 169 | 203 | memcpy(buf, temp, rv); |
| 170 | 204 | fossil_filename_free(temp); |
| 171 | 205 | } |
| 206 | + | |
| 207 | + fossil_free(data); | |
| 172 | 208 | |
| 173 | 209 | /* all done, close the reparse point */ |
| 174 | 210 | CloseHandle(file); |
| 175 | 211 | } |
| 176 | 212 | } |
| 177 | 213 | |
| 178 | 214 | return rv; |
| 179 | 215 | } |
| 180 | 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 | +*/ | |
| 181 | 247 | int win32_symlink(const char *oldpath, const char *newpath){ |
| 182 | 248 | fossilStat stat; |
| 183 | 249 | int created = 0; |
| 184 | 250 | DWORD flags = 0; |
| 185 | 251 | wchar_t *zMbcs; |
| @@ -187,10 +253,12 @@ | ||
| 187 | 253 | /* does oldpath exist? is it a dir or a file? */ |
| 188 | 254 | zMbcs = fossil_utf8_to_filename(oldpath); |
| 189 | 255 | if (win32_stat(zMbcs, &stat) == 0){ |
| 190 | 256 | if (stat.st_mode == S_IFDIR) |
| 191 | 257 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 192 | - DeleteFile(newpath); | |
| 193 | - if (CreateSymbolicLink(newpath, oldpath, flags)) | |
| 194 | - created = 1; | |
| 195 | 258 | } |
| 196 | 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); | |
| 197 | 267 | |
| 198 | 268 | /* if the symlink was not created, create a plain text file */ |
| 199 | 269 | if (!created){ |
| 200 | 270 | Blob content; |
| 201 | 271 | blob_set(&content, oldpath); |
| @@ -202,13 +272,21 @@ | ||
| 202 | 272 | blob_write_to_file(&content, newpath); |
| 203 | 273 | blob_reset(&content); |
| 204 | 274 | created = 1; |
| 205 | 275 | } |
| 206 | 276 | |
| 207 | - return created ? 0 : -1; | |
| 277 | + return !created; | |
| 208 | 278 | } |
| 209 | 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 | +*/ | |
| 210 | 288 | int win32_symlinks_supported(){ |
| 211 | 289 | TOKEN_PRIVILEGES tp; |
| 212 | 290 | LUID luid; |
| 213 | 291 | HANDLE process, token; |
| 214 | 292 | DWORD status; |
| 215 | 293 |
| --- 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 @@ | ||
| 28 | 28 | |
| 29 | 29 | #ifndef LABEL_SECURITY_INFORMATION |
| 30 | 30 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 31 | 31 | #endif |
| 32 | 32 | |
| 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 | + | |
| 34 | 43 | typedef struct _REPARSE_DATA_BUFFER { |
| 35 | - ULONG ReparseTag; | |
| 44 | + ULONG ReparseTag; | |
| 36 | 45 | USHORT ReparseDataLength; |
| 37 | 46 | USHORT Reserved; |
| 38 | - union { | |
| 47 | + _ANONYMOUS_UNION union { | |
| 39 | 48 | struct { |
| 40 | 49 | USHORT SubstituteNameOffset; |
| 41 | 50 | USHORT SubstituteNameLength; |
| 42 | 51 | USHORT PrintNameOffset; |
| 43 | 52 | USHORT PrintNameLength; |
| 44 | - ULONG Flags; | |
| 45 | - WCHAR PathBuffer[1]; | |
| 53 | + ULONG Flags; | |
| 54 | + WCHAR PathBuffer[1]; | |
| 46 | 55 | } SymbolicLinkReparseBuffer; |
| 47 | 56 | struct { |
| 48 | 57 | USHORT SubstituteNameOffset; |
| 49 | 58 | USHORT SubstituteNameLength; |
| 50 | 59 | USHORT PrintNameOffset; |
| 51 | 60 | USHORT PrintNameLength; |
| 52 | - WCHAR PathBuffer[1]; | |
| 61 | + WCHAR PathBuffer[1]; | |
| 53 | 62 | } MountPointReparseBuffer; |
| 54 | 63 | struct { |
| 55 | 64 | UCHAR DataBuffer[1]; |
| 56 | 65 | } GenericReparseBuffer; |
| 57 | - }; | |
| 66 | + } DUMMYUNIONNAME; | |
| 58 | 67 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 59 | 68 | |
| 60 | 69 | #define LINK_BUFFER_SIZE 1024 |
| 61 | 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 | +*/ | |
| 62 | 77 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 63 | 78 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 64 | 79 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 65 | 80 | 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 | + | |
| 69 | 92 | ULARGE_INTEGER ull; |
| 70 | 93 | |
| 94 | + /* if a link was retrieved, it is a symlink, otherwise a dir or file */ | |
| 71 | 95 | buf->st_mode = (tlen > 0) ? S_IFLNK : |
| 72 | 96 | ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 73 | 97 | S_IFDIR : S_IFREG); |
| 74 | 98 | |
| 75 | 99 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| @@ -80,40 +104,47 @@ | ||
| 80 | 104 | } |
| 81 | 105 | return !rc; |
| 82 | 106 | } |
| 83 | 107 | |
| 84 | 108 | /* |
| 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. | |
| 88 | 117 | */ |
| 89 | 118 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 90 | 119 | int rc; |
| 91 | 120 | HANDLE file; |
| 92 | 121 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 93 | 122 | DWORD len; |
| 123 | + int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ | |
| 94 | 124 | |
| 95 | - while (1){ | |
| 125 | + while (iterationsRemaining-- > 0){ | |
| 96 | 126 | rc = win32_lstat(zFilename, buf); |
| 127 | + | |
| 97 | 128 | /* exit on error or not link */ |
| 98 | 129 | if ((rc != 0) || (buf->st_mode != S_IFLNK)) |
| 99 | 130 | break; |
| 100 | 131 | |
| 101 | 132 | /* it is a link, so open the linked file */ |
| 102 | 133 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); |
| 103 | 134 | if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ |
| 104 | - rc = -1; | |
| 135 | + rc = 1; | |
| 105 | 136 | break; |
| 106 | 137 | } |
| 107 | 138 | |
| 108 | 139 | /* get the final path name and close the handle */ |
| 109 | 140 | len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 110 | 141 | CloseHandle(file); |
| 111 | 142 | |
| 112 | 143 | /* if any problems getting the final path name error so exit */ |
| 113 | 144 | if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ |
| 114 | - rc = -1; | |
| 145 | + rc = 1; | |
| 115 | 146 | break; |
| 116 | 147 | } |
| 117 | 148 | |
| 118 | 149 | /* prepare to try again just in case we have a chain to follow */ |
| 119 | 150 | /* this shouldn't happen, but just trying to be safe */ |
| @@ -121,10 +152,15 @@ | ||
| 121 | 152 | } |
| 122 | 153 | |
| 123 | 154 | return rc; |
| 124 | 155 | } |
| 125 | 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 | +*/ | |
| 126 | 162 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 127 | 163 | /* assume we're going to fail */ |
| 128 | 164 | ssize_t rv = -1; |
| 129 | 165 | |
| 130 | 166 | /* does path reference a reparse point? */ |
| @@ -137,49 +173,79 @@ | ||
| 137 | 173 | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 138 | 174 | if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ |
| 139 | 175 | |
| 140 | 176 | /* use DeviceIoControl to get the reparse point data */ |
| 141 | 177 | |
| 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; | |
| 151 | 185 | |
| 152 | 186 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 153 | - &u, sizeof(u), &bytes, NULL); | |
| 187 | + data, data_size, &data_used, NULL); | |
| 154 | 188 | |
| 155 | 189 | /* did the reparse point data fit into the desired buffer? */ |
| 156 | - if (rc && (bytes < sizeof(u))){ | |
| 190 | + if (rc && (data_used < data_size)){ | |
| 157 | 191 | /* it fit, so setup the print name for further processing */ |
| 158 | 192 | 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); | |
| 161 | 195 | char *temp; |
| 162 | - u.data.SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 196 | + data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 163 | 197 | |
| 164 | 198 | /* 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); | |
| 166 | 200 | rv = strlen(temp); |
| 167 | 201 | if (rv >= bufsiz) |
| 168 | 202 | rv = bufsiz; |
| 169 | 203 | memcpy(buf, temp, rv); |
| 170 | 204 | fossil_filename_free(temp); |
| 171 | 205 | } |
| 206 | + | |
| 207 | + fossil_free(data); | |
| 172 | 208 | |
| 173 | 209 | /* all done, close the reparse point */ |
| 174 | 210 | CloseHandle(file); |
| 175 | 211 | } |
| 176 | 212 | } |
| 177 | 213 | |
| 178 | 214 | return rv; |
| 179 | 215 | } |
| 180 | 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 | +*/ | |
| 181 | 247 | int win32_symlink(const char *oldpath, const char *newpath){ |
| 182 | 248 | fossilStat stat; |
| 183 | 249 | int created = 0; |
| 184 | 250 | DWORD flags = 0; |
| 185 | 251 | wchar_t *zMbcs; |
| @@ -187,10 +253,12 @@ | ||
| 187 | 253 | /* does oldpath exist? is it a dir or a file? */ |
| 188 | 254 | zMbcs = fossil_utf8_to_filename(oldpath); |
| 189 | 255 | if (win32_stat(zMbcs, &stat) == 0){ |
| 190 | 256 | if (stat.st_mode == S_IFDIR) |
| 191 | 257 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 192 | - DeleteFile(newpath); | |
| 193 | - if (CreateSymbolicLink(newpath, oldpath, flags)) | |
| 194 | - created = 1; | |
| 195 | 258 | } |
| 196 | 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); | |
| 197 | 267 | |
| 198 | 268 | /* if the symlink was not created, create a plain text file */ |
| 199 | 269 | if (!created){ |
| 200 | 270 | Blob content; |
| 201 | 271 | blob_set(&content, oldpath); |
| @@ -202,13 +272,21 @@ | ||
| 202 | 272 | blob_write_to_file(&content, newpath); |
| 203 | 273 | blob_reset(&content); |
| 204 | 274 | created = 1; |
| 205 | 275 | } |
| 206 | 276 | |
| 207 | - return created ? 0 : -1; | |
| 277 | + return !created; | |
| 208 | 278 | } |
| 209 | 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 | +*/ | |
| 210 | 288 | int win32_symlinks_supported(){ |
| 211 | 289 | TOKEN_PRIVILEGES tp; |
| 212 | 290 | LUID luid; |
| 213 | 291 | HANDLE process, token; |
| 214 | 292 | DWORD status; |
| 215 | 293 |
| --- 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 @@ | ||
| 28 | 28 | |
| 29 | 29 | #ifndef LABEL_SECURITY_INFORMATION |
| 30 | 30 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 31 | 31 | #endif |
| 32 | 32 | |
| 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 | + | |
| 34 | 43 | typedef struct _REPARSE_DATA_BUFFER { |
| 35 | - ULONG ReparseTag; | |
| 44 | + ULONG ReparseTag; | |
| 36 | 45 | USHORT ReparseDataLength; |
| 37 | 46 | USHORT Reserved; |
| 38 | - union { | |
| 47 | + _ANONYMOUS_UNION union { | |
| 39 | 48 | struct { |
| 40 | 49 | USHORT SubstituteNameOffset; |
| 41 | 50 | USHORT SubstituteNameLength; |
| 42 | 51 | USHORT PrintNameOffset; |
| 43 | 52 | USHORT PrintNameLength; |
| 44 | - ULONG Flags; | |
| 45 | - WCHAR PathBuffer[1]; | |
| 53 | + ULONG Flags; | |
| 54 | + WCHAR PathBuffer[1]; | |
| 46 | 55 | } SymbolicLinkReparseBuffer; |
| 47 | 56 | struct { |
| 48 | 57 | USHORT SubstituteNameOffset; |
| 49 | 58 | USHORT SubstituteNameLength; |
| 50 | 59 | USHORT PrintNameOffset; |
| 51 | 60 | USHORT PrintNameLength; |
| 52 | - WCHAR PathBuffer[1]; | |
| 61 | + WCHAR PathBuffer[1]; | |
| 53 | 62 | } MountPointReparseBuffer; |
| 54 | 63 | struct { |
| 55 | 64 | UCHAR DataBuffer[1]; |
| 56 | 65 | } GenericReparseBuffer; |
| 57 | - }; | |
| 66 | + } DUMMYUNIONNAME; | |
| 58 | 67 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 59 | 68 | |
| 60 | 69 | #define LINK_BUFFER_SIZE 1024 |
| 61 | 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 | +*/ | |
| 62 | 77 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 63 | 78 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 64 | 79 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 65 | 80 | 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 | + | |
| 69 | 92 | ULARGE_INTEGER ull; |
| 70 | 93 | |
| 94 | + /* if a link was retrieved, it is a symlink, otherwise a dir or file */ | |
| 71 | 95 | buf->st_mode = (tlen > 0) ? S_IFLNK : |
| 72 | 96 | ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 73 | 97 | S_IFDIR : S_IFREG); |
| 74 | 98 | |
| 75 | 99 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| @@ -80,40 +104,47 @@ | ||
| 80 | 104 | } |
| 81 | 105 | return !rc; |
| 82 | 106 | } |
| 83 | 107 | |
| 84 | 108 | /* |
| 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. | |
| 88 | 117 | */ |
| 89 | 118 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 90 | 119 | int rc; |
| 91 | 120 | HANDLE file; |
| 92 | 121 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 93 | 122 | DWORD len; |
| 123 | + int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ | |
| 94 | 124 | |
| 95 | - while (1){ | |
| 125 | + while (iterationsRemaining-- > 0){ | |
| 96 | 126 | rc = win32_lstat(zFilename, buf); |
| 127 | + | |
| 97 | 128 | /* exit on error or not link */ |
| 98 | 129 | if ((rc != 0) || (buf->st_mode != S_IFLNK)) |
| 99 | 130 | break; |
| 100 | 131 | |
| 101 | 132 | /* it is a link, so open the linked file */ |
| 102 | 133 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); |
| 103 | 134 | if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ |
| 104 | - rc = -1; | |
| 135 | + rc = 1; | |
| 105 | 136 | break; |
| 106 | 137 | } |
| 107 | 138 | |
| 108 | 139 | /* get the final path name and close the handle */ |
| 109 | 140 | len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 110 | 141 | CloseHandle(file); |
| 111 | 142 | |
| 112 | 143 | /* if any problems getting the final path name error so exit */ |
| 113 | 144 | if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ |
| 114 | - rc = -1; | |
| 145 | + rc = 1; | |
| 115 | 146 | break; |
| 116 | 147 | } |
| 117 | 148 | |
| 118 | 149 | /* prepare to try again just in case we have a chain to follow */ |
| 119 | 150 | /* this shouldn't happen, but just trying to be safe */ |
| @@ -121,10 +152,15 @@ | ||
| 121 | 152 | } |
| 122 | 153 | |
| 123 | 154 | return rc; |
| 124 | 155 | } |
| 125 | 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 | +*/ | |
| 126 | 162 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 127 | 163 | /* assume we're going to fail */ |
| 128 | 164 | ssize_t rv = -1; |
| 129 | 165 | |
| 130 | 166 | /* does path reference a reparse point? */ |
| @@ -137,49 +173,79 @@ | ||
| 137 | 173 | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 138 | 174 | if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ |
| 139 | 175 | |
| 140 | 176 | /* use DeviceIoControl to get the reparse point data */ |
| 141 | 177 | |
| 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; | |
| 151 | 185 | |
| 152 | 186 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 153 | - &u, sizeof(u), &bytes, NULL); | |
| 187 | + data, data_size, &data_used, NULL); | |
| 154 | 188 | |
| 155 | 189 | /* did the reparse point data fit into the desired buffer? */ |
| 156 | - if (rc && (bytes < sizeof(u))){ | |
| 190 | + if (rc && (data_used < data_size)){ | |
| 157 | 191 | /* it fit, so setup the print name for further processing */ |
| 158 | 192 | 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); | |
| 161 | 195 | char *temp; |
| 162 | - u.data.SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 196 | + data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 163 | 197 | |
| 164 | 198 | /* 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); | |
| 166 | 200 | rv = strlen(temp); |
| 167 | 201 | if (rv >= bufsiz) |
| 168 | 202 | rv = bufsiz; |
| 169 | 203 | memcpy(buf, temp, rv); |
| 170 | 204 | fossil_filename_free(temp); |
| 171 | 205 | } |
| 206 | + | |
| 207 | + fossil_free(data); | |
| 172 | 208 | |
| 173 | 209 | /* all done, close the reparse point */ |
| 174 | 210 | CloseHandle(file); |
| 175 | 211 | } |
| 176 | 212 | } |
| 177 | 213 | |
| 178 | 214 | return rv; |
| 179 | 215 | } |
| 180 | 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 | +*/ | |
| 181 | 247 | int win32_symlink(const char *oldpath, const char *newpath){ |
| 182 | 248 | fossilStat stat; |
| 183 | 249 | int created = 0; |
| 184 | 250 | DWORD flags = 0; |
| 185 | 251 | wchar_t *zMbcs; |
| @@ -187,10 +253,12 @@ | ||
| 187 | 253 | /* does oldpath exist? is it a dir or a file? */ |
| 188 | 254 | zMbcs = fossil_utf8_to_filename(oldpath); |
| 189 | 255 | if (win32_stat(zMbcs, &stat) == 0){ |
| 190 | 256 | if (stat.st_mode == S_IFDIR) |
| 191 | 257 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 192 | - DeleteFile(newpath); | |
| 193 | - if (CreateSymbolicLink(newpath, oldpath, flags)) | |
| 194 | - created = 1; | |
| 195 | 258 | } |
| 196 | 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); | |
| 197 | 267 | |
| 198 | 268 | /* if the symlink was not created, create a plain text file */ |
| 199 | 269 | if (!created){ |
| 200 | 270 | Blob content; |
| 201 | 271 | blob_set(&content, oldpath); |
| @@ -202,13 +272,21 @@ | ||
| 202 | 272 | blob_write_to_file(&content, newpath); |
| 203 | 273 | blob_reset(&content); |
| 204 | 274 | created = 1; |
| 205 | 275 | } |
| 206 | 276 | |
| 207 | - return created ? 0 : -1; | |
| 277 | + return !created; | |
| 208 | 278 | } |
| 209 | 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 | +*/ | |
| 210 | 288 | int win32_symlinks_supported(){ |
| 211 | 289 | TOKEN_PRIVILEGES tp; |
| 212 | 290 | LUID luid; |
| 213 | 291 | HANDLE process, token; |
| 214 | 292 | DWORD status; |
| 215 | 293 |
| --- 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 |