Fossil SCM
Merge trunk. Style changes. Implement fallback in case of Windows XP.
Commit
f2ba1d223373bd51ab888b7c2d58df25f3252ed6
Parent
631dff61e0e8e18…
7 files changed
+24
-3
+13
-1
+61
-61
+85
-61
+85
-61
+24
-3
+2
-2
+24
-3
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1171,10 +1171,13 @@ | ||
| 1171 | 1171 | # Uncomment to enable miniz usage |
| 1172 | 1172 | # FOSSIL_ENABLE_MINIZ = 1 |
| 1173 | 1173 | |
| 1174 | 1174 | # Uncomment to enable SSL support |
| 1175 | 1175 | # FOSSIL_ENABLE_SSL = 1 |
| 1176 | + | |
| 1177 | +# Uncomment to build SSL libraries | |
| 1178 | +# FOSSIL_BUILD_SSL = 1 | |
| 1176 | 1179 | |
| 1177 | 1180 | # Uncomment to enable TH1 scripts in embedded documentation files |
| 1178 | 1181 | # FOSSIL_ENABLE_TH1_DOCS = 1 |
| 1179 | 1182 | |
| 1180 | 1183 | # Uncomment to enable TH1 hooks |
| @@ -1186,10 +1189,26 @@ | ||
| 1186 | 1189 | !ifdef FOSSIL_ENABLE_SSL |
| 1187 | 1190 | SSLDIR = $(B)\compat\openssl-1.0.1i |
| 1188 | 1191 | SSLINCDIR = $(SSLDIR)\inc32 |
| 1189 | 1192 | SSLLIBDIR = $(SSLDIR)\out32 |
| 1190 | 1193 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 1194 | +!if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" | |
| 1195 | +!message Using 'x64' platform for OpenSSL... | |
| 1196 | +SSLCONFIG = VC-WIN64A no-asm | |
| 1197 | +SSLSETUP = ms\do_win64a.bat | |
| 1198 | +SSLNMAKE = ms\nt.mak all | |
| 1199 | +!elseif "$(PLATFORM)"=="ia64" | |
| 1200 | +!message Using 'ia64' platform for OpenSSL... | |
| 1201 | +SSLCONFIG = VC-WIN64I no-asm | |
| 1202 | +SSLSETUP = ms\do_win64i.bat | |
| 1203 | +SSLNMAKE = ms\nt.mak all | |
| 1204 | +!else | |
| 1205 | +!message Assuming 'x86' platform for OpenSSL... | |
| 1206 | +SSLCONFIG = VC-WIN32 no-asm | |
| 1207 | +SSLSETUP = ms\do_ms.bat | |
| 1208 | +SSLNMAKE = ms\nt.mak all | |
| 1209 | +!endif | |
| 1191 | 1210 | !endif |
| 1192 | 1211 | |
| 1193 | 1212 | !ifdef FOSSIL_ENABLE_TCL |
| 1194 | 1213 | TCLDIR = $(B)\compat\tcl-8.6 |
| 1195 | 1214 | TCLSRCDIR = $(TCLDIR) |
| @@ -1329,21 +1348,23 @@ | ||
| 1329 | 1348 | openssl: |
| 1330 | 1349 | @echo Building OpenSSL from "$(SSLDIR)"... |
| 1331 | 1350 | !if "$(PERLDIR)" != "" |
| 1332 | 1351 | @set PATH=$(PERLDIR);$(PATH) |
| 1333 | 1352 | !endif |
| 1334 | - @pushd "$(SSLDIR)" && $(PERL) Configure VC-WIN32 no-asm && popd | |
| 1335 | - @pushd "$(SSLDIR)" && call ms\do_ms.bat && popd | |
| 1336 | - @pushd "$(SSLDIR)" && $(MAKE) /f ms\nt.mak all && popd | |
| 1353 | + @pushd "$(SSLDIR)" && $(PERL) Configure $(SSLCONFIG) && popd | |
| 1354 | + @pushd "$(SSLDIR)" && call $(SSLSETUP) && popd | |
| 1355 | + @pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) && popd | |
| 1337 | 1356 | !endif |
| 1338 | 1357 | |
| 1339 | 1358 | !ifndef FOSSIL_ENABLE_MINIZ |
| 1340 | 1359 | APPTARGETS = $(APPTARGETS) zlib |
| 1341 | 1360 | !endif |
| 1342 | 1361 | |
| 1343 | 1362 | !ifdef FOSSIL_ENABLE_SSL |
| 1363 | +!ifdef FOSSIL_BUILD_SSL | |
| 1344 | 1364 | APPTARGETS = $(APPTARGETS) openssl |
| 1365 | +!endif | |
| 1345 | 1366 | !endif |
| 1346 | 1367 | |
| 1347 | 1368 | $(APPNAME) : $(APPTARGETS) translate$E mkindex$E headers $(OBJ) $(OX)\linkopts |
| 1348 | 1369 | cd $(OX) |
| 1349 | 1370 | link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 1350 | 1371 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -1171,10 +1171,13 @@ | |
| 1171 | # Uncomment to enable miniz usage |
| 1172 | # FOSSIL_ENABLE_MINIZ = 1 |
| 1173 | |
| 1174 | # Uncomment to enable SSL support |
| 1175 | # FOSSIL_ENABLE_SSL = 1 |
| 1176 | |
| 1177 | # Uncomment to enable TH1 scripts in embedded documentation files |
| 1178 | # FOSSIL_ENABLE_TH1_DOCS = 1 |
| 1179 | |
| 1180 | # Uncomment to enable TH1 hooks |
| @@ -1186,10 +1189,26 @@ | |
| 1186 | !ifdef FOSSIL_ENABLE_SSL |
| 1187 | SSLDIR = $(B)\compat\openssl-1.0.1i |
| 1188 | SSLINCDIR = $(SSLDIR)\inc32 |
| 1189 | SSLLIBDIR = $(SSLDIR)\out32 |
| 1190 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 1191 | !endif |
| 1192 | |
| 1193 | !ifdef FOSSIL_ENABLE_TCL |
| 1194 | TCLDIR = $(B)\compat\tcl-8.6 |
| 1195 | TCLSRCDIR = $(TCLDIR) |
| @@ -1329,21 +1348,23 @@ | |
| 1329 | openssl: |
| 1330 | @echo Building OpenSSL from "$(SSLDIR)"... |
| 1331 | !if "$(PERLDIR)" != "" |
| 1332 | @set PATH=$(PERLDIR);$(PATH) |
| 1333 | !endif |
| 1334 | @pushd "$(SSLDIR)" && $(PERL) Configure VC-WIN32 no-asm && popd |
| 1335 | @pushd "$(SSLDIR)" && call ms\do_ms.bat && popd |
| 1336 | @pushd "$(SSLDIR)" && $(MAKE) /f ms\nt.mak all && popd |
| 1337 | !endif |
| 1338 | |
| 1339 | !ifndef FOSSIL_ENABLE_MINIZ |
| 1340 | APPTARGETS = $(APPTARGETS) zlib |
| 1341 | !endif |
| 1342 | |
| 1343 | !ifdef FOSSIL_ENABLE_SSL |
| 1344 | APPTARGETS = $(APPTARGETS) openssl |
| 1345 | !endif |
| 1346 | |
| 1347 | $(APPNAME) : $(APPTARGETS) translate$E mkindex$E headers $(OBJ) $(OX)\linkopts |
| 1348 | cd $(OX) |
| 1349 | link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 1350 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -1171,10 +1171,13 @@ | |
| 1171 | # Uncomment to enable miniz usage |
| 1172 | # FOSSIL_ENABLE_MINIZ = 1 |
| 1173 | |
| 1174 | # Uncomment to enable SSL support |
| 1175 | # FOSSIL_ENABLE_SSL = 1 |
| 1176 | |
| 1177 | # Uncomment to build SSL libraries |
| 1178 | # FOSSIL_BUILD_SSL = 1 |
| 1179 | |
| 1180 | # Uncomment to enable TH1 scripts in embedded documentation files |
| 1181 | # FOSSIL_ENABLE_TH1_DOCS = 1 |
| 1182 | |
| 1183 | # Uncomment to enable TH1 hooks |
| @@ -1186,10 +1189,26 @@ | |
| 1189 | !ifdef FOSSIL_ENABLE_SSL |
| 1190 | SSLDIR = $(B)\compat\openssl-1.0.1i |
| 1191 | SSLINCDIR = $(SSLDIR)\inc32 |
| 1192 | SSLLIBDIR = $(SSLDIR)\out32 |
| 1193 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 1194 | !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" |
| 1195 | !message Using 'x64' platform for OpenSSL... |
| 1196 | SSLCONFIG = VC-WIN64A no-asm |
| 1197 | SSLSETUP = ms\do_win64a.bat |
| 1198 | SSLNMAKE = ms\nt.mak all |
| 1199 | !elseif "$(PLATFORM)"=="ia64" |
| 1200 | !message Using 'ia64' platform for OpenSSL... |
| 1201 | SSLCONFIG = VC-WIN64I no-asm |
| 1202 | SSLSETUP = ms\do_win64i.bat |
| 1203 | SSLNMAKE = ms\nt.mak all |
| 1204 | !else |
| 1205 | !message Assuming 'x86' platform for OpenSSL... |
| 1206 | SSLCONFIG = VC-WIN32 no-asm |
| 1207 | SSLSETUP = ms\do_ms.bat |
| 1208 | SSLNMAKE = ms\nt.mak all |
| 1209 | !endif |
| 1210 | !endif |
| 1211 | |
| 1212 | !ifdef FOSSIL_ENABLE_TCL |
| 1213 | TCLDIR = $(B)\compat\tcl-8.6 |
| 1214 | TCLSRCDIR = $(TCLDIR) |
| @@ -1329,21 +1348,23 @@ | |
| 1348 | openssl: |
| 1349 | @echo Building OpenSSL from "$(SSLDIR)"... |
| 1350 | !if "$(PERLDIR)" != "" |
| 1351 | @set PATH=$(PERLDIR);$(PATH) |
| 1352 | !endif |
| 1353 | @pushd "$(SSLDIR)" && $(PERL) Configure $(SSLCONFIG) && popd |
| 1354 | @pushd "$(SSLDIR)" && call $(SSLSETUP) && popd |
| 1355 | @pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) && popd |
| 1356 | !endif |
| 1357 | |
| 1358 | !ifndef FOSSIL_ENABLE_MINIZ |
| 1359 | APPTARGETS = $(APPTARGETS) zlib |
| 1360 | !endif |
| 1361 | |
| 1362 | !ifdef FOSSIL_ENABLE_SSL |
| 1363 | !ifdef FOSSIL_BUILD_SSL |
| 1364 | APPTARGETS = $(APPTARGETS) openssl |
| 1365 | !endif |
| 1366 | !endif |
| 1367 | |
| 1368 | $(APPNAME) : $(APPTARGETS) translate$E mkindex$E headers $(OBJ) $(OX)\linkopts |
| 1369 | cd $(OX) |
| 1370 | link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 1371 |
+13
-1
| --- src/markdown_html.c | ||
| +++ src/markdown_html.c | ||
| @@ -82,10 +82,20 @@ | ||
| 82 | 82 | } |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | 85 | |
| 86 | 86 | /* HTML block tags */ |
| 87 | + | |
| 88 | +static void html_prolog(struct Blob *ob, void *opaque){ | |
| 89 | + INTER_BLOCK(ob); | |
| 90 | + BLOB_APPEND_LITTERAL(ob, "<div class=\"markdown\">\n"); | |
| 91 | +} | |
| 92 | + | |
| 93 | +static void html_epilog(struct Blob *ob, void *opaque){ | |
| 94 | + INTER_BLOCK(ob); | |
| 95 | + BLOB_APPEND_LITTERAL(ob, "</div>\n"); | |
| 96 | +} | |
| 87 | 97 | |
| 88 | 98 | static void html_raw_block(struct Blob *ob, struct Blob *text, void *opaque){ |
| 89 | 99 | char *data = blob_buffer(text); |
| 90 | 100 | size_t first = 0, size = blob_size(text); |
| 91 | 101 | INTER_BLOCK(ob); |
| @@ -364,11 +374,13 @@ | ||
| 364 | 374 | struct Blob *input_markdown, |
| 365 | 375 | struct Blob *output_title, |
| 366 | 376 | struct Blob *output_body |
| 367 | 377 | ){ |
| 368 | 378 | struct mkd_renderer html_renderer = { |
| 369 | - 0, 0, /* no prolog or epilog */ | |
| 379 | + /* prolog and epilog */ | |
| 380 | + html_prolog, | |
| 381 | + html_epilog, | |
| 370 | 382 | |
| 371 | 383 | /* block level elements */ |
| 372 | 384 | html_blockcode, |
| 373 | 385 | html_blockquote, |
| 374 | 386 | html_raw_block, |
| 375 | 387 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -82,10 +82,20 @@ | |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | |
| 86 | /* HTML block tags */ |
| 87 | |
| 88 | static void html_raw_block(struct Blob *ob, struct Blob *text, void *opaque){ |
| 89 | char *data = blob_buffer(text); |
| 90 | size_t first = 0, size = blob_size(text); |
| 91 | INTER_BLOCK(ob); |
| @@ -364,11 +374,13 @@ | |
| 364 | struct Blob *input_markdown, |
| 365 | struct Blob *output_title, |
| 366 | struct Blob *output_body |
| 367 | ){ |
| 368 | struct mkd_renderer html_renderer = { |
| 369 | 0, 0, /* no prolog or epilog */ |
| 370 | |
| 371 | /* block level elements */ |
| 372 | html_blockcode, |
| 373 | html_blockquote, |
| 374 | html_raw_block, |
| 375 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -82,10 +82,20 @@ | |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | |
| 86 | /* HTML block tags */ |
| 87 | |
| 88 | static void html_prolog(struct Blob *ob, void *opaque){ |
| 89 | INTER_BLOCK(ob); |
| 90 | BLOB_APPEND_LITTERAL(ob, "<div class=\"markdown\">\n"); |
| 91 | } |
| 92 | |
| 93 | static void html_epilog(struct Blob *ob, void *opaque){ |
| 94 | INTER_BLOCK(ob); |
| 95 | BLOB_APPEND_LITTERAL(ob, "</div>\n"); |
| 96 | } |
| 97 | |
| 98 | static void html_raw_block(struct Blob *ob, struct Blob *text, void *opaque){ |
| 99 | char *data = blob_buffer(text); |
| 100 | size_t first = 0, size = blob_size(text); |
| 101 | INTER_BLOCK(ob); |
| @@ -364,11 +374,13 @@ | |
| 374 | struct Blob *input_markdown, |
| 375 | struct Blob *output_title, |
| 376 | struct Blob *output_body |
| 377 | ){ |
| 378 | struct mkd_renderer html_renderer = { |
| 379 | /* prolog and epilog */ |
| 380 | html_prolog, |
| 381 | html_epilog, |
| 382 | |
| 383 | /* block level elements */ |
| 384 | html_blockcode, |
| 385 | html_blockquote, |
| 386 | html_raw_block, |
| 387 |
+61
-61
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -441,38 +441,38 @@ | ||
| 441 | 441 | if( fossil_strcmp(zLogin, "developer") ){ |
| 442 | 442 | char *z1, *z2; |
| 443 | 443 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'"); |
| 444 | 444 | while( z1 && *z1 ){ |
| 445 | 445 | inherit[0x7f & *(z1++)] = |
| 446 | - "<span class=\"ueditInheritDeveloper\">•</span>"; | |
| 446 | + "<span class=\"ueditInheritDeveloper\"><sub>D</sub></span>"; | |
| 447 | 447 | } |
| 448 | 448 | free(z2); |
| 449 | 449 | } |
| 450 | 450 | if( fossil_strcmp(zLogin, "reader") ){ |
| 451 | 451 | char *z1, *z2; |
| 452 | 452 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'"); |
| 453 | 453 | while( z1 && *z1 ){ |
| 454 | 454 | inherit[0x7f & *(z1++)] = |
| 455 | - "<span class=\"ueditInheritReader\">•</span>"; | |
| 455 | + "<span class=\"ueditInheritReader\"><sub>R</sub></span>"; | |
| 456 | 456 | } |
| 457 | 457 | free(z2); |
| 458 | 458 | } |
| 459 | 459 | if( fossil_strcmp(zLogin, "anonymous") ){ |
| 460 | 460 | char *z1, *z2; |
| 461 | 461 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'"); |
| 462 | 462 | while( z1 && *z1 ){ |
| 463 | 463 | inherit[0x7f & *(z1++)] = |
| 464 | - "<span class=\"ueditInheritAnonymous\">•</span>"; | |
| 464 | + "<span class=\"ueditInheritAnonymous\"><sub>A</sub></span>"; | |
| 465 | 465 | } |
| 466 | 466 | free(z2); |
| 467 | 467 | } |
| 468 | 468 | if( fossil_strcmp(zLogin, "nobody") ){ |
| 469 | 469 | char *z1, *z2; |
| 470 | 470 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'"); |
| 471 | 471 | while( z1 && *z1 ){ |
| 472 | 472 | inherit[0x7f & *(z1++)] = |
| 473 | - "<span class=\"ueditInheritNobody\">•</span>"; | |
| 473 | + "<span class=\"ueditInheritNobody\"><sub>N</sub></span>"; | |
| 474 | 474 | } |
| 475 | 475 | free(z2); |
| 476 | 476 | } |
| 477 | 477 | |
| 478 | 478 | /* Begin generating the page |
| @@ -516,63 +516,63 @@ | ||
| 516 | 516 | @ <td class="usetupEditLabel">Capabilities:</td> |
| 517 | 517 | @ <td> |
| 518 | 518 | #define B(x) inherit[x] |
| 519 | 519 | @ <table border=0><tr><td valign="top"> |
| 520 | 520 | if( g.perm.Setup ){ |
| 521 | - @ <label><input type="checkbox" name="as"%s(oa['s']) />%s(B('s'))Setup | |
| 522 | - @ </label><br /> | |
| 523 | - } | |
| 524 | - @ <label><input type="checkbox" name="aa"%s(oa['a']) />%s(B('a'))Admin | |
| 525 | - @ </label><br /> | |
| 526 | - @ <label><input type="checkbox" name="ad"%s(oa['d']) />%s(B('d'))Delete | |
| 527 | - @ </label><br /> | |
| 528 | - @ <label><input type="checkbox" name="ae"%s(oa['e']) />%s(B('e'))Email | |
| 529 | - @ </label><br /> | |
| 530 | - @ <label><input type="checkbox" name="ap"%s(oa['p']) />%s(B('p'))Password | |
| 531 | - @ </label><br /> | |
| 532 | - @ <label><input type="checkbox" name="ai"%s(oa['i']) />%s(B('i'))Check-In | |
| 533 | - @ </label><br /> | |
| 534 | - @ <label><input type="checkbox" name="ao"%s(oa['o']) />%s(B('o'))Check-Out | |
| 535 | - @ </label><br /> | |
| 536 | - @ <label><input type="checkbox" name="ah"%s(oa['h']) />%s(B('h'))Hyperlinks | |
| 537 | - @ </label><br /> | |
| 538 | - @ <label><input type="checkbox" name="ab"%s(oa['b']) />%s(B('b'))Attachments | |
| 539 | - @ </label><br /> | |
| 540 | - @ </td><td><td width="40"></td><td valign="top"> | |
| 541 | - @ <label><input type="checkbox" name="au"%s(oa['u']) />%s(B('u'))Reader | |
| 542 | - @ </label><br /> | |
| 543 | - @ <label><input type="checkbox" name="av"%s(oa['v']) />%s(B('v'))Developer | |
| 544 | - @ </label><br /> | |
| 545 | - @ <label><input type="checkbox" name="ag"%s(oa['g']) />%s(B('g'))Clone | |
| 546 | - @ </label><br /> | |
| 547 | - @ <label><input type="checkbox" name="aj"%s(oa['j']) />%s(B('j'))Read Wiki | |
| 548 | - @ </label><br /> | |
| 549 | - @ <label><input type="checkbox" name="af"%s(oa['f']) />%s(B('f'))New Wiki | |
| 550 | - @ </label><br /> | |
| 551 | - @ <label><input type="checkbox" name="am"%s(oa['m']) />%s(B('m'))Append Wiki | |
| 552 | - @ </label><br /> | |
| 553 | - @ <label><input type="checkbox" name="ak"%s(oa['k']) />%s(B('k'))Write Wiki | |
| 554 | - @ </label><br /> | |
| 555 | - @ <label><input type="checkbox" name="al"%s(oa['l']) />%s(B('l'))Moderate | |
| 556 | - @ Wiki</label><br /> | |
| 557 | - @ </td><td><td width="40"></td><td valign="top"> | |
| 558 | - @ <label><input type="checkbox" name="ar"%s(oa['r']) />%s(B('r'))Read Ticket | |
| 559 | - @ </label><br /> | |
| 560 | - @ <label><input type="checkbox" name="an"%s(oa['n']) />%s(B('n'))New Tickets | |
| 561 | - @ </label><br /> | |
| 562 | - @ <label><input type="checkbox" name="ac"%s(oa['c']) />%s(B('c'))Append | |
| 563 | - @ To Ticket </label><br /> | |
| 564 | - @ <label><input type="checkbox" name="aw"%s(oa['w']) />%s(B('w'))Write | |
| 565 | - @ Tickets </label><br /> | |
| 566 | - @ <label><input type="checkbox" name="aq"%s(oa['q']) />%s(B('q'))Moderate | |
| 567 | - @ Tickets </label><br /> | |
| 568 | - @ <label><input type="checkbox" name="at"%s(oa['t']) />%s(B('t'))Ticket | |
| 569 | - @ Report </label><br /> | |
| 570 | - @ <label><input type="checkbox" name="ax"%s(oa['x']) />%s(B('x'))Private | |
| 571 | - @ </label><br /> | |
| 572 | - @ <label><input type="checkbox" name="az"%s(oa['z']) />%s(B('z'))Download | |
| 573 | - @ Zip </label> | |
| 521 | + @ <label><input type="checkbox" name="as"%s(oa['s']) /> | |
| 522 | + @ Setup%s(B('s'))</label><br /> | |
| 523 | + } | |
| 524 | + @ <label><input type="checkbox" name="aa"%s(oa['a']) /> | |
| 525 | + @ Admin%s(B('a'))</label><br /> | |
| 526 | + @ <label><input type="checkbox" name="ad"%s(oa['d']) /> | |
| 527 | + @ Delete%s(B('d'))</label><br /> | |
| 528 | + @ <label><input type="checkbox" name="ae"%s(oa['e']) /> | |
| 529 | + @ Email%s(B('e'))</label><br /> | |
| 530 | + @ <label><input type="checkbox" name="ap"%s(oa['p']) /> | |
| 531 | + @ Password%s(B('p'))</label><br /> | |
| 532 | + @ <label><input type="checkbox" name="ai"%s(oa['i']) /> | |
| 533 | + @ Check-In%s(B('i'))</label><br /> | |
| 534 | + @ <label><input type="checkbox" name="ao"%s(oa['o']) /> | |
| 535 | + @ Check-Out%s(B('o'))</label><br /> | |
| 536 | + @ <label><input type="checkbox" name="ah"%s(oa['h']) /> | |
| 537 | + @ Hyperlinks%s(B('h'))</label><br /> | |
| 538 | + @ <label><input type="checkbox" name="ab"%s(oa['b']) /> | |
| 539 | + @ Attachments%s(B('b'))</label><br /> | |
| 540 | + @ </td><td><td width="40"></td><td valign="top"> | |
| 541 | + @ <label><input type="checkbox" name="au"%s(oa['u']) /> | |
| 542 | + @ Reader%s(B('u'))</label><br /> | |
| 543 | + @ <label><input type="checkbox" name="av"%s(oa['v']) /> | |
| 544 | + @ Developer%s(B('v'))</label><br /> | |
| 545 | + @ <label><input type="checkbox" name="ag"%s(oa['g']) /> | |
| 546 | + @ Clone%s(B('g'))</label><br /> | |
| 547 | + @ <label><input type="checkbox" name="aj"%s(oa['j']) /> | |
| 548 | + @ Read Wiki%s(B('j'))</label><br /> | |
| 549 | + @ <label><input type="checkbox" name="af"%s(oa['f']) /> | |
| 550 | + @ New Wiki%s(B('f'))</label><br /> | |
| 551 | + @ <label><input type="checkbox" name="am"%s(oa['m']) /> | |
| 552 | + @ Append Wiki%s(B('m'))</label><br /> | |
| 553 | + @ <label><input type="checkbox" name="ak"%s(oa['k']) /> | |
| 554 | + @ Write Wiki%s(B('k'))</label><br /> | |
| 555 | + @ <label><input type="checkbox" name="al"%s(oa['l']) /> | |
| 556 | + @ Moderate Wiki%s(B('l'))</label><br /> | |
| 557 | + @ </td><td><td width="40"></td><td valign="top"> | |
| 558 | + @ <label><input type="checkbox" name="ar"%s(oa['r']) /> | |
| 559 | + @ Read Ticket%s(B('r'))</label><br /> | |
| 560 | + @ <label><input type="checkbox" name="an"%s(oa['n']) /> | |
| 561 | + @ New Tickets%s(B('n'))</label><br /> | |
| 562 | + @ <label><input type="checkbox" name="ac"%s(oa['c']) /> | |
| 563 | + @ Append To Ticket%s(B('c'))</label><br /> | |
| 564 | + @ <label><input type="checkbox" name="aw"%s(oa['w']) /> | |
| 565 | + @ Write Tickets%s(B('w'))</label><br /> | |
| 566 | + @ <label><input type="checkbox" name="aq"%s(oa['q']) /> | |
| 567 | + @ Moderate Tickets%s(B('q'))</label><br /> | |
| 568 | + @ <label><input type="checkbox" name="at"%s(oa['t']) /> | |
| 569 | + @ Ticket Report%s(B('t'))</label><br /> | |
| 570 | + @ <label><input type="checkbox" name="ax"%s(oa['x']) /> | |
| 571 | + @ Private%s(B('x'))</label><br /> | |
| 572 | + @ <label><input type="checkbox" name="az"%s(oa['z']) /> | |
| 573 | + @ Download Zip%s(B('z'))</label> | |
| 574 | 574 | @ </td></tr></table> |
| 575 | 575 | @ </td> |
| 576 | 576 | @ </tr> |
| 577 | 577 | if( !login_is_special(zLogin) ){ |
| 578 | 578 | @ <tr> |
| @@ -622,30 +622,30 @@ | ||
| 622 | 622 | @ and reset user passwords. Both automatically get all other privileges |
| 623 | 623 | @ listed below. Use these two settings with discretion. |
| 624 | 624 | @ </p></li> |
| 625 | 625 | @ |
| 626 | 626 | @ <li><p> |
| 627 | - @ The "<span class="ueditInheritNobody"><big>•</big></span>" mark | |
| 627 | + @ The "<span class="ueditInheritNobody"><sub>N</sub></span>" subscript suffix | |
| 628 | 628 | @ indicates the privileges of <span class="usertype">nobody</span> that |
| 629 | 629 | @ are available to all users regardless of whether or not they are logged in. |
| 630 | 630 | @ </p></li> |
| 631 | 631 | @ |
| 632 | 632 | @ <li><p> |
| 633 | - @ The "<span class="ueditInheritAnonymous"><big>•</big></span>" mark | |
| 633 | + @ The "<span class="ueditInheritAnonymous"><sub>A</sub></span>" subscript suffix | |
| 634 | 634 | @ indicates the privileges of <span class="usertype">anonymous</span> that |
| 635 | 635 | @ are inherited by all logged-in users. |
| 636 | 636 | @ </p></li> |
| 637 | 637 | @ |
| 638 | 638 | @ <li><p> |
| 639 | - @ The "<span class="ueditInheritDeveloper"><big>•</big></span>" mark | |
| 639 | + @ The "<span class="ueditInheritDeveloper"><sub>D</sub></span>" subscript suffix | |
| 640 | 640 | @ indicates the privileges of <span class="usertype">developer</span> that |
| 641 | 641 | @ are inherited by all users with the |
| 642 | 642 | @ <span class="capability">Developer</span> privilege. |
| 643 | 643 | @ </p></li> |
| 644 | 644 | @ |
| 645 | 645 | @ <li><p> |
| 646 | - @ The "<span class="ueditInheritReader"><big>•</big></span>" mark | |
| 646 | + @ The "<span class="ueditInheritReader"><sub>R</sub></span>" subscript suffix | |
| 647 | 647 | @ indicates the privileges of <span class="usertype">reader</span> that |
| 648 | 648 | @ are inherited by all users with the <span class="capability">Reader</span> |
| 649 | 649 | @ privilege. |
| 650 | 650 | @ </p></li> |
| 651 | 651 | @ |
| 652 | 652 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -441,38 +441,38 @@ | |
| 441 | if( fossil_strcmp(zLogin, "developer") ){ |
| 442 | char *z1, *z2; |
| 443 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'"); |
| 444 | while( z1 && *z1 ){ |
| 445 | inherit[0x7f & *(z1++)] = |
| 446 | "<span class=\"ueditInheritDeveloper\">•</span>"; |
| 447 | } |
| 448 | free(z2); |
| 449 | } |
| 450 | if( fossil_strcmp(zLogin, "reader") ){ |
| 451 | char *z1, *z2; |
| 452 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'"); |
| 453 | while( z1 && *z1 ){ |
| 454 | inherit[0x7f & *(z1++)] = |
| 455 | "<span class=\"ueditInheritReader\">•</span>"; |
| 456 | } |
| 457 | free(z2); |
| 458 | } |
| 459 | if( fossil_strcmp(zLogin, "anonymous") ){ |
| 460 | char *z1, *z2; |
| 461 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'"); |
| 462 | while( z1 && *z1 ){ |
| 463 | inherit[0x7f & *(z1++)] = |
| 464 | "<span class=\"ueditInheritAnonymous\">•</span>"; |
| 465 | } |
| 466 | free(z2); |
| 467 | } |
| 468 | if( fossil_strcmp(zLogin, "nobody") ){ |
| 469 | char *z1, *z2; |
| 470 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'"); |
| 471 | while( z1 && *z1 ){ |
| 472 | inherit[0x7f & *(z1++)] = |
| 473 | "<span class=\"ueditInheritNobody\">•</span>"; |
| 474 | } |
| 475 | free(z2); |
| 476 | } |
| 477 | |
| 478 | /* Begin generating the page |
| @@ -516,63 +516,63 @@ | |
| 516 | @ <td class="usetupEditLabel">Capabilities:</td> |
| 517 | @ <td> |
| 518 | #define B(x) inherit[x] |
| 519 | @ <table border=0><tr><td valign="top"> |
| 520 | if( g.perm.Setup ){ |
| 521 | @ <label><input type="checkbox" name="as"%s(oa['s']) />%s(B('s'))Setup |
| 522 | @ </label><br /> |
| 523 | } |
| 524 | @ <label><input type="checkbox" name="aa"%s(oa['a']) />%s(B('a'))Admin |
| 525 | @ </label><br /> |
| 526 | @ <label><input type="checkbox" name="ad"%s(oa['d']) />%s(B('d'))Delete |
| 527 | @ </label><br /> |
| 528 | @ <label><input type="checkbox" name="ae"%s(oa['e']) />%s(B('e'))Email |
| 529 | @ </label><br /> |
| 530 | @ <label><input type="checkbox" name="ap"%s(oa['p']) />%s(B('p'))Password |
| 531 | @ </label><br /> |
| 532 | @ <label><input type="checkbox" name="ai"%s(oa['i']) />%s(B('i'))Check-In |
| 533 | @ </label><br /> |
| 534 | @ <label><input type="checkbox" name="ao"%s(oa['o']) />%s(B('o'))Check-Out |
| 535 | @ </label><br /> |
| 536 | @ <label><input type="checkbox" name="ah"%s(oa['h']) />%s(B('h'))Hyperlinks |
| 537 | @ </label><br /> |
| 538 | @ <label><input type="checkbox" name="ab"%s(oa['b']) />%s(B('b'))Attachments |
| 539 | @ </label><br /> |
| 540 | @ </td><td><td width="40"></td><td valign="top"> |
| 541 | @ <label><input type="checkbox" name="au"%s(oa['u']) />%s(B('u'))Reader |
| 542 | @ </label><br /> |
| 543 | @ <label><input type="checkbox" name="av"%s(oa['v']) />%s(B('v'))Developer |
| 544 | @ </label><br /> |
| 545 | @ <label><input type="checkbox" name="ag"%s(oa['g']) />%s(B('g'))Clone |
| 546 | @ </label><br /> |
| 547 | @ <label><input type="checkbox" name="aj"%s(oa['j']) />%s(B('j'))Read Wiki |
| 548 | @ </label><br /> |
| 549 | @ <label><input type="checkbox" name="af"%s(oa['f']) />%s(B('f'))New Wiki |
| 550 | @ </label><br /> |
| 551 | @ <label><input type="checkbox" name="am"%s(oa['m']) />%s(B('m'))Append Wiki |
| 552 | @ </label><br /> |
| 553 | @ <label><input type="checkbox" name="ak"%s(oa['k']) />%s(B('k'))Write Wiki |
| 554 | @ </label><br /> |
| 555 | @ <label><input type="checkbox" name="al"%s(oa['l']) />%s(B('l'))Moderate |
| 556 | @ Wiki</label><br /> |
| 557 | @ </td><td><td width="40"></td><td valign="top"> |
| 558 | @ <label><input type="checkbox" name="ar"%s(oa['r']) />%s(B('r'))Read Ticket |
| 559 | @ </label><br /> |
| 560 | @ <label><input type="checkbox" name="an"%s(oa['n']) />%s(B('n'))New Tickets |
| 561 | @ </label><br /> |
| 562 | @ <label><input type="checkbox" name="ac"%s(oa['c']) />%s(B('c'))Append |
| 563 | @ To Ticket </label><br /> |
| 564 | @ <label><input type="checkbox" name="aw"%s(oa['w']) />%s(B('w'))Write |
| 565 | @ Tickets </label><br /> |
| 566 | @ <label><input type="checkbox" name="aq"%s(oa['q']) />%s(B('q'))Moderate |
| 567 | @ Tickets </label><br /> |
| 568 | @ <label><input type="checkbox" name="at"%s(oa['t']) />%s(B('t'))Ticket |
| 569 | @ Report </label><br /> |
| 570 | @ <label><input type="checkbox" name="ax"%s(oa['x']) />%s(B('x'))Private |
| 571 | @ </label><br /> |
| 572 | @ <label><input type="checkbox" name="az"%s(oa['z']) />%s(B('z'))Download |
| 573 | @ Zip </label> |
| 574 | @ </td></tr></table> |
| 575 | @ </td> |
| 576 | @ </tr> |
| 577 | if( !login_is_special(zLogin) ){ |
| 578 | @ <tr> |
| @@ -622,30 +622,30 @@ | |
| 622 | @ and reset user passwords. Both automatically get all other privileges |
| 623 | @ listed below. Use these two settings with discretion. |
| 624 | @ </p></li> |
| 625 | @ |
| 626 | @ <li><p> |
| 627 | @ The "<span class="ueditInheritNobody"><big>•</big></span>" mark |
| 628 | @ indicates the privileges of <span class="usertype">nobody</span> that |
| 629 | @ are available to all users regardless of whether or not they are logged in. |
| 630 | @ </p></li> |
| 631 | @ |
| 632 | @ <li><p> |
| 633 | @ The "<span class="ueditInheritAnonymous"><big>•</big></span>" mark |
| 634 | @ indicates the privileges of <span class="usertype">anonymous</span> that |
| 635 | @ are inherited by all logged-in users. |
| 636 | @ </p></li> |
| 637 | @ |
| 638 | @ <li><p> |
| 639 | @ The "<span class="ueditInheritDeveloper"><big>•</big></span>" mark |
| 640 | @ indicates the privileges of <span class="usertype">developer</span> that |
| 641 | @ are inherited by all users with the |
| 642 | @ <span class="capability">Developer</span> privilege. |
| 643 | @ </p></li> |
| 644 | @ |
| 645 | @ <li><p> |
| 646 | @ The "<span class="ueditInheritReader"><big>•</big></span>" mark |
| 647 | @ indicates the privileges of <span class="usertype">reader</span> that |
| 648 | @ are inherited by all users with the <span class="capability">Reader</span> |
| 649 | @ privilege. |
| 650 | @ </p></li> |
| 651 | @ |
| 652 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -441,38 +441,38 @@ | |
| 441 | if( fossil_strcmp(zLogin, "developer") ){ |
| 442 | char *z1, *z2; |
| 443 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'"); |
| 444 | while( z1 && *z1 ){ |
| 445 | inherit[0x7f & *(z1++)] = |
| 446 | "<span class=\"ueditInheritDeveloper\"><sub>D</sub></span>"; |
| 447 | } |
| 448 | free(z2); |
| 449 | } |
| 450 | if( fossil_strcmp(zLogin, "reader") ){ |
| 451 | char *z1, *z2; |
| 452 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'"); |
| 453 | while( z1 && *z1 ){ |
| 454 | inherit[0x7f & *(z1++)] = |
| 455 | "<span class=\"ueditInheritReader\"><sub>R</sub></span>"; |
| 456 | } |
| 457 | free(z2); |
| 458 | } |
| 459 | if( fossil_strcmp(zLogin, "anonymous") ){ |
| 460 | char *z1, *z2; |
| 461 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'"); |
| 462 | while( z1 && *z1 ){ |
| 463 | inherit[0x7f & *(z1++)] = |
| 464 | "<span class=\"ueditInheritAnonymous\"><sub>A</sub></span>"; |
| 465 | } |
| 466 | free(z2); |
| 467 | } |
| 468 | if( fossil_strcmp(zLogin, "nobody") ){ |
| 469 | char *z1, *z2; |
| 470 | z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'"); |
| 471 | while( z1 && *z1 ){ |
| 472 | inherit[0x7f & *(z1++)] = |
| 473 | "<span class=\"ueditInheritNobody\"><sub>N</sub></span>"; |
| 474 | } |
| 475 | free(z2); |
| 476 | } |
| 477 | |
| 478 | /* Begin generating the page |
| @@ -516,63 +516,63 @@ | |
| 516 | @ <td class="usetupEditLabel">Capabilities:</td> |
| 517 | @ <td> |
| 518 | #define B(x) inherit[x] |
| 519 | @ <table border=0><tr><td valign="top"> |
| 520 | if( g.perm.Setup ){ |
| 521 | @ <label><input type="checkbox" name="as"%s(oa['s']) /> |
| 522 | @ Setup%s(B('s'))</label><br /> |
| 523 | } |
| 524 | @ <label><input type="checkbox" name="aa"%s(oa['a']) /> |
| 525 | @ Admin%s(B('a'))</label><br /> |
| 526 | @ <label><input type="checkbox" name="ad"%s(oa['d']) /> |
| 527 | @ Delete%s(B('d'))</label><br /> |
| 528 | @ <label><input type="checkbox" name="ae"%s(oa['e']) /> |
| 529 | @ Email%s(B('e'))</label><br /> |
| 530 | @ <label><input type="checkbox" name="ap"%s(oa['p']) /> |
| 531 | @ Password%s(B('p'))</label><br /> |
| 532 | @ <label><input type="checkbox" name="ai"%s(oa['i']) /> |
| 533 | @ Check-In%s(B('i'))</label><br /> |
| 534 | @ <label><input type="checkbox" name="ao"%s(oa['o']) /> |
| 535 | @ Check-Out%s(B('o'))</label><br /> |
| 536 | @ <label><input type="checkbox" name="ah"%s(oa['h']) /> |
| 537 | @ Hyperlinks%s(B('h'))</label><br /> |
| 538 | @ <label><input type="checkbox" name="ab"%s(oa['b']) /> |
| 539 | @ Attachments%s(B('b'))</label><br /> |
| 540 | @ </td><td><td width="40"></td><td valign="top"> |
| 541 | @ <label><input type="checkbox" name="au"%s(oa['u']) /> |
| 542 | @ Reader%s(B('u'))</label><br /> |
| 543 | @ <label><input type="checkbox" name="av"%s(oa['v']) /> |
| 544 | @ Developer%s(B('v'))</label><br /> |
| 545 | @ <label><input type="checkbox" name="ag"%s(oa['g']) /> |
| 546 | @ Clone%s(B('g'))</label><br /> |
| 547 | @ <label><input type="checkbox" name="aj"%s(oa['j']) /> |
| 548 | @ Read Wiki%s(B('j'))</label><br /> |
| 549 | @ <label><input type="checkbox" name="af"%s(oa['f']) /> |
| 550 | @ New Wiki%s(B('f'))</label><br /> |
| 551 | @ <label><input type="checkbox" name="am"%s(oa['m']) /> |
| 552 | @ Append Wiki%s(B('m'))</label><br /> |
| 553 | @ <label><input type="checkbox" name="ak"%s(oa['k']) /> |
| 554 | @ Write Wiki%s(B('k'))</label><br /> |
| 555 | @ <label><input type="checkbox" name="al"%s(oa['l']) /> |
| 556 | @ Moderate Wiki%s(B('l'))</label><br /> |
| 557 | @ </td><td><td width="40"></td><td valign="top"> |
| 558 | @ <label><input type="checkbox" name="ar"%s(oa['r']) /> |
| 559 | @ Read Ticket%s(B('r'))</label><br /> |
| 560 | @ <label><input type="checkbox" name="an"%s(oa['n']) /> |
| 561 | @ New Tickets%s(B('n'))</label><br /> |
| 562 | @ <label><input type="checkbox" name="ac"%s(oa['c']) /> |
| 563 | @ Append To Ticket%s(B('c'))</label><br /> |
| 564 | @ <label><input type="checkbox" name="aw"%s(oa['w']) /> |
| 565 | @ Write Tickets%s(B('w'))</label><br /> |
| 566 | @ <label><input type="checkbox" name="aq"%s(oa['q']) /> |
| 567 | @ Moderate Tickets%s(B('q'))</label><br /> |
| 568 | @ <label><input type="checkbox" name="at"%s(oa['t']) /> |
| 569 | @ Ticket Report%s(B('t'))</label><br /> |
| 570 | @ <label><input type="checkbox" name="ax"%s(oa['x']) /> |
| 571 | @ Private%s(B('x'))</label><br /> |
| 572 | @ <label><input type="checkbox" name="az"%s(oa['z']) /> |
| 573 | @ Download Zip%s(B('z'))</label> |
| 574 | @ </td></tr></table> |
| 575 | @ </td> |
| 576 | @ </tr> |
| 577 | if( !login_is_special(zLogin) ){ |
| 578 | @ <tr> |
| @@ -622,30 +622,30 @@ | |
| 622 | @ and reset user passwords. Both automatically get all other privileges |
| 623 | @ listed below. Use these two settings with discretion. |
| 624 | @ </p></li> |
| 625 | @ |
| 626 | @ <li><p> |
| 627 | @ The "<span class="ueditInheritNobody"><sub>N</sub></span>" subscript suffix |
| 628 | @ indicates the privileges of <span class="usertype">nobody</span> that |
| 629 | @ are available to all users regardless of whether or not they are logged in. |
| 630 | @ </p></li> |
| 631 | @ |
| 632 | @ <li><p> |
| 633 | @ The "<span class="ueditInheritAnonymous"><sub>A</sub></span>" subscript suffix |
| 634 | @ indicates the privileges of <span class="usertype">anonymous</span> that |
| 635 | @ are inherited by all logged-in users. |
| 636 | @ </p></li> |
| 637 | @ |
| 638 | @ <li><p> |
| 639 | @ The "<span class="ueditInheritDeveloper"><sub>D</sub></span>" subscript suffix |
| 640 | @ indicates the privileges of <span class="usertype">developer</span> that |
| 641 | @ are inherited by all users with the |
| 642 | @ <span class="capability">Developer</span> privilege. |
| 643 | @ </p></li> |
| 644 | @ |
| 645 | @ <li><p> |
| 646 | @ The "<span class="ueditInheritReader"><sub>R</sub></span>" subscript suffix |
| 647 | @ indicates the privileges of <span class="usertype">reader</span> that |
| 648 | @ are inherited by all users with the <span class="capability">Reader</span> |
| 649 | @ privilege. |
| 650 | @ </p></li> |
| 651 | @ |
| 652 |
+85
-61
| --- src/winfile.c | ||
| +++ src/winfile.c | ||
| @@ -35,13 +35,13 @@ | ||
| 35 | 35 | #ifndef LABEL_SECURITY_INFORMATION |
| 36 | 36 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 37 | 37 | #endif |
| 38 | 38 | |
| 39 | 39 | #if defined(__MSVCRT__) |
| 40 | -/* TODO: determine those dynamically. */ | |
| 41 | -WINBASEAPI DWORD WINAPI GetFinalPathNameByHandleW (HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); | |
| 42 | -WINBASEAPI BOOLEAN APIENTRY CreateSymbolicLinkW (LPCWSTR lpSymlinkFileName, LPCWSTR lpTargetFileName, DWORD dwFlags); | |
| 40 | +static HANDLE dllhandle = NULL; | |
| 41 | +static DWORD WINAPI (*getFinalPathNameByHandleW) (HANDLE, LPWSTR, DWORD, DWORD) = NULL; | |
| 42 | +static BOOLEAN APIENTRY (*createSymbolicLinkW) (LPCWSTR, LPCWSTR, DWORD) = NULL; | |
| 43 | 43 | #endif |
| 44 | 44 | |
| 45 | 45 | /* a couple defines to make the borrowed struct below compile */ |
| 46 | 46 | #ifndef _ANONYMOUS_UNION |
| 47 | 47 | # define _ANONYMOUS_UNION |
| @@ -79,10 +79,20 @@ | ||
| 79 | 79 | } GenericReparseBuffer; |
| 80 | 80 | } DUMMYUNIONNAME; |
| 81 | 81 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 82 | 82 | |
| 83 | 83 | #define LINK_BUFFER_SIZE 1024 |
| 84 | + | |
| 85 | +static int isVistaOrLater(){ | |
| 86 | + if( !dllhandle ){ | |
| 87 | + HANDLE h = LoadLibraryW(L"KERNEL32"); | |
| 88 | + createSymbolicLinkW = (BOOLEAN APIENTRY (*) (LPCWSTR, LPCWSTR, DWORD)) GetProcAddress(h, "CreateSymbolicLinkW"); | |
| 89 | + getFinalPathNameByHandleW = (DWORD WINAPI (*) (HANDLE, LPWSTR, DWORD, DWORD)) GetProcAddress(h, "GetFinalPathNameByHandleW"); | |
| 90 | + dllhandle = h; | |
| 91 | + } | |
| 92 | + return createSymbolicLinkW != NULL; | |
| 93 | +} | |
| 84 | 94 | |
| 85 | 95 | /* |
| 86 | 96 | ** Fill stat buf with information received from GetFileAttributesExW(). |
| 87 | 97 | ** Does not follow symbolic links, returning instead information about |
| 88 | 98 | ** the link itself. |
| @@ -91,33 +101,33 @@ | ||
| 91 | 101 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 92 | 102 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 93 | 103 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 94 | 104 | if( rc ){ |
| 95 | 105 | ssize_t tlen = 0; /* assume it is not a symbolic link */ |
| 96 | - | |
| 106 | + | |
| 97 | 107 | /* if it is a reparse point it *might* be a symbolic link */ |
| 98 | 108 | /* so defer to win32_readlink to actually check */ |
| 99 | - if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){ | |
| 109 | + if( attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ){ | |
| 100 | 110 | char *tname = fossil_filename_to_utf8(zFilename); |
| 101 | 111 | char tlink[LINK_BUFFER_SIZE]; |
| 102 | 112 | tlen = win32_readlink(tname, tlink, sizeof(tlink)); |
| 103 | 113 | fossil_filename_free(tname); |
| 104 | 114 | } |
| 105 | - | |
| 115 | + | |
| 106 | 116 | ULARGE_INTEGER ull; |
| 107 | 117 | |
| 108 | 118 | /* if a link was retrieved, it is a symlink, otherwise a dir or file */ |
| 109 | - if (tlen == 0){ | |
| 119 | + if( tlen == 0 ){ | |
| 110 | 120 | buf->st_mode = ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 111 | 121 | S_IFDIR : S_IFREG); |
| 112 | - | |
| 122 | + | |
| 113 | 123 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| 114 | 124 | }else{ |
| 115 | 125 | buf->st_mode = S_IFLNK; |
| 116 | 126 | buf->st_size = tlen; |
| 117 | 127 | } |
| 118 | - | |
| 128 | + | |
| 119 | 129 | ull.LowPart = attr.ftLastWriteTime.dwLowDateTime; |
| 120 | 130 | ull.HighPart = attr.ftLastWriteTime.dwHighDateTime; |
| 121 | 131 | buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL; |
| 122 | 132 | } |
| 123 | 133 | return !rc; |
| @@ -126,11 +136,11 @@ | ||
| 126 | 136 | /* |
| 127 | 137 | ** Fill stat buf with information received from win32_lstat(). |
| 128 | 138 | ** If a symbolic link is found, follow it and return information about |
| 129 | 139 | ** the target, repeating until an actual target is found. |
| 130 | 140 | ** Limit the number of loop iterations so as to avoid an infinite loop |
| 131 | -** due to circular links. This should never happen because | |
| 141 | +** due to circular links. This should never happen because | |
| 132 | 142 | ** GetFinalPathNameByHandleW() should always preclude that need, but being |
| 133 | 143 | ** prepared to loop seems prudent, or at least not harmful. |
| 134 | 144 | ** Returns 0 on success, 1 on failure. |
| 135 | 145 | */ |
| 136 | 146 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| @@ -137,40 +147,44 @@ | ||
| 137 | 147 | int rc; |
| 138 | 148 | HANDLE file; |
| 139 | 149 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 140 | 150 | DWORD len; |
| 141 | 151 | int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ |
| 142 | - | |
| 152 | + | |
| 143 | 153 | while (iterationsRemaining-- > 0){ |
| 144 | 154 | rc = win32_lstat(zFilename, buf); |
| 145 | 155 | |
| 146 | 156 | /* exit on error or not link */ |
| 147 | - if ((rc != 0) || (buf->st_mode != S_IFLNK)) | |
| 157 | + if( (rc != 0) || (buf->st_mode != S_IFLNK) ) | |
| 148 | 158 | break; |
| 149 | 159 | |
| 150 | - /* it is a link, so open the linked file */ | |
| 160 | + /* it is a link, so open the linked file */ | |
| 151 | 161 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
| 152 | - if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ | |
| 162 | + if( (file == NULL) || (file == INVALID_HANDLE_VALUE) ){ | |
| 153 | 163 | rc = 1; |
| 154 | 164 | break; |
| 155 | 165 | } |
| 156 | 166 | |
| 157 | 167 | /* get the final path name and close the handle */ |
| 158 | - len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); | |
| 168 | + if( isVistaOrLater() ){ | |
| 169 | + len = getFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); | |
| 170 | + }else{ | |
| 171 | + len = -1; | |
| 172 | + } | |
| 159 | 173 | CloseHandle(file); |
| 160 | - | |
| 174 | + | |
| 161 | 175 | /* if any problems getting the final path name error so exit */ |
| 162 | - if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ | |
| 176 | + if( (len <= 0) || (len > LINK_BUFFER_SIZE - 1) ){ | |
| 163 | 177 | rc = 1; |
| 164 | 178 | break; |
| 165 | 179 | } |
| 166 | - | |
| 180 | + | |
| 167 | 181 | /* prepare to try again just in case we have a chain to follow */ |
| 168 | 182 | /* this shouldn't happen, but just trying to be safe */ |
| 169 | 183 | zFilename = nextFilename; |
| 170 | 184 | } |
| 171 | - | |
| 185 | + | |
| 172 | 186 | return rc; |
| 173 | 187 | } |
| 174 | 188 | |
| 175 | 189 | /* |
| 176 | 190 | ** An implementation of a posix-like readlink function for win32. |
| @@ -178,36 +192,36 @@ | ||
| 178 | 192 | ** Returns the length of the link copied to buf on success, -1 on failure. |
| 179 | 193 | */ |
| 180 | 194 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 181 | 195 | /* assume we're going to fail */ |
| 182 | 196 | ssize_t rv = -1; |
| 183 | - | |
| 197 | + | |
| 184 | 198 | /* does path reference a reparse point? */ |
| 185 | 199 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 186 | 200 | int rc = GetFileAttributesEx(path, GetFileExInfoStandard, &attr); |
| 187 | - if (rc && (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)){ | |
| 188 | - | |
| 201 | + if( rc && (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ){ | |
| 202 | + | |
| 189 | 203 | /* since it is a reparse point, open it */ |
| 190 | - HANDLE file = CreateFile(path, FILE_READ_EA, 0, NULL, OPEN_EXISTING, | |
| 191 | - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); | |
| 192 | - if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ | |
| 204 | + HANDLE file = CreateFile(path, FILE_READ_EA, 0, NULL, OPEN_EXISTING, | |
| 205 | + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); | |
| 206 | + if( (file != NULL) && (file != INVALID_HANDLE_VALUE) ){ | |
| 193 | 207 | |
| 194 | 208 | /* use DeviceIoControl to get the reparse point data */ |
| 195 | - | |
| 209 | + | |
| 196 | 210 | int data_size = sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t); |
| 197 | 211 | REPARSE_DATA_BUFFER* data = fossil_malloc(data_size); |
| 198 | 212 | DWORD data_used; |
| 199 | - | |
| 213 | + | |
| 200 | 214 | data->ReparseTag = IO_REPARSE_TAG_SYMLINK; |
| 201 | 215 | data->ReparseDataLength = 0; |
| 202 | 216 | data->Reserved = 0; |
| 203 | - | |
| 217 | + | |
| 204 | 218 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 205 | 219 | data, data_size, &data_used, NULL); |
| 206 | 220 | |
| 207 | 221 | /* did the reparse point data fit into the desired buffer? */ |
| 208 | - if (rc && (data_used < data_size)){ | |
| 222 | + if( rc && (data_used < data_size) ){ | |
| 209 | 223 | /* it fit, so setup the print name for further processing */ |
| 210 | 224 | USHORT |
| 211 | 225 | offset = data->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), |
| 212 | 226 | length = data->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t); |
| 213 | 227 | char *temp; |
| @@ -214,18 +228,18 @@ | ||
| 214 | 228 | data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; |
| 215 | 229 | |
| 216 | 230 | /* convert the filename to utf8, copy it, and discard the converted copy */ |
| 217 | 231 | temp = fossil_filename_to_utf8(data->SymbolicLinkReparseBuffer.PathBuffer + offset); |
| 218 | 232 | rv = strlen(temp); |
| 219 | - if (rv >= bufsiz) | |
| 233 | + if( rv >= bufsiz ) | |
| 220 | 234 | rv = bufsiz; |
| 221 | 235 | memcpy(buf, temp, rv); |
| 222 | 236 | fossil_filename_free(temp); |
| 223 | 237 | } |
| 224 | - | |
| 238 | + | |
| 225 | 239 | fossil_free(data); |
| 226 | - | |
| 240 | + | |
| 227 | 241 | /* all done, close the reparse point */ |
| 228 | 242 | CloseHandle(file); |
| 229 | 243 | } |
| 230 | 244 | } |
| 231 | 245 | |
| @@ -242,12 +256,12 @@ | ||
| 242 | 256 | ** Returns 0 on success, 1 on failure. |
| 243 | 257 | */ |
| 244 | 258 | int win32_unlink_rmdir(const wchar_t *zFilename){ |
| 245 | 259 | int rc = 0; |
| 246 | 260 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 247 | - if (GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr)){ | |
| 248 | - if ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) | |
| 261 | + if( GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr) ){ | |
| 262 | + if( (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY ) | |
| 249 | 263 | rc = RemoveDirectoryW(zFilename); |
| 250 | 264 | else |
| 251 | 265 | rc = DeleteFileW(zFilename); |
| 252 | 266 | } |
| 253 | 267 | return !rc; |
| @@ -266,33 +280,36 @@ | ||
| 266 | 280 | fossilStat stat; |
| 267 | 281 | int created = 0; |
| 268 | 282 | DWORD flags = 0; |
| 269 | 283 | wchar_t *zMbcs, *zMbcsOld; |
| 270 | 284 | |
| 271 | - /* does oldpath exist? is it a dir or a file? */ | |
| 285 | + /* does oldpath exist? is it a dir or a file? */ | |
| 272 | 286 | zMbcsOld = fossil_utf8_to_filename(oldpath); |
| 273 | - if (win32_stat(zMbcsOld, &stat) == 0){ | |
| 274 | - if (stat.st_mode == S_IFDIR) | |
| 287 | + if( win32_stat(zMbcsOld, &stat) == 0 ){ | |
| 288 | + if( stat.st_mode == S_IFDIR ){ | |
| 275 | 289 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 290 | + } | |
| 276 | 291 | } |
| 277 | 292 | |
| 278 | 293 | /* remove newpath before creating the symlink */ |
| 279 | 294 | zMbcs = fossil_utf8_to_filename(newpath); |
| 280 | 295 | win32_unlink_rmdir(zMbcs); |
| 281 | - created = CreateSymbolicLinkW(zMbcs, zMbcsOld, flags); | |
| 296 | + if( isVistaOrLater() ){ | |
| 297 | + created = createSymbolicLinkW(zMbcs, zMbcsOld, flags); | |
| 298 | + } | |
| 282 | 299 | fossil_filename_free(zMbcs); |
| 283 | 300 | fossil_filename_free(zMbcsOld); |
| 284 | 301 | |
| 285 | 302 | /* if the symlink was not created, create a plain text file */ |
| 286 | - if (!created){ | |
| 303 | + if( !created ){ | |
| 287 | 304 | Blob content; |
| 288 | 305 | blob_set(&content, oldpath); |
| 289 | 306 | blob_write_to_file(&content, newpath); |
| 290 | 307 | blob_reset(&content); |
| 291 | 308 | created = 1; |
| 292 | 309 | } |
| 293 | - | |
| 310 | + | |
| 294 | 311 | return !created; |
| 295 | 312 | } |
| 296 | 313 | |
| 297 | 314 | /* |
| 298 | 315 | ** Given a pathname to a file, return true if: |
| @@ -305,12 +322,13 @@ | ||
| 305 | 322 | int changed = 0; |
| 306 | 323 | wchar_t* zMbcs; |
| 307 | 324 | fossilStat lstat_buf, stat_buf; |
| 308 | 325 | WIN32_FILE_ATTRIBUTE_DATA lstat_attr; |
| 309 | 326 | zMbcs = fossil_utf8_to_filename(zName); |
| 310 | - if (win32_stat(zMbcs, &stat_buf) != 0) | |
| 327 | + if( win32_stat(zMbcs, &stat_buf) != 0 ){ | |
| 311 | 328 | stat_buf.st_mode = S_IFREG; |
| 329 | + } | |
| 312 | 330 | changed = |
| 313 | 331 | (win32_lstat(zMbcs, &lstat_buf) == 0) && |
| 314 | 332 | (lstat_buf.st_mode == S_IFLNK) && |
| 315 | 333 | GetFileAttributesExW(zMbcs, GetFileExInfoStandard, &lstat_attr) && |
| 316 | 334 | ((stat_buf.st_mode == S_IFDIR) != ((lstat_attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)); |
| @@ -337,65 +355,69 @@ | ||
| 337 | 355 | DWORD fullLength; |
| 338 | 356 | wchar_t volName[MAX_PATH+1]; |
| 339 | 357 | DWORD fsFlags; |
| 340 | 358 | |
| 341 | 359 | /* symlinks only supported on vista or greater */ |
| 342 | - /* if (!IsWindowsVistaOrGreater()) // TODO: make it work on MinGW | |
| 343 | - return 0; */ | |
| 360 | + if( !isVistaOrLater() ){ | |
| 361 | + return 0; | |
| 362 | + } | |
| 344 | 363 | |
| 345 | 364 | /* next we need to check to see if the privilege is available */ |
| 346 | - | |
| 365 | + | |
| 347 | 366 | /* can't check privilege if we can't lookup its value */ |
| 348 | - if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid)) | |
| 367 | + if( !LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid) ){ | |
| 349 | 368 | return 0; |
| 350 | - | |
| 369 | + } | |
| 370 | + | |
| 351 | 371 | /* can't check privilege if we can't open the process token */ |
| 352 | 372 | process = GetCurrentProcess(); |
| 353 | - if (!OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token)) | |
| 373 | + if( !OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token) ){ | |
| 354 | 374 | return 0; |
| 355 | - | |
| 375 | + } | |
| 376 | + | |
| 356 | 377 | /* by this point, we have a process token and the privilege value */ |
| 357 | 378 | /* try to enable the privilege then close the token */ |
| 358 | - | |
| 379 | + | |
| 359 | 380 | tp.PrivilegeCount = 1; |
| 360 | 381 | tp.Privileges[0].Luid = luid; |
| 361 | 382 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
| 362 | - | |
| 383 | + | |
| 363 | 384 | AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); |
| 364 | 385 | status = GetLastError(); |
| 365 | - | |
| 386 | + | |
| 366 | 387 | CloseHandle(token); |
| 367 | 388 | |
| 368 | 389 | /* any error means we failed to enable the privilege, symlinks not supported */ |
| 369 | - if (status != ERROR_SUCCESS) | |
| 390 | + if( status != ERROR_SUCCESS ){ | |
| 370 | 391 | return 0; |
| 392 | + } | |
| 371 | 393 | |
| 372 | 394 | /* assume no support for symlinks */ |
| 373 | 395 | success = 0; |
| 374 | - | |
| 396 | + | |
| 375 | 397 | pFilename = fossil_utf8_to_filename(zFilename); |
| 376 | 398 | |
| 377 | 399 | /* given the filename we're interested in, symlinks are supported if */ |
| 378 | 400 | /* 1. we can get the full name of the path from the given path */ |
| 379 | 401 | fullLength = GetFullPathNameW(pFilename, sizeof(fullName), fullName, NULL); |
| 380 | - if ((fullLength > 0) && (fullLength < sizeof(fullName))){ | |
| 402 | + if( (fullLength > 0) && (fullLength < sizeof(fullName)) ){ | |
| 381 | 403 | /* 2. we can get the volume path name from the full name */ |
| 382 | - if (GetVolumePathNameW(fullName, volName, sizeof(volName))){ | |
| 404 | + if( GetVolumePathNameW(fullName, volName, sizeof(volName)) ){ | |
| 383 | 405 | /* 3. we can get volume information from the volume path name */ |
| 384 | - if (GetVolumeInformationW(volName, NULL, 0, NULL, NULL, &fsFlags, NULL, 0)){ | |
| 406 | + if( GetVolumeInformationW(volName, NULL, 0, NULL, NULL, &fsFlags, NULL, 0) ){ | |
| 385 | 407 | /* 4. the given volume support reparse points */ |
| 386 | - if (fsFlags & FILE_SUPPORTS_REPARSE_POINTS){ | |
| 408 | + if( fsFlags & FILE_SUPPORTS_REPARSE_POINTS ){ | |
| 387 | 409 | /* all four conditions were true, so we support symlinks; success! */ |
| 388 | 410 | success = 1; |
| 389 | 411 | } |
| 390 | 412 | } |
| 391 | 413 | } |
| 392 | 414 | } |
| 393 | - | |
| 415 | + | |
| 394 | 416 | fossil_filename_free(pFilename); |
| 395 | - | |
| 396 | - return success; | |
| 417 | + | |
| 418 | + return success; | |
| 397 | 419 | } |
| 398 | 420 | |
| 399 | 421 | /* |
| 400 | 422 | ** Wrapper around the access() system call. This code was copied from Tcl |
| 401 | 423 | ** 8.6 and then modified. |
| @@ -587,11 +609,13 @@ | ||
| 587 | 609 | * Unable to perform access check. |
| 588 | 610 | */ |
| 589 | 611 | |
| 590 | 612 | rc = -1; goto done; |
| 591 | 613 | } |
| 592 | - if( !accessYesNo ) rc = -1; | |
| 614 | + if( !accessYesNo ){ | |
| 615 | + rc = -1; | |
| 616 | + } | |
| 593 | 617 | |
| 594 | 618 | done: |
| 595 | 619 | |
| 596 | 620 | if( hToken != NULL ){ |
| 597 | 621 | CloseHandle(hToken); |
| 598 | 622 |
| --- src/winfile.c | |
| +++ src/winfile.c | |
| @@ -35,13 +35,13 @@ | |
| 35 | #ifndef LABEL_SECURITY_INFORMATION |
| 36 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 37 | #endif |
| 38 | |
| 39 | #if defined(__MSVCRT__) |
| 40 | /* TODO: determine those dynamically. */ |
| 41 | WINBASEAPI DWORD WINAPI GetFinalPathNameByHandleW (HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); |
| 42 | WINBASEAPI BOOLEAN APIENTRY CreateSymbolicLinkW (LPCWSTR lpSymlinkFileName, LPCWSTR lpTargetFileName, DWORD dwFlags); |
| 43 | #endif |
| 44 | |
| 45 | /* a couple defines to make the borrowed struct below compile */ |
| 46 | #ifndef _ANONYMOUS_UNION |
| 47 | # define _ANONYMOUS_UNION |
| @@ -79,10 +79,20 @@ | |
| 79 | } GenericReparseBuffer; |
| 80 | } DUMMYUNIONNAME; |
| 81 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 82 | |
| 83 | #define LINK_BUFFER_SIZE 1024 |
| 84 | |
| 85 | /* |
| 86 | ** Fill stat buf with information received from GetFileAttributesExW(). |
| 87 | ** Does not follow symbolic links, returning instead information about |
| 88 | ** the link itself. |
| @@ -91,33 +101,33 @@ | |
| 91 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 92 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 93 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 94 | if( rc ){ |
| 95 | ssize_t tlen = 0; /* assume it is not a symbolic link */ |
| 96 | |
| 97 | /* if it is a reparse point it *might* be a symbolic link */ |
| 98 | /* so defer to win32_readlink to actually check */ |
| 99 | if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){ |
| 100 | char *tname = fossil_filename_to_utf8(zFilename); |
| 101 | char tlink[LINK_BUFFER_SIZE]; |
| 102 | tlen = win32_readlink(tname, tlink, sizeof(tlink)); |
| 103 | fossil_filename_free(tname); |
| 104 | } |
| 105 | |
| 106 | ULARGE_INTEGER ull; |
| 107 | |
| 108 | /* if a link was retrieved, it is a symlink, otherwise a dir or file */ |
| 109 | if (tlen == 0){ |
| 110 | buf->st_mode = ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 111 | S_IFDIR : S_IFREG); |
| 112 | |
| 113 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| 114 | }else{ |
| 115 | buf->st_mode = S_IFLNK; |
| 116 | buf->st_size = tlen; |
| 117 | } |
| 118 | |
| 119 | ull.LowPart = attr.ftLastWriteTime.dwLowDateTime; |
| 120 | ull.HighPart = attr.ftLastWriteTime.dwHighDateTime; |
| 121 | buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL; |
| 122 | } |
| 123 | return !rc; |
| @@ -126,11 +136,11 @@ | |
| 126 | /* |
| 127 | ** Fill stat buf with information received from win32_lstat(). |
| 128 | ** If a symbolic link is found, follow it and return information about |
| 129 | ** the target, repeating until an actual target is found. |
| 130 | ** Limit the number of loop iterations so as to avoid an infinite loop |
| 131 | ** due to circular links. This should never happen because |
| 132 | ** GetFinalPathNameByHandleW() should always preclude that need, but being |
| 133 | ** prepared to loop seems prudent, or at least not harmful. |
| 134 | ** Returns 0 on success, 1 on failure. |
| 135 | */ |
| 136 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| @@ -137,40 +147,44 @@ | |
| 137 | int rc; |
| 138 | HANDLE file; |
| 139 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 140 | DWORD len; |
| 141 | int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ |
| 142 | |
| 143 | while (iterationsRemaining-- > 0){ |
| 144 | rc = win32_lstat(zFilename, buf); |
| 145 | |
| 146 | /* exit on error or not link */ |
| 147 | if ((rc != 0) || (buf->st_mode != S_IFLNK)) |
| 148 | break; |
| 149 | |
| 150 | /* it is a link, so open the linked file */ |
| 151 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
| 152 | if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ |
| 153 | rc = 1; |
| 154 | break; |
| 155 | } |
| 156 | |
| 157 | /* get the final path name and close the handle */ |
| 158 | len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 159 | CloseHandle(file); |
| 160 | |
| 161 | /* if any problems getting the final path name error so exit */ |
| 162 | if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ |
| 163 | rc = 1; |
| 164 | break; |
| 165 | } |
| 166 | |
| 167 | /* prepare to try again just in case we have a chain to follow */ |
| 168 | /* this shouldn't happen, but just trying to be safe */ |
| 169 | zFilename = nextFilename; |
| 170 | } |
| 171 | |
| 172 | return rc; |
| 173 | } |
| 174 | |
| 175 | /* |
| 176 | ** An implementation of a posix-like readlink function for win32. |
| @@ -178,36 +192,36 @@ | |
| 178 | ** Returns the length of the link copied to buf on success, -1 on failure. |
| 179 | */ |
| 180 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 181 | /* assume we're going to fail */ |
| 182 | ssize_t rv = -1; |
| 183 | |
| 184 | /* does path reference a reparse point? */ |
| 185 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 186 | int rc = GetFileAttributesEx(path, GetFileExInfoStandard, &attr); |
| 187 | if (rc && (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)){ |
| 188 | |
| 189 | /* since it is a reparse point, open it */ |
| 190 | HANDLE file = CreateFile(path, FILE_READ_EA, 0, NULL, OPEN_EXISTING, |
| 191 | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 192 | if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ |
| 193 | |
| 194 | /* use DeviceIoControl to get the reparse point data */ |
| 195 | |
| 196 | int data_size = sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t); |
| 197 | REPARSE_DATA_BUFFER* data = fossil_malloc(data_size); |
| 198 | DWORD data_used; |
| 199 | |
| 200 | data->ReparseTag = IO_REPARSE_TAG_SYMLINK; |
| 201 | data->ReparseDataLength = 0; |
| 202 | data->Reserved = 0; |
| 203 | |
| 204 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 205 | data, data_size, &data_used, NULL); |
| 206 | |
| 207 | /* did the reparse point data fit into the desired buffer? */ |
| 208 | if (rc && (data_used < data_size)){ |
| 209 | /* it fit, so setup the print name for further processing */ |
| 210 | USHORT |
| 211 | offset = data->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), |
| 212 | length = data->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t); |
| 213 | char *temp; |
| @@ -214,18 +228,18 @@ | |
| 214 | data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; |
| 215 | |
| 216 | /* convert the filename to utf8, copy it, and discard the converted copy */ |
| 217 | temp = fossil_filename_to_utf8(data->SymbolicLinkReparseBuffer.PathBuffer + offset); |
| 218 | rv = strlen(temp); |
| 219 | if (rv >= bufsiz) |
| 220 | rv = bufsiz; |
| 221 | memcpy(buf, temp, rv); |
| 222 | fossil_filename_free(temp); |
| 223 | } |
| 224 | |
| 225 | fossil_free(data); |
| 226 | |
| 227 | /* all done, close the reparse point */ |
| 228 | CloseHandle(file); |
| 229 | } |
| 230 | } |
| 231 | |
| @@ -242,12 +256,12 @@ | |
| 242 | ** Returns 0 on success, 1 on failure. |
| 243 | */ |
| 244 | int win32_unlink_rmdir(const wchar_t *zFilename){ |
| 245 | int rc = 0; |
| 246 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 247 | if (GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr)){ |
| 248 | if ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) |
| 249 | rc = RemoveDirectoryW(zFilename); |
| 250 | else |
| 251 | rc = DeleteFileW(zFilename); |
| 252 | } |
| 253 | return !rc; |
| @@ -266,33 +280,36 @@ | |
| 266 | fossilStat stat; |
| 267 | int created = 0; |
| 268 | DWORD flags = 0; |
| 269 | wchar_t *zMbcs, *zMbcsOld; |
| 270 | |
| 271 | /* does oldpath exist? is it a dir or a file? */ |
| 272 | zMbcsOld = fossil_utf8_to_filename(oldpath); |
| 273 | if (win32_stat(zMbcsOld, &stat) == 0){ |
| 274 | if (stat.st_mode == S_IFDIR) |
| 275 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 276 | } |
| 277 | |
| 278 | /* remove newpath before creating the symlink */ |
| 279 | zMbcs = fossil_utf8_to_filename(newpath); |
| 280 | win32_unlink_rmdir(zMbcs); |
| 281 | created = CreateSymbolicLinkW(zMbcs, zMbcsOld, flags); |
| 282 | fossil_filename_free(zMbcs); |
| 283 | fossil_filename_free(zMbcsOld); |
| 284 | |
| 285 | /* if the symlink was not created, create a plain text file */ |
| 286 | if (!created){ |
| 287 | Blob content; |
| 288 | blob_set(&content, oldpath); |
| 289 | blob_write_to_file(&content, newpath); |
| 290 | blob_reset(&content); |
| 291 | created = 1; |
| 292 | } |
| 293 | |
| 294 | return !created; |
| 295 | } |
| 296 | |
| 297 | /* |
| 298 | ** Given a pathname to a file, return true if: |
| @@ -305,12 +322,13 @@ | |
| 305 | int changed = 0; |
| 306 | wchar_t* zMbcs; |
| 307 | fossilStat lstat_buf, stat_buf; |
| 308 | WIN32_FILE_ATTRIBUTE_DATA lstat_attr; |
| 309 | zMbcs = fossil_utf8_to_filename(zName); |
| 310 | if (win32_stat(zMbcs, &stat_buf) != 0) |
| 311 | stat_buf.st_mode = S_IFREG; |
| 312 | changed = |
| 313 | (win32_lstat(zMbcs, &lstat_buf) == 0) && |
| 314 | (lstat_buf.st_mode == S_IFLNK) && |
| 315 | GetFileAttributesExW(zMbcs, GetFileExInfoStandard, &lstat_attr) && |
| 316 | ((stat_buf.st_mode == S_IFDIR) != ((lstat_attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)); |
| @@ -337,65 +355,69 @@ | |
| 337 | DWORD fullLength; |
| 338 | wchar_t volName[MAX_PATH+1]; |
| 339 | DWORD fsFlags; |
| 340 | |
| 341 | /* symlinks only supported on vista or greater */ |
| 342 | /* if (!IsWindowsVistaOrGreater()) // TODO: make it work on MinGW |
| 343 | return 0; */ |
| 344 | |
| 345 | /* next we need to check to see if the privilege is available */ |
| 346 | |
| 347 | /* can't check privilege if we can't lookup its value */ |
| 348 | if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid)) |
| 349 | return 0; |
| 350 | |
| 351 | /* can't check privilege if we can't open the process token */ |
| 352 | process = GetCurrentProcess(); |
| 353 | if (!OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token)) |
| 354 | return 0; |
| 355 | |
| 356 | /* by this point, we have a process token and the privilege value */ |
| 357 | /* try to enable the privilege then close the token */ |
| 358 | |
| 359 | tp.PrivilegeCount = 1; |
| 360 | tp.Privileges[0].Luid = luid; |
| 361 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
| 362 | |
| 363 | AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); |
| 364 | status = GetLastError(); |
| 365 | |
| 366 | CloseHandle(token); |
| 367 | |
| 368 | /* any error means we failed to enable the privilege, symlinks not supported */ |
| 369 | if (status != ERROR_SUCCESS) |
| 370 | return 0; |
| 371 | |
| 372 | /* assume no support for symlinks */ |
| 373 | success = 0; |
| 374 | |
| 375 | pFilename = fossil_utf8_to_filename(zFilename); |
| 376 | |
| 377 | /* given the filename we're interested in, symlinks are supported if */ |
| 378 | /* 1. we can get the full name of the path from the given path */ |
| 379 | fullLength = GetFullPathNameW(pFilename, sizeof(fullName), fullName, NULL); |
| 380 | if ((fullLength > 0) && (fullLength < sizeof(fullName))){ |
| 381 | /* 2. we can get the volume path name from the full name */ |
| 382 | if (GetVolumePathNameW(fullName, volName, sizeof(volName))){ |
| 383 | /* 3. we can get volume information from the volume path name */ |
| 384 | if (GetVolumeInformationW(volName, NULL, 0, NULL, NULL, &fsFlags, NULL, 0)){ |
| 385 | /* 4. the given volume support reparse points */ |
| 386 | if (fsFlags & FILE_SUPPORTS_REPARSE_POINTS){ |
| 387 | /* all four conditions were true, so we support symlinks; success! */ |
| 388 | success = 1; |
| 389 | } |
| 390 | } |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | fossil_filename_free(pFilename); |
| 395 | |
| 396 | return success; |
| 397 | } |
| 398 | |
| 399 | /* |
| 400 | ** Wrapper around the access() system call. This code was copied from Tcl |
| 401 | ** 8.6 and then modified. |
| @@ -587,11 +609,13 @@ | |
| 587 | * Unable to perform access check. |
| 588 | */ |
| 589 | |
| 590 | rc = -1; goto done; |
| 591 | } |
| 592 | if( !accessYesNo ) rc = -1; |
| 593 | |
| 594 | done: |
| 595 | |
| 596 | if( hToken != NULL ){ |
| 597 | CloseHandle(hToken); |
| 598 |
| --- src/winfile.c | |
| +++ src/winfile.c | |
| @@ -35,13 +35,13 @@ | |
| 35 | #ifndef LABEL_SECURITY_INFORMATION |
| 36 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 37 | #endif |
| 38 | |
| 39 | #if defined(__MSVCRT__) |
| 40 | static HANDLE dllhandle = NULL; |
| 41 | static DWORD WINAPI (*getFinalPathNameByHandleW) (HANDLE, LPWSTR, DWORD, DWORD) = NULL; |
| 42 | static BOOLEAN APIENTRY (*createSymbolicLinkW) (LPCWSTR, LPCWSTR, DWORD) = NULL; |
| 43 | #endif |
| 44 | |
| 45 | /* a couple defines to make the borrowed struct below compile */ |
| 46 | #ifndef _ANONYMOUS_UNION |
| 47 | # define _ANONYMOUS_UNION |
| @@ -79,10 +79,20 @@ | |
| 79 | } GenericReparseBuffer; |
| 80 | } DUMMYUNIONNAME; |
| 81 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 82 | |
| 83 | #define LINK_BUFFER_SIZE 1024 |
| 84 | |
| 85 | static int isVistaOrLater(){ |
| 86 | if( !dllhandle ){ |
| 87 | HANDLE h = LoadLibraryW(L"KERNEL32"); |
| 88 | createSymbolicLinkW = (BOOLEAN APIENTRY (*) (LPCWSTR, LPCWSTR, DWORD)) GetProcAddress(h, "CreateSymbolicLinkW"); |
| 89 | getFinalPathNameByHandleW = (DWORD WINAPI (*) (HANDLE, LPWSTR, DWORD, DWORD)) GetProcAddress(h, "GetFinalPathNameByHandleW"); |
| 90 | dllhandle = h; |
| 91 | } |
| 92 | return createSymbolicLinkW != NULL; |
| 93 | } |
| 94 | |
| 95 | /* |
| 96 | ** Fill stat buf with information received from GetFileAttributesExW(). |
| 97 | ** Does not follow symbolic links, returning instead information about |
| 98 | ** the link itself. |
| @@ -91,33 +101,33 @@ | |
| 101 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 102 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 103 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 104 | if( rc ){ |
| 105 | ssize_t tlen = 0; /* assume it is not a symbolic link */ |
| 106 | |
| 107 | /* if it is a reparse point it *might* be a symbolic link */ |
| 108 | /* so defer to win32_readlink to actually check */ |
| 109 | if( attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ){ |
| 110 | char *tname = fossil_filename_to_utf8(zFilename); |
| 111 | char tlink[LINK_BUFFER_SIZE]; |
| 112 | tlen = win32_readlink(tname, tlink, sizeof(tlink)); |
| 113 | fossil_filename_free(tname); |
| 114 | } |
| 115 | |
| 116 | ULARGE_INTEGER ull; |
| 117 | |
| 118 | /* if a link was retrieved, it is a symlink, otherwise a dir or file */ |
| 119 | if( tlen == 0 ){ |
| 120 | buf->st_mode = ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 121 | S_IFDIR : S_IFREG); |
| 122 | |
| 123 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| 124 | }else{ |
| 125 | buf->st_mode = S_IFLNK; |
| 126 | buf->st_size = tlen; |
| 127 | } |
| 128 | |
| 129 | ull.LowPart = attr.ftLastWriteTime.dwLowDateTime; |
| 130 | ull.HighPart = attr.ftLastWriteTime.dwHighDateTime; |
| 131 | buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL; |
| 132 | } |
| 133 | return !rc; |
| @@ -126,11 +136,11 @@ | |
| 136 | /* |
| 137 | ** Fill stat buf with information received from win32_lstat(). |
| 138 | ** If a symbolic link is found, follow it and return information about |
| 139 | ** the target, repeating until an actual target is found. |
| 140 | ** Limit the number of loop iterations so as to avoid an infinite loop |
| 141 | ** due to circular links. This should never happen because |
| 142 | ** GetFinalPathNameByHandleW() should always preclude that need, but being |
| 143 | ** prepared to loop seems prudent, or at least not harmful. |
| 144 | ** Returns 0 on success, 1 on failure. |
| 145 | */ |
| 146 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| @@ -137,40 +147,44 @@ | |
| 147 | int rc; |
| 148 | HANDLE file; |
| 149 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 150 | DWORD len; |
| 151 | int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ |
| 152 | |
| 153 | while (iterationsRemaining-- > 0){ |
| 154 | rc = win32_lstat(zFilename, buf); |
| 155 | |
| 156 | /* exit on error or not link */ |
| 157 | if( (rc != 0) || (buf->st_mode != S_IFLNK) ) |
| 158 | break; |
| 159 | |
| 160 | /* it is a link, so open the linked file */ |
| 161 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
| 162 | if( (file == NULL) || (file == INVALID_HANDLE_VALUE) ){ |
| 163 | rc = 1; |
| 164 | break; |
| 165 | } |
| 166 | |
| 167 | /* get the final path name and close the handle */ |
| 168 | if( isVistaOrLater() ){ |
| 169 | len = getFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 170 | }else{ |
| 171 | len = -1; |
| 172 | } |
| 173 | CloseHandle(file); |
| 174 | |
| 175 | /* if any problems getting the final path name error so exit */ |
| 176 | if( (len <= 0) || (len > LINK_BUFFER_SIZE - 1) ){ |
| 177 | rc = 1; |
| 178 | break; |
| 179 | } |
| 180 | |
| 181 | /* prepare to try again just in case we have a chain to follow */ |
| 182 | /* this shouldn't happen, but just trying to be safe */ |
| 183 | zFilename = nextFilename; |
| 184 | } |
| 185 | |
| 186 | return rc; |
| 187 | } |
| 188 | |
| 189 | /* |
| 190 | ** An implementation of a posix-like readlink function for win32. |
| @@ -178,36 +192,36 @@ | |
| 192 | ** Returns the length of the link copied to buf on success, -1 on failure. |
| 193 | */ |
| 194 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 195 | /* assume we're going to fail */ |
| 196 | ssize_t rv = -1; |
| 197 | |
| 198 | /* does path reference a reparse point? */ |
| 199 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 200 | int rc = GetFileAttributesEx(path, GetFileExInfoStandard, &attr); |
| 201 | if( rc && (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ){ |
| 202 | |
| 203 | /* since it is a reparse point, open it */ |
| 204 | HANDLE file = CreateFile(path, FILE_READ_EA, 0, NULL, OPEN_EXISTING, |
| 205 | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 206 | if( (file != NULL) && (file != INVALID_HANDLE_VALUE) ){ |
| 207 | |
| 208 | /* use DeviceIoControl to get the reparse point data */ |
| 209 | |
| 210 | int data_size = sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t); |
| 211 | REPARSE_DATA_BUFFER* data = fossil_malloc(data_size); |
| 212 | DWORD data_used; |
| 213 | |
| 214 | data->ReparseTag = IO_REPARSE_TAG_SYMLINK; |
| 215 | data->ReparseDataLength = 0; |
| 216 | data->Reserved = 0; |
| 217 | |
| 218 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 219 | data, data_size, &data_used, NULL); |
| 220 | |
| 221 | /* did the reparse point data fit into the desired buffer? */ |
| 222 | if( rc && (data_used < data_size) ){ |
| 223 | /* it fit, so setup the print name for further processing */ |
| 224 | USHORT |
| 225 | offset = data->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), |
| 226 | length = data->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t); |
| 227 | char *temp; |
| @@ -214,18 +228,18 @@ | |
| 228 | data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; |
| 229 | |
| 230 | /* convert the filename to utf8, copy it, and discard the converted copy */ |
| 231 | temp = fossil_filename_to_utf8(data->SymbolicLinkReparseBuffer.PathBuffer + offset); |
| 232 | rv = strlen(temp); |
| 233 | if( rv >= bufsiz ) |
| 234 | rv = bufsiz; |
| 235 | memcpy(buf, temp, rv); |
| 236 | fossil_filename_free(temp); |
| 237 | } |
| 238 | |
| 239 | fossil_free(data); |
| 240 | |
| 241 | /* all done, close the reparse point */ |
| 242 | CloseHandle(file); |
| 243 | } |
| 244 | } |
| 245 | |
| @@ -242,12 +256,12 @@ | |
| 256 | ** Returns 0 on success, 1 on failure. |
| 257 | */ |
| 258 | int win32_unlink_rmdir(const wchar_t *zFilename){ |
| 259 | int rc = 0; |
| 260 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 261 | if( GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr) ){ |
| 262 | if( (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY ) |
| 263 | rc = RemoveDirectoryW(zFilename); |
| 264 | else |
| 265 | rc = DeleteFileW(zFilename); |
| 266 | } |
| 267 | return !rc; |
| @@ -266,33 +280,36 @@ | |
| 280 | fossilStat stat; |
| 281 | int created = 0; |
| 282 | DWORD flags = 0; |
| 283 | wchar_t *zMbcs, *zMbcsOld; |
| 284 | |
| 285 | /* does oldpath exist? is it a dir or a file? */ |
| 286 | zMbcsOld = fossil_utf8_to_filename(oldpath); |
| 287 | if( win32_stat(zMbcsOld, &stat) == 0 ){ |
| 288 | if( stat.st_mode == S_IFDIR ){ |
| 289 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* remove newpath before creating the symlink */ |
| 294 | zMbcs = fossil_utf8_to_filename(newpath); |
| 295 | win32_unlink_rmdir(zMbcs); |
| 296 | if( isVistaOrLater() ){ |
| 297 | created = createSymbolicLinkW(zMbcs, zMbcsOld, flags); |
| 298 | } |
| 299 | fossil_filename_free(zMbcs); |
| 300 | fossil_filename_free(zMbcsOld); |
| 301 | |
| 302 | /* if the symlink was not created, create a plain text file */ |
| 303 | if( !created ){ |
| 304 | Blob content; |
| 305 | blob_set(&content, oldpath); |
| 306 | blob_write_to_file(&content, newpath); |
| 307 | blob_reset(&content); |
| 308 | created = 1; |
| 309 | } |
| 310 | |
| 311 | return !created; |
| 312 | } |
| 313 | |
| 314 | /* |
| 315 | ** Given a pathname to a file, return true if: |
| @@ -305,12 +322,13 @@ | |
| 322 | int changed = 0; |
| 323 | wchar_t* zMbcs; |
| 324 | fossilStat lstat_buf, stat_buf; |
| 325 | WIN32_FILE_ATTRIBUTE_DATA lstat_attr; |
| 326 | zMbcs = fossil_utf8_to_filename(zName); |
| 327 | if( win32_stat(zMbcs, &stat_buf) != 0 ){ |
| 328 | stat_buf.st_mode = S_IFREG; |
| 329 | } |
| 330 | changed = |
| 331 | (win32_lstat(zMbcs, &lstat_buf) == 0) && |
| 332 | (lstat_buf.st_mode == S_IFLNK) && |
| 333 | GetFileAttributesExW(zMbcs, GetFileExInfoStandard, &lstat_attr) && |
| 334 | ((stat_buf.st_mode == S_IFDIR) != ((lstat_attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)); |
| @@ -337,65 +355,69 @@ | |
| 355 | DWORD fullLength; |
| 356 | wchar_t volName[MAX_PATH+1]; |
| 357 | DWORD fsFlags; |
| 358 | |
| 359 | /* symlinks only supported on vista or greater */ |
| 360 | if( !isVistaOrLater() ){ |
| 361 | return 0; |
| 362 | } |
| 363 | |
| 364 | /* next we need to check to see if the privilege is available */ |
| 365 | |
| 366 | /* can't check privilege if we can't lookup its value */ |
| 367 | if( !LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid) ){ |
| 368 | return 0; |
| 369 | } |
| 370 | |
| 371 | /* can't check privilege if we can't open the process token */ |
| 372 | process = GetCurrentProcess(); |
| 373 | if( !OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token) ){ |
| 374 | return 0; |
| 375 | } |
| 376 | |
| 377 | /* by this point, we have a process token and the privilege value */ |
| 378 | /* try to enable the privilege then close the token */ |
| 379 | |
| 380 | tp.PrivilegeCount = 1; |
| 381 | tp.Privileges[0].Luid = luid; |
| 382 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
| 383 | |
| 384 | AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); |
| 385 | status = GetLastError(); |
| 386 | |
| 387 | CloseHandle(token); |
| 388 | |
| 389 | /* any error means we failed to enable the privilege, symlinks not supported */ |
| 390 | if( status != ERROR_SUCCESS ){ |
| 391 | return 0; |
| 392 | } |
| 393 | |
| 394 | /* assume no support for symlinks */ |
| 395 | success = 0; |
| 396 | |
| 397 | pFilename = fossil_utf8_to_filename(zFilename); |
| 398 | |
| 399 | /* given the filename we're interested in, symlinks are supported if */ |
| 400 | /* 1. we can get the full name of the path from the given path */ |
| 401 | fullLength = GetFullPathNameW(pFilename, sizeof(fullName), fullName, NULL); |
| 402 | if( (fullLength > 0) && (fullLength < sizeof(fullName)) ){ |
| 403 | /* 2. we can get the volume path name from the full name */ |
| 404 | if( GetVolumePathNameW(fullName, volName, sizeof(volName)) ){ |
| 405 | /* 3. we can get volume information from the volume path name */ |
| 406 | if( GetVolumeInformationW(volName, NULL, 0, NULL, NULL, &fsFlags, NULL, 0) ){ |
| 407 | /* 4. the given volume support reparse points */ |
| 408 | if( fsFlags & FILE_SUPPORTS_REPARSE_POINTS ){ |
| 409 | /* all four conditions were true, so we support symlinks; success! */ |
| 410 | success = 1; |
| 411 | } |
| 412 | } |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | fossil_filename_free(pFilename); |
| 417 | |
| 418 | return success; |
| 419 | } |
| 420 | |
| 421 | /* |
| 422 | ** Wrapper around the access() system call. This code was copied from Tcl |
| 423 | ** 8.6 and then modified. |
| @@ -587,11 +609,13 @@ | |
| 609 | * Unable to perform access check. |
| 610 | */ |
| 611 | |
| 612 | rc = -1; goto done; |
| 613 | } |
| 614 | if( !accessYesNo ){ |
| 615 | rc = -1; |
| 616 | } |
| 617 | |
| 618 | done: |
| 619 | |
| 620 | if( hToken != NULL ){ |
| 621 | CloseHandle(hToken); |
| 622 |
+85
-61
| --- src/winfile.c | ||
| +++ src/winfile.c | ||
| @@ -35,13 +35,13 @@ | ||
| 35 | 35 | #ifndef LABEL_SECURITY_INFORMATION |
| 36 | 36 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 37 | 37 | #endif |
| 38 | 38 | |
| 39 | 39 | #if defined(__MSVCRT__) |
| 40 | -/* TODO: determine those dynamically. */ | |
| 41 | -WINBASEAPI DWORD WINAPI GetFinalPathNameByHandleW (HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); | |
| 42 | -WINBASEAPI BOOLEAN APIENTRY CreateSymbolicLinkW (LPCWSTR lpSymlinkFileName, LPCWSTR lpTargetFileName, DWORD dwFlags); | |
| 40 | +static HANDLE dllhandle = NULL; | |
| 41 | +static DWORD WINAPI (*getFinalPathNameByHandleW) (HANDLE, LPWSTR, DWORD, DWORD) = NULL; | |
| 42 | +static BOOLEAN APIENTRY (*createSymbolicLinkW) (LPCWSTR, LPCWSTR, DWORD) = NULL; | |
| 43 | 43 | #endif |
| 44 | 44 | |
| 45 | 45 | /* a couple defines to make the borrowed struct below compile */ |
| 46 | 46 | #ifndef _ANONYMOUS_UNION |
| 47 | 47 | # define _ANONYMOUS_UNION |
| @@ -79,10 +79,20 @@ | ||
| 79 | 79 | } GenericReparseBuffer; |
| 80 | 80 | } DUMMYUNIONNAME; |
| 81 | 81 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 82 | 82 | |
| 83 | 83 | #define LINK_BUFFER_SIZE 1024 |
| 84 | + | |
| 85 | +static int isVistaOrLater(){ | |
| 86 | + if( !dllhandle ){ | |
| 87 | + HANDLE h = LoadLibraryW(L"KERNEL32"); | |
| 88 | + createSymbolicLinkW = (BOOLEAN APIENTRY (*) (LPCWSTR, LPCWSTR, DWORD)) GetProcAddress(h, "CreateSymbolicLinkW"); | |
| 89 | + getFinalPathNameByHandleW = (DWORD WINAPI (*) (HANDLE, LPWSTR, DWORD, DWORD)) GetProcAddress(h, "GetFinalPathNameByHandleW"); | |
| 90 | + dllhandle = h; | |
| 91 | + } | |
| 92 | + return createSymbolicLinkW != NULL; | |
| 93 | +} | |
| 84 | 94 | |
| 85 | 95 | /* |
| 86 | 96 | ** Fill stat buf with information received from GetFileAttributesExW(). |
| 87 | 97 | ** Does not follow symbolic links, returning instead information about |
| 88 | 98 | ** the link itself. |
| @@ -91,33 +101,33 @@ | ||
| 91 | 101 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 92 | 102 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 93 | 103 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 94 | 104 | if( rc ){ |
| 95 | 105 | ssize_t tlen = 0; /* assume it is not a symbolic link */ |
| 96 | - | |
| 106 | + | |
| 97 | 107 | /* if it is a reparse point it *might* be a symbolic link */ |
| 98 | 108 | /* so defer to win32_readlink to actually check */ |
| 99 | - if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){ | |
| 109 | + if( attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ){ | |
| 100 | 110 | char *tname = fossil_filename_to_utf8(zFilename); |
| 101 | 111 | char tlink[LINK_BUFFER_SIZE]; |
| 102 | 112 | tlen = win32_readlink(tname, tlink, sizeof(tlink)); |
| 103 | 113 | fossil_filename_free(tname); |
| 104 | 114 | } |
| 105 | - | |
| 115 | + | |
| 106 | 116 | ULARGE_INTEGER ull; |
| 107 | 117 | |
| 108 | 118 | /* if a link was retrieved, it is a symlink, otherwise a dir or file */ |
| 109 | - if (tlen == 0){ | |
| 119 | + if( tlen == 0 ){ | |
| 110 | 120 | buf->st_mode = ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 111 | 121 | S_IFDIR : S_IFREG); |
| 112 | - | |
| 122 | + | |
| 113 | 123 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| 114 | 124 | }else{ |
| 115 | 125 | buf->st_mode = S_IFLNK; |
| 116 | 126 | buf->st_size = tlen; |
| 117 | 127 | } |
| 118 | - | |
| 128 | + | |
| 119 | 129 | ull.LowPart = attr.ftLastWriteTime.dwLowDateTime; |
| 120 | 130 | ull.HighPart = attr.ftLastWriteTime.dwHighDateTime; |
| 121 | 131 | buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL; |
| 122 | 132 | } |
| 123 | 133 | return !rc; |
| @@ -126,11 +136,11 @@ | ||
| 126 | 136 | /* |
| 127 | 137 | ** Fill stat buf with information received from win32_lstat(). |
| 128 | 138 | ** If a symbolic link is found, follow it and return information about |
| 129 | 139 | ** the target, repeating until an actual target is found. |
| 130 | 140 | ** Limit the number of loop iterations so as to avoid an infinite loop |
| 131 | -** due to circular links. This should never happen because | |
| 141 | +** due to circular links. This should never happen because | |
| 132 | 142 | ** GetFinalPathNameByHandleW() should always preclude that need, but being |
| 133 | 143 | ** prepared to loop seems prudent, or at least not harmful. |
| 134 | 144 | ** Returns 0 on success, 1 on failure. |
| 135 | 145 | */ |
| 136 | 146 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| @@ -137,40 +147,44 @@ | ||
| 137 | 147 | int rc; |
| 138 | 148 | HANDLE file; |
| 139 | 149 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 140 | 150 | DWORD len; |
| 141 | 151 | int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ |
| 142 | - | |
| 152 | + | |
| 143 | 153 | while (iterationsRemaining-- > 0){ |
| 144 | 154 | rc = win32_lstat(zFilename, buf); |
| 145 | 155 | |
| 146 | 156 | /* exit on error or not link */ |
| 147 | - if ((rc != 0) || (buf->st_mode != S_IFLNK)) | |
| 157 | + if( (rc != 0) || (buf->st_mode != S_IFLNK) ) | |
| 148 | 158 | break; |
| 149 | 159 | |
| 150 | - /* it is a link, so open the linked file */ | |
| 160 | + /* it is a link, so open the linked file */ | |
| 151 | 161 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
| 152 | - if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ | |
| 162 | + if( (file == NULL) || (file == INVALID_HANDLE_VALUE) ){ | |
| 153 | 163 | rc = 1; |
| 154 | 164 | break; |
| 155 | 165 | } |
| 156 | 166 | |
| 157 | 167 | /* get the final path name and close the handle */ |
| 158 | - len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); | |
| 168 | + if( isVistaOrLater() ){ | |
| 169 | + len = getFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); | |
| 170 | + }else{ | |
| 171 | + len = -1; | |
| 172 | + } | |
| 159 | 173 | CloseHandle(file); |
| 160 | - | |
| 174 | + | |
| 161 | 175 | /* if any problems getting the final path name error so exit */ |
| 162 | - if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ | |
| 176 | + if( (len <= 0) || (len > LINK_BUFFER_SIZE - 1) ){ | |
| 163 | 177 | rc = 1; |
| 164 | 178 | break; |
| 165 | 179 | } |
| 166 | - | |
| 180 | + | |
| 167 | 181 | /* prepare to try again just in case we have a chain to follow */ |
| 168 | 182 | /* this shouldn't happen, but just trying to be safe */ |
| 169 | 183 | zFilename = nextFilename; |
| 170 | 184 | } |
| 171 | - | |
| 185 | + | |
| 172 | 186 | return rc; |
| 173 | 187 | } |
| 174 | 188 | |
| 175 | 189 | /* |
| 176 | 190 | ** An implementation of a posix-like readlink function for win32. |
| @@ -178,36 +192,36 @@ | ||
| 178 | 192 | ** Returns the length of the link copied to buf on success, -1 on failure. |
| 179 | 193 | */ |
| 180 | 194 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 181 | 195 | /* assume we're going to fail */ |
| 182 | 196 | ssize_t rv = -1; |
| 183 | - | |
| 197 | + | |
| 184 | 198 | /* does path reference a reparse point? */ |
| 185 | 199 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 186 | 200 | int rc = GetFileAttributesEx(path, GetFileExInfoStandard, &attr); |
| 187 | - if (rc && (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)){ | |
| 188 | - | |
| 201 | + if( rc && (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ){ | |
| 202 | + | |
| 189 | 203 | /* since it is a reparse point, open it */ |
| 190 | - HANDLE file = CreateFile(path, FILE_READ_EA, 0, NULL, OPEN_EXISTING, | |
| 191 | - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); | |
| 192 | - if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ | |
| 204 | + HANDLE file = CreateFile(path, FILE_READ_EA, 0, NULL, OPEN_EXISTING, | |
| 205 | + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); | |
| 206 | + if( (file != NULL) && (file != INVALID_HANDLE_VALUE) ){ | |
| 193 | 207 | |
| 194 | 208 | /* use DeviceIoControl to get the reparse point data */ |
| 195 | - | |
| 209 | + | |
| 196 | 210 | int data_size = sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t); |
| 197 | 211 | REPARSE_DATA_BUFFER* data = fossil_malloc(data_size); |
| 198 | 212 | DWORD data_used; |
| 199 | - | |
| 213 | + | |
| 200 | 214 | data->ReparseTag = IO_REPARSE_TAG_SYMLINK; |
| 201 | 215 | data->ReparseDataLength = 0; |
| 202 | 216 | data->Reserved = 0; |
| 203 | - | |
| 217 | + | |
| 204 | 218 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 205 | 219 | data, data_size, &data_used, NULL); |
| 206 | 220 | |
| 207 | 221 | /* did the reparse point data fit into the desired buffer? */ |
| 208 | - if (rc && (data_used < data_size)){ | |
| 222 | + if( rc && (data_used < data_size) ){ | |
| 209 | 223 | /* it fit, so setup the print name for further processing */ |
| 210 | 224 | USHORT |
| 211 | 225 | offset = data->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), |
| 212 | 226 | length = data->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t); |
| 213 | 227 | char *temp; |
| @@ -214,18 +228,18 @@ | ||
| 214 | 228 | data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; |
| 215 | 229 | |
| 216 | 230 | /* convert the filename to utf8, copy it, and discard the converted copy */ |
| 217 | 231 | temp = fossil_filename_to_utf8(data->SymbolicLinkReparseBuffer.PathBuffer + offset); |
| 218 | 232 | rv = strlen(temp); |
| 219 | - if (rv >= bufsiz) | |
| 233 | + if( rv >= bufsiz ) | |
| 220 | 234 | rv = bufsiz; |
| 221 | 235 | memcpy(buf, temp, rv); |
| 222 | 236 | fossil_filename_free(temp); |
| 223 | 237 | } |
| 224 | - | |
| 238 | + | |
| 225 | 239 | fossil_free(data); |
| 226 | - | |
| 240 | + | |
| 227 | 241 | /* all done, close the reparse point */ |
| 228 | 242 | CloseHandle(file); |
| 229 | 243 | } |
| 230 | 244 | } |
| 231 | 245 | |
| @@ -242,12 +256,12 @@ | ||
| 242 | 256 | ** Returns 0 on success, 1 on failure. |
| 243 | 257 | */ |
| 244 | 258 | int win32_unlink_rmdir(const wchar_t *zFilename){ |
| 245 | 259 | int rc = 0; |
| 246 | 260 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 247 | - if (GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr)){ | |
| 248 | - if ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) | |
| 261 | + if( GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr) ){ | |
| 262 | + if( (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY ) | |
| 249 | 263 | rc = RemoveDirectoryW(zFilename); |
| 250 | 264 | else |
| 251 | 265 | rc = DeleteFileW(zFilename); |
| 252 | 266 | } |
| 253 | 267 | return !rc; |
| @@ -266,33 +280,36 @@ | ||
| 266 | 280 | fossilStat stat; |
| 267 | 281 | int created = 0; |
| 268 | 282 | DWORD flags = 0; |
| 269 | 283 | wchar_t *zMbcs, *zMbcsOld; |
| 270 | 284 | |
| 271 | - /* does oldpath exist? is it a dir or a file? */ | |
| 285 | + /* does oldpath exist? is it a dir or a file? */ | |
| 272 | 286 | zMbcsOld = fossil_utf8_to_filename(oldpath); |
| 273 | - if (win32_stat(zMbcsOld, &stat) == 0){ | |
| 274 | - if (stat.st_mode == S_IFDIR) | |
| 287 | + if( win32_stat(zMbcsOld, &stat) == 0 ){ | |
| 288 | + if( stat.st_mode == S_IFDIR ){ | |
| 275 | 289 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 290 | + } | |
| 276 | 291 | } |
| 277 | 292 | |
| 278 | 293 | /* remove newpath before creating the symlink */ |
| 279 | 294 | zMbcs = fossil_utf8_to_filename(newpath); |
| 280 | 295 | win32_unlink_rmdir(zMbcs); |
| 281 | - created = CreateSymbolicLinkW(zMbcs, zMbcsOld, flags); | |
| 296 | + if( isVistaOrLater() ){ | |
| 297 | + created = createSymbolicLinkW(zMbcs, zMbcsOld, flags); | |
| 298 | + } | |
| 282 | 299 | fossil_filename_free(zMbcs); |
| 283 | 300 | fossil_filename_free(zMbcsOld); |
| 284 | 301 | |
| 285 | 302 | /* if the symlink was not created, create a plain text file */ |
| 286 | - if (!created){ | |
| 303 | + if( !created ){ | |
| 287 | 304 | Blob content; |
| 288 | 305 | blob_set(&content, oldpath); |
| 289 | 306 | blob_write_to_file(&content, newpath); |
| 290 | 307 | blob_reset(&content); |
| 291 | 308 | created = 1; |
| 292 | 309 | } |
| 293 | - | |
| 310 | + | |
| 294 | 311 | return !created; |
| 295 | 312 | } |
| 296 | 313 | |
| 297 | 314 | /* |
| 298 | 315 | ** Given a pathname to a file, return true if: |
| @@ -305,12 +322,13 @@ | ||
| 305 | 322 | int changed = 0; |
| 306 | 323 | wchar_t* zMbcs; |
| 307 | 324 | fossilStat lstat_buf, stat_buf; |
| 308 | 325 | WIN32_FILE_ATTRIBUTE_DATA lstat_attr; |
| 309 | 326 | zMbcs = fossil_utf8_to_filename(zName); |
| 310 | - if (win32_stat(zMbcs, &stat_buf) != 0) | |
| 327 | + if( win32_stat(zMbcs, &stat_buf) != 0 ){ | |
| 311 | 328 | stat_buf.st_mode = S_IFREG; |
| 329 | + } | |
| 312 | 330 | changed = |
| 313 | 331 | (win32_lstat(zMbcs, &lstat_buf) == 0) && |
| 314 | 332 | (lstat_buf.st_mode == S_IFLNK) && |
| 315 | 333 | GetFileAttributesExW(zMbcs, GetFileExInfoStandard, &lstat_attr) && |
| 316 | 334 | ((stat_buf.st_mode == S_IFDIR) != ((lstat_attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)); |
| @@ -337,65 +355,69 @@ | ||
| 337 | 355 | DWORD fullLength; |
| 338 | 356 | wchar_t volName[MAX_PATH+1]; |
| 339 | 357 | DWORD fsFlags; |
| 340 | 358 | |
| 341 | 359 | /* symlinks only supported on vista or greater */ |
| 342 | - /* if (!IsWindowsVistaOrGreater()) // TODO: make it work on MinGW | |
| 343 | - return 0; */ | |
| 360 | + if( !isVistaOrLater() ){ | |
| 361 | + return 0; | |
| 362 | + } | |
| 344 | 363 | |
| 345 | 364 | /* next we need to check to see if the privilege is available */ |
| 346 | - | |
| 365 | + | |
| 347 | 366 | /* can't check privilege if we can't lookup its value */ |
| 348 | - if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid)) | |
| 367 | + if( !LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid) ){ | |
| 349 | 368 | return 0; |
| 350 | - | |
| 369 | + } | |
| 370 | + | |
| 351 | 371 | /* can't check privilege if we can't open the process token */ |
| 352 | 372 | process = GetCurrentProcess(); |
| 353 | - if (!OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token)) | |
| 373 | + if( !OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token) ){ | |
| 354 | 374 | return 0; |
| 355 | - | |
| 375 | + } | |
| 376 | + | |
| 356 | 377 | /* by this point, we have a process token and the privilege value */ |
| 357 | 378 | /* try to enable the privilege then close the token */ |
| 358 | - | |
| 379 | + | |
| 359 | 380 | tp.PrivilegeCount = 1; |
| 360 | 381 | tp.Privileges[0].Luid = luid; |
| 361 | 382 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
| 362 | - | |
| 383 | + | |
| 363 | 384 | AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); |
| 364 | 385 | status = GetLastError(); |
| 365 | - | |
| 386 | + | |
| 366 | 387 | CloseHandle(token); |
| 367 | 388 | |
| 368 | 389 | /* any error means we failed to enable the privilege, symlinks not supported */ |
| 369 | - if (status != ERROR_SUCCESS) | |
| 390 | + if( status != ERROR_SUCCESS ){ | |
| 370 | 391 | return 0; |
| 392 | + } | |
| 371 | 393 | |
| 372 | 394 | /* assume no support for symlinks */ |
| 373 | 395 | success = 0; |
| 374 | - | |
| 396 | + | |
| 375 | 397 | pFilename = fossil_utf8_to_filename(zFilename); |
| 376 | 398 | |
| 377 | 399 | /* given the filename we're interested in, symlinks are supported if */ |
| 378 | 400 | /* 1. we can get the full name of the path from the given path */ |
| 379 | 401 | fullLength = GetFullPathNameW(pFilename, sizeof(fullName), fullName, NULL); |
| 380 | - if ((fullLength > 0) && (fullLength < sizeof(fullName))){ | |
| 402 | + if( (fullLength > 0) && (fullLength < sizeof(fullName)) ){ | |
| 381 | 403 | /* 2. we can get the volume path name from the full name */ |
| 382 | - if (GetVolumePathNameW(fullName, volName, sizeof(volName))){ | |
| 404 | + if( GetVolumePathNameW(fullName, volName, sizeof(volName)) ){ | |
| 383 | 405 | /* 3. we can get volume information from the volume path name */ |
| 384 | - if (GetVolumeInformationW(volName, NULL, 0, NULL, NULL, &fsFlags, NULL, 0)){ | |
| 406 | + if( GetVolumeInformationW(volName, NULL, 0, NULL, NULL, &fsFlags, NULL, 0) ){ | |
| 385 | 407 | /* 4. the given volume support reparse points */ |
| 386 | - if (fsFlags & FILE_SUPPORTS_REPARSE_POINTS){ | |
| 408 | + if( fsFlags & FILE_SUPPORTS_REPARSE_POINTS ){ | |
| 387 | 409 | /* all four conditions were true, so we support symlinks; success! */ |
| 388 | 410 | success = 1; |
| 389 | 411 | } |
| 390 | 412 | } |
| 391 | 413 | } |
| 392 | 414 | } |
| 393 | - | |
| 415 | + | |
| 394 | 416 | fossil_filename_free(pFilename); |
| 395 | - | |
| 396 | - return success; | |
| 417 | + | |
| 418 | + return success; | |
| 397 | 419 | } |
| 398 | 420 | |
| 399 | 421 | /* |
| 400 | 422 | ** Wrapper around the access() system call. This code was copied from Tcl |
| 401 | 423 | ** 8.6 and then modified. |
| @@ -587,11 +609,13 @@ | ||
| 587 | 609 | * Unable to perform access check. |
| 588 | 610 | */ |
| 589 | 611 | |
| 590 | 612 | rc = -1; goto done; |
| 591 | 613 | } |
| 592 | - if( !accessYesNo ) rc = -1; | |
| 614 | + if( !accessYesNo ){ | |
| 615 | + rc = -1; | |
| 616 | + } | |
| 593 | 617 | |
| 594 | 618 | done: |
| 595 | 619 | |
| 596 | 620 | if( hToken != NULL ){ |
| 597 | 621 | CloseHandle(hToken); |
| 598 | 622 |
| --- src/winfile.c | |
| +++ src/winfile.c | |
| @@ -35,13 +35,13 @@ | |
| 35 | #ifndef LABEL_SECURITY_INFORMATION |
| 36 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 37 | #endif |
| 38 | |
| 39 | #if defined(__MSVCRT__) |
| 40 | /* TODO: determine those dynamically. */ |
| 41 | WINBASEAPI DWORD WINAPI GetFinalPathNameByHandleW (HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); |
| 42 | WINBASEAPI BOOLEAN APIENTRY CreateSymbolicLinkW (LPCWSTR lpSymlinkFileName, LPCWSTR lpTargetFileName, DWORD dwFlags); |
| 43 | #endif |
| 44 | |
| 45 | /* a couple defines to make the borrowed struct below compile */ |
| 46 | #ifndef _ANONYMOUS_UNION |
| 47 | # define _ANONYMOUS_UNION |
| @@ -79,10 +79,20 @@ | |
| 79 | } GenericReparseBuffer; |
| 80 | } DUMMYUNIONNAME; |
| 81 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 82 | |
| 83 | #define LINK_BUFFER_SIZE 1024 |
| 84 | |
| 85 | /* |
| 86 | ** Fill stat buf with information received from GetFileAttributesExW(). |
| 87 | ** Does not follow symbolic links, returning instead information about |
| 88 | ** the link itself. |
| @@ -91,33 +101,33 @@ | |
| 91 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 92 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 93 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 94 | if( rc ){ |
| 95 | ssize_t tlen = 0; /* assume it is not a symbolic link */ |
| 96 | |
| 97 | /* if it is a reparse point it *might* be a symbolic link */ |
| 98 | /* so defer to win32_readlink to actually check */ |
| 99 | if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){ |
| 100 | char *tname = fossil_filename_to_utf8(zFilename); |
| 101 | char tlink[LINK_BUFFER_SIZE]; |
| 102 | tlen = win32_readlink(tname, tlink, sizeof(tlink)); |
| 103 | fossil_filename_free(tname); |
| 104 | } |
| 105 | |
| 106 | ULARGE_INTEGER ull; |
| 107 | |
| 108 | /* if a link was retrieved, it is a symlink, otherwise a dir or file */ |
| 109 | if (tlen == 0){ |
| 110 | buf->st_mode = ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 111 | S_IFDIR : S_IFREG); |
| 112 | |
| 113 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| 114 | }else{ |
| 115 | buf->st_mode = S_IFLNK; |
| 116 | buf->st_size = tlen; |
| 117 | } |
| 118 | |
| 119 | ull.LowPart = attr.ftLastWriteTime.dwLowDateTime; |
| 120 | ull.HighPart = attr.ftLastWriteTime.dwHighDateTime; |
| 121 | buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL; |
| 122 | } |
| 123 | return !rc; |
| @@ -126,11 +136,11 @@ | |
| 126 | /* |
| 127 | ** Fill stat buf with information received from win32_lstat(). |
| 128 | ** If a symbolic link is found, follow it and return information about |
| 129 | ** the target, repeating until an actual target is found. |
| 130 | ** Limit the number of loop iterations so as to avoid an infinite loop |
| 131 | ** due to circular links. This should never happen because |
| 132 | ** GetFinalPathNameByHandleW() should always preclude that need, but being |
| 133 | ** prepared to loop seems prudent, or at least not harmful. |
| 134 | ** Returns 0 on success, 1 on failure. |
| 135 | */ |
| 136 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| @@ -137,40 +147,44 @@ | |
| 137 | int rc; |
| 138 | HANDLE file; |
| 139 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 140 | DWORD len; |
| 141 | int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ |
| 142 | |
| 143 | while (iterationsRemaining-- > 0){ |
| 144 | rc = win32_lstat(zFilename, buf); |
| 145 | |
| 146 | /* exit on error or not link */ |
| 147 | if ((rc != 0) || (buf->st_mode != S_IFLNK)) |
| 148 | break; |
| 149 | |
| 150 | /* it is a link, so open the linked file */ |
| 151 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
| 152 | if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ |
| 153 | rc = 1; |
| 154 | break; |
| 155 | } |
| 156 | |
| 157 | /* get the final path name and close the handle */ |
| 158 | len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 159 | CloseHandle(file); |
| 160 | |
| 161 | /* if any problems getting the final path name error so exit */ |
| 162 | if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ |
| 163 | rc = 1; |
| 164 | break; |
| 165 | } |
| 166 | |
| 167 | /* prepare to try again just in case we have a chain to follow */ |
| 168 | /* this shouldn't happen, but just trying to be safe */ |
| 169 | zFilename = nextFilename; |
| 170 | } |
| 171 | |
| 172 | return rc; |
| 173 | } |
| 174 | |
| 175 | /* |
| 176 | ** An implementation of a posix-like readlink function for win32. |
| @@ -178,36 +192,36 @@ | |
| 178 | ** Returns the length of the link copied to buf on success, -1 on failure. |
| 179 | */ |
| 180 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 181 | /* assume we're going to fail */ |
| 182 | ssize_t rv = -1; |
| 183 | |
| 184 | /* does path reference a reparse point? */ |
| 185 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 186 | int rc = GetFileAttributesEx(path, GetFileExInfoStandard, &attr); |
| 187 | if (rc && (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)){ |
| 188 | |
| 189 | /* since it is a reparse point, open it */ |
| 190 | HANDLE file = CreateFile(path, FILE_READ_EA, 0, NULL, OPEN_EXISTING, |
| 191 | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 192 | if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ |
| 193 | |
| 194 | /* use DeviceIoControl to get the reparse point data */ |
| 195 | |
| 196 | int data_size = sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t); |
| 197 | REPARSE_DATA_BUFFER* data = fossil_malloc(data_size); |
| 198 | DWORD data_used; |
| 199 | |
| 200 | data->ReparseTag = IO_REPARSE_TAG_SYMLINK; |
| 201 | data->ReparseDataLength = 0; |
| 202 | data->Reserved = 0; |
| 203 | |
| 204 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 205 | data, data_size, &data_used, NULL); |
| 206 | |
| 207 | /* did the reparse point data fit into the desired buffer? */ |
| 208 | if (rc && (data_used < data_size)){ |
| 209 | /* it fit, so setup the print name for further processing */ |
| 210 | USHORT |
| 211 | offset = data->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), |
| 212 | length = data->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t); |
| 213 | char *temp; |
| @@ -214,18 +228,18 @@ | |
| 214 | data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; |
| 215 | |
| 216 | /* convert the filename to utf8, copy it, and discard the converted copy */ |
| 217 | temp = fossil_filename_to_utf8(data->SymbolicLinkReparseBuffer.PathBuffer + offset); |
| 218 | rv = strlen(temp); |
| 219 | if (rv >= bufsiz) |
| 220 | rv = bufsiz; |
| 221 | memcpy(buf, temp, rv); |
| 222 | fossil_filename_free(temp); |
| 223 | } |
| 224 | |
| 225 | fossil_free(data); |
| 226 | |
| 227 | /* all done, close the reparse point */ |
| 228 | CloseHandle(file); |
| 229 | } |
| 230 | } |
| 231 | |
| @@ -242,12 +256,12 @@ | |
| 242 | ** Returns 0 on success, 1 on failure. |
| 243 | */ |
| 244 | int win32_unlink_rmdir(const wchar_t *zFilename){ |
| 245 | int rc = 0; |
| 246 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 247 | if (GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr)){ |
| 248 | if ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) |
| 249 | rc = RemoveDirectoryW(zFilename); |
| 250 | else |
| 251 | rc = DeleteFileW(zFilename); |
| 252 | } |
| 253 | return !rc; |
| @@ -266,33 +280,36 @@ | |
| 266 | fossilStat stat; |
| 267 | int created = 0; |
| 268 | DWORD flags = 0; |
| 269 | wchar_t *zMbcs, *zMbcsOld; |
| 270 | |
| 271 | /* does oldpath exist? is it a dir or a file? */ |
| 272 | zMbcsOld = fossil_utf8_to_filename(oldpath); |
| 273 | if (win32_stat(zMbcsOld, &stat) == 0){ |
| 274 | if (stat.st_mode == S_IFDIR) |
| 275 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 276 | } |
| 277 | |
| 278 | /* remove newpath before creating the symlink */ |
| 279 | zMbcs = fossil_utf8_to_filename(newpath); |
| 280 | win32_unlink_rmdir(zMbcs); |
| 281 | created = CreateSymbolicLinkW(zMbcs, zMbcsOld, flags); |
| 282 | fossil_filename_free(zMbcs); |
| 283 | fossil_filename_free(zMbcsOld); |
| 284 | |
| 285 | /* if the symlink was not created, create a plain text file */ |
| 286 | if (!created){ |
| 287 | Blob content; |
| 288 | blob_set(&content, oldpath); |
| 289 | blob_write_to_file(&content, newpath); |
| 290 | blob_reset(&content); |
| 291 | created = 1; |
| 292 | } |
| 293 | |
| 294 | return !created; |
| 295 | } |
| 296 | |
| 297 | /* |
| 298 | ** Given a pathname to a file, return true if: |
| @@ -305,12 +322,13 @@ | |
| 305 | int changed = 0; |
| 306 | wchar_t* zMbcs; |
| 307 | fossilStat lstat_buf, stat_buf; |
| 308 | WIN32_FILE_ATTRIBUTE_DATA lstat_attr; |
| 309 | zMbcs = fossil_utf8_to_filename(zName); |
| 310 | if (win32_stat(zMbcs, &stat_buf) != 0) |
| 311 | stat_buf.st_mode = S_IFREG; |
| 312 | changed = |
| 313 | (win32_lstat(zMbcs, &lstat_buf) == 0) && |
| 314 | (lstat_buf.st_mode == S_IFLNK) && |
| 315 | GetFileAttributesExW(zMbcs, GetFileExInfoStandard, &lstat_attr) && |
| 316 | ((stat_buf.st_mode == S_IFDIR) != ((lstat_attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)); |
| @@ -337,65 +355,69 @@ | |
| 337 | DWORD fullLength; |
| 338 | wchar_t volName[MAX_PATH+1]; |
| 339 | DWORD fsFlags; |
| 340 | |
| 341 | /* symlinks only supported on vista or greater */ |
| 342 | /* if (!IsWindowsVistaOrGreater()) // TODO: make it work on MinGW |
| 343 | return 0; */ |
| 344 | |
| 345 | /* next we need to check to see if the privilege is available */ |
| 346 | |
| 347 | /* can't check privilege if we can't lookup its value */ |
| 348 | if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid)) |
| 349 | return 0; |
| 350 | |
| 351 | /* can't check privilege if we can't open the process token */ |
| 352 | process = GetCurrentProcess(); |
| 353 | if (!OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token)) |
| 354 | return 0; |
| 355 | |
| 356 | /* by this point, we have a process token and the privilege value */ |
| 357 | /* try to enable the privilege then close the token */ |
| 358 | |
| 359 | tp.PrivilegeCount = 1; |
| 360 | tp.Privileges[0].Luid = luid; |
| 361 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
| 362 | |
| 363 | AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); |
| 364 | status = GetLastError(); |
| 365 | |
| 366 | CloseHandle(token); |
| 367 | |
| 368 | /* any error means we failed to enable the privilege, symlinks not supported */ |
| 369 | if (status != ERROR_SUCCESS) |
| 370 | return 0; |
| 371 | |
| 372 | /* assume no support for symlinks */ |
| 373 | success = 0; |
| 374 | |
| 375 | pFilename = fossil_utf8_to_filename(zFilename); |
| 376 | |
| 377 | /* given the filename we're interested in, symlinks are supported if */ |
| 378 | /* 1. we can get the full name of the path from the given path */ |
| 379 | fullLength = GetFullPathNameW(pFilename, sizeof(fullName), fullName, NULL); |
| 380 | if ((fullLength > 0) && (fullLength < sizeof(fullName))){ |
| 381 | /* 2. we can get the volume path name from the full name */ |
| 382 | if (GetVolumePathNameW(fullName, volName, sizeof(volName))){ |
| 383 | /* 3. we can get volume information from the volume path name */ |
| 384 | if (GetVolumeInformationW(volName, NULL, 0, NULL, NULL, &fsFlags, NULL, 0)){ |
| 385 | /* 4. the given volume support reparse points */ |
| 386 | if (fsFlags & FILE_SUPPORTS_REPARSE_POINTS){ |
| 387 | /* all four conditions were true, so we support symlinks; success! */ |
| 388 | success = 1; |
| 389 | } |
| 390 | } |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | fossil_filename_free(pFilename); |
| 395 | |
| 396 | return success; |
| 397 | } |
| 398 | |
| 399 | /* |
| 400 | ** Wrapper around the access() system call. This code was copied from Tcl |
| 401 | ** 8.6 and then modified. |
| @@ -587,11 +609,13 @@ | |
| 587 | * Unable to perform access check. |
| 588 | */ |
| 589 | |
| 590 | rc = -1; goto done; |
| 591 | } |
| 592 | if( !accessYesNo ) rc = -1; |
| 593 | |
| 594 | done: |
| 595 | |
| 596 | if( hToken != NULL ){ |
| 597 | CloseHandle(hToken); |
| 598 |
| --- src/winfile.c | |
| +++ src/winfile.c | |
| @@ -35,13 +35,13 @@ | |
| 35 | #ifndef LABEL_SECURITY_INFORMATION |
| 36 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 37 | #endif |
| 38 | |
| 39 | #if defined(__MSVCRT__) |
| 40 | static HANDLE dllhandle = NULL; |
| 41 | static DWORD WINAPI (*getFinalPathNameByHandleW) (HANDLE, LPWSTR, DWORD, DWORD) = NULL; |
| 42 | static BOOLEAN APIENTRY (*createSymbolicLinkW) (LPCWSTR, LPCWSTR, DWORD) = NULL; |
| 43 | #endif |
| 44 | |
| 45 | /* a couple defines to make the borrowed struct below compile */ |
| 46 | #ifndef _ANONYMOUS_UNION |
| 47 | # define _ANONYMOUS_UNION |
| @@ -79,10 +79,20 @@ | |
| 79 | } GenericReparseBuffer; |
| 80 | } DUMMYUNIONNAME; |
| 81 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 82 | |
| 83 | #define LINK_BUFFER_SIZE 1024 |
| 84 | |
| 85 | static int isVistaOrLater(){ |
| 86 | if( !dllhandle ){ |
| 87 | HANDLE h = LoadLibraryW(L"KERNEL32"); |
| 88 | createSymbolicLinkW = (BOOLEAN APIENTRY (*) (LPCWSTR, LPCWSTR, DWORD)) GetProcAddress(h, "CreateSymbolicLinkW"); |
| 89 | getFinalPathNameByHandleW = (DWORD WINAPI (*) (HANDLE, LPWSTR, DWORD, DWORD)) GetProcAddress(h, "GetFinalPathNameByHandleW"); |
| 90 | dllhandle = h; |
| 91 | } |
| 92 | return createSymbolicLinkW != NULL; |
| 93 | } |
| 94 | |
| 95 | /* |
| 96 | ** Fill stat buf with information received from GetFileAttributesExW(). |
| 97 | ** Does not follow symbolic links, returning instead information about |
| 98 | ** the link itself. |
| @@ -91,33 +101,33 @@ | |
| 101 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 102 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 103 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 104 | if( rc ){ |
| 105 | ssize_t tlen = 0; /* assume it is not a symbolic link */ |
| 106 | |
| 107 | /* if it is a reparse point it *might* be a symbolic link */ |
| 108 | /* so defer to win32_readlink to actually check */ |
| 109 | if( attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ){ |
| 110 | char *tname = fossil_filename_to_utf8(zFilename); |
| 111 | char tlink[LINK_BUFFER_SIZE]; |
| 112 | tlen = win32_readlink(tname, tlink, sizeof(tlink)); |
| 113 | fossil_filename_free(tname); |
| 114 | } |
| 115 | |
| 116 | ULARGE_INTEGER ull; |
| 117 | |
| 118 | /* if a link was retrieved, it is a symlink, otherwise a dir or file */ |
| 119 | if( tlen == 0 ){ |
| 120 | buf->st_mode = ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 121 | S_IFDIR : S_IFREG); |
| 122 | |
| 123 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| 124 | }else{ |
| 125 | buf->st_mode = S_IFLNK; |
| 126 | buf->st_size = tlen; |
| 127 | } |
| 128 | |
| 129 | ull.LowPart = attr.ftLastWriteTime.dwLowDateTime; |
| 130 | ull.HighPart = attr.ftLastWriteTime.dwHighDateTime; |
| 131 | buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL; |
| 132 | } |
| 133 | return !rc; |
| @@ -126,11 +136,11 @@ | |
| 136 | /* |
| 137 | ** Fill stat buf with information received from win32_lstat(). |
| 138 | ** If a symbolic link is found, follow it and return information about |
| 139 | ** the target, repeating until an actual target is found. |
| 140 | ** Limit the number of loop iterations so as to avoid an infinite loop |
| 141 | ** due to circular links. This should never happen because |
| 142 | ** GetFinalPathNameByHandleW() should always preclude that need, but being |
| 143 | ** prepared to loop seems prudent, or at least not harmful. |
| 144 | ** Returns 0 on success, 1 on failure. |
| 145 | */ |
| 146 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| @@ -137,40 +147,44 @@ | |
| 147 | int rc; |
| 148 | HANDLE file; |
| 149 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 150 | DWORD len; |
| 151 | int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ |
| 152 | |
| 153 | while (iterationsRemaining-- > 0){ |
| 154 | rc = win32_lstat(zFilename, buf); |
| 155 | |
| 156 | /* exit on error or not link */ |
| 157 | if( (rc != 0) || (buf->st_mode != S_IFLNK) ) |
| 158 | break; |
| 159 | |
| 160 | /* it is a link, so open the linked file */ |
| 161 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
| 162 | if( (file == NULL) || (file == INVALID_HANDLE_VALUE) ){ |
| 163 | rc = 1; |
| 164 | break; |
| 165 | } |
| 166 | |
| 167 | /* get the final path name and close the handle */ |
| 168 | if( isVistaOrLater() ){ |
| 169 | len = getFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 170 | }else{ |
| 171 | len = -1; |
| 172 | } |
| 173 | CloseHandle(file); |
| 174 | |
| 175 | /* if any problems getting the final path name error so exit */ |
| 176 | if( (len <= 0) || (len > LINK_BUFFER_SIZE - 1) ){ |
| 177 | rc = 1; |
| 178 | break; |
| 179 | } |
| 180 | |
| 181 | /* prepare to try again just in case we have a chain to follow */ |
| 182 | /* this shouldn't happen, but just trying to be safe */ |
| 183 | zFilename = nextFilename; |
| 184 | } |
| 185 | |
| 186 | return rc; |
| 187 | } |
| 188 | |
| 189 | /* |
| 190 | ** An implementation of a posix-like readlink function for win32. |
| @@ -178,36 +192,36 @@ | |
| 192 | ** Returns the length of the link copied to buf on success, -1 on failure. |
| 193 | */ |
| 194 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 195 | /* assume we're going to fail */ |
| 196 | ssize_t rv = -1; |
| 197 | |
| 198 | /* does path reference a reparse point? */ |
| 199 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 200 | int rc = GetFileAttributesEx(path, GetFileExInfoStandard, &attr); |
| 201 | if( rc && (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ){ |
| 202 | |
| 203 | /* since it is a reparse point, open it */ |
| 204 | HANDLE file = CreateFile(path, FILE_READ_EA, 0, NULL, OPEN_EXISTING, |
| 205 | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 206 | if( (file != NULL) && (file != INVALID_HANDLE_VALUE) ){ |
| 207 | |
| 208 | /* use DeviceIoControl to get the reparse point data */ |
| 209 | |
| 210 | int data_size = sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t); |
| 211 | REPARSE_DATA_BUFFER* data = fossil_malloc(data_size); |
| 212 | DWORD data_used; |
| 213 | |
| 214 | data->ReparseTag = IO_REPARSE_TAG_SYMLINK; |
| 215 | data->ReparseDataLength = 0; |
| 216 | data->Reserved = 0; |
| 217 | |
| 218 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 219 | data, data_size, &data_used, NULL); |
| 220 | |
| 221 | /* did the reparse point data fit into the desired buffer? */ |
| 222 | if( rc && (data_used < data_size) ){ |
| 223 | /* it fit, so setup the print name for further processing */ |
| 224 | USHORT |
| 225 | offset = data->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), |
| 226 | length = data->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t); |
| 227 | char *temp; |
| @@ -214,18 +228,18 @@ | |
| 228 | data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; |
| 229 | |
| 230 | /* convert the filename to utf8, copy it, and discard the converted copy */ |
| 231 | temp = fossil_filename_to_utf8(data->SymbolicLinkReparseBuffer.PathBuffer + offset); |
| 232 | rv = strlen(temp); |
| 233 | if( rv >= bufsiz ) |
| 234 | rv = bufsiz; |
| 235 | memcpy(buf, temp, rv); |
| 236 | fossil_filename_free(temp); |
| 237 | } |
| 238 | |
| 239 | fossil_free(data); |
| 240 | |
| 241 | /* all done, close the reparse point */ |
| 242 | CloseHandle(file); |
| 243 | } |
| 244 | } |
| 245 | |
| @@ -242,12 +256,12 @@ | |
| 256 | ** Returns 0 on success, 1 on failure. |
| 257 | */ |
| 258 | int win32_unlink_rmdir(const wchar_t *zFilename){ |
| 259 | int rc = 0; |
| 260 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 261 | if( GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr) ){ |
| 262 | if( (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY ) |
| 263 | rc = RemoveDirectoryW(zFilename); |
| 264 | else |
| 265 | rc = DeleteFileW(zFilename); |
| 266 | } |
| 267 | return !rc; |
| @@ -266,33 +280,36 @@ | |
| 280 | fossilStat stat; |
| 281 | int created = 0; |
| 282 | DWORD flags = 0; |
| 283 | wchar_t *zMbcs, *zMbcsOld; |
| 284 | |
| 285 | /* does oldpath exist? is it a dir or a file? */ |
| 286 | zMbcsOld = fossil_utf8_to_filename(oldpath); |
| 287 | if( win32_stat(zMbcsOld, &stat) == 0 ){ |
| 288 | if( stat.st_mode == S_IFDIR ){ |
| 289 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* remove newpath before creating the symlink */ |
| 294 | zMbcs = fossil_utf8_to_filename(newpath); |
| 295 | win32_unlink_rmdir(zMbcs); |
| 296 | if( isVistaOrLater() ){ |
| 297 | created = createSymbolicLinkW(zMbcs, zMbcsOld, flags); |
| 298 | } |
| 299 | fossil_filename_free(zMbcs); |
| 300 | fossil_filename_free(zMbcsOld); |
| 301 | |
| 302 | /* if the symlink was not created, create a plain text file */ |
| 303 | if( !created ){ |
| 304 | Blob content; |
| 305 | blob_set(&content, oldpath); |
| 306 | blob_write_to_file(&content, newpath); |
| 307 | blob_reset(&content); |
| 308 | created = 1; |
| 309 | } |
| 310 | |
| 311 | return !created; |
| 312 | } |
| 313 | |
| 314 | /* |
| 315 | ** Given a pathname to a file, return true if: |
| @@ -305,12 +322,13 @@ | |
| 322 | int changed = 0; |
| 323 | wchar_t* zMbcs; |
| 324 | fossilStat lstat_buf, stat_buf; |
| 325 | WIN32_FILE_ATTRIBUTE_DATA lstat_attr; |
| 326 | zMbcs = fossil_utf8_to_filename(zName); |
| 327 | if( win32_stat(zMbcs, &stat_buf) != 0 ){ |
| 328 | stat_buf.st_mode = S_IFREG; |
| 329 | } |
| 330 | changed = |
| 331 | (win32_lstat(zMbcs, &lstat_buf) == 0) && |
| 332 | (lstat_buf.st_mode == S_IFLNK) && |
| 333 | GetFileAttributesExW(zMbcs, GetFileExInfoStandard, &lstat_attr) && |
| 334 | ((stat_buf.st_mode == S_IFDIR) != ((lstat_attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)); |
| @@ -337,65 +355,69 @@ | |
| 355 | DWORD fullLength; |
| 356 | wchar_t volName[MAX_PATH+1]; |
| 357 | DWORD fsFlags; |
| 358 | |
| 359 | /* symlinks only supported on vista or greater */ |
| 360 | if( !isVistaOrLater() ){ |
| 361 | return 0; |
| 362 | } |
| 363 | |
| 364 | /* next we need to check to see if the privilege is available */ |
| 365 | |
| 366 | /* can't check privilege if we can't lookup its value */ |
| 367 | if( !LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid) ){ |
| 368 | return 0; |
| 369 | } |
| 370 | |
| 371 | /* can't check privilege if we can't open the process token */ |
| 372 | process = GetCurrentProcess(); |
| 373 | if( !OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token) ){ |
| 374 | return 0; |
| 375 | } |
| 376 | |
| 377 | /* by this point, we have a process token and the privilege value */ |
| 378 | /* try to enable the privilege then close the token */ |
| 379 | |
| 380 | tp.PrivilegeCount = 1; |
| 381 | tp.Privileges[0].Luid = luid; |
| 382 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
| 383 | |
| 384 | AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); |
| 385 | status = GetLastError(); |
| 386 | |
| 387 | CloseHandle(token); |
| 388 | |
| 389 | /* any error means we failed to enable the privilege, symlinks not supported */ |
| 390 | if( status != ERROR_SUCCESS ){ |
| 391 | return 0; |
| 392 | } |
| 393 | |
| 394 | /* assume no support for symlinks */ |
| 395 | success = 0; |
| 396 | |
| 397 | pFilename = fossil_utf8_to_filename(zFilename); |
| 398 | |
| 399 | /* given the filename we're interested in, symlinks are supported if */ |
| 400 | /* 1. we can get the full name of the path from the given path */ |
| 401 | fullLength = GetFullPathNameW(pFilename, sizeof(fullName), fullName, NULL); |
| 402 | if( (fullLength > 0) && (fullLength < sizeof(fullName)) ){ |
| 403 | /* 2. we can get the volume path name from the full name */ |
| 404 | if( GetVolumePathNameW(fullName, volName, sizeof(volName)) ){ |
| 405 | /* 3. we can get volume information from the volume path name */ |
| 406 | if( GetVolumeInformationW(volName, NULL, 0, NULL, NULL, &fsFlags, NULL, 0) ){ |
| 407 | /* 4. the given volume support reparse points */ |
| 408 | if( fsFlags & FILE_SUPPORTS_REPARSE_POINTS ){ |
| 409 | /* all four conditions were true, so we support symlinks; success! */ |
| 410 | success = 1; |
| 411 | } |
| 412 | } |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | fossil_filename_free(pFilename); |
| 417 | |
| 418 | return success; |
| 419 | } |
| 420 | |
| 421 | /* |
| 422 | ** Wrapper around the access() system call. This code was copied from Tcl |
| 423 | ** 8.6 and then modified. |
| @@ -587,11 +609,13 @@ | |
| 609 | * Unable to perform access check. |
| 610 | */ |
| 611 | |
| 612 | rc = -1; goto done; |
| 613 | } |
| 614 | if( !accessYesNo ){ |
| 615 | rc = -1; |
| 616 | } |
| 617 | |
| 618 | done: |
| 619 | |
| 620 | if( hToken != NULL ){ |
| 621 | CloseHandle(hToken); |
| 622 |
+24
-3
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -30,10 +30,13 @@ | ||
| 30 | 30 | # Uncomment to enable miniz usage |
| 31 | 31 | # FOSSIL_ENABLE_MINIZ = 1 |
| 32 | 32 | |
| 33 | 33 | # Uncomment to enable SSL support |
| 34 | 34 | # FOSSIL_ENABLE_SSL = 1 |
| 35 | + | |
| 36 | +# Uncomment to build SSL libraries | |
| 37 | +# FOSSIL_BUILD_SSL = 1 | |
| 35 | 38 | |
| 36 | 39 | # Uncomment to enable TH1 scripts in embedded documentation files |
| 37 | 40 | # FOSSIL_ENABLE_TH1_DOCS = 1 |
| 38 | 41 | |
| 39 | 42 | # Uncomment to enable TH1 hooks |
| @@ -45,10 +48,26 @@ | ||
| 45 | 48 | !ifdef FOSSIL_ENABLE_SSL |
| 46 | 49 | SSLDIR = $(B)\compat\openssl-1.0.1i |
| 47 | 50 | SSLINCDIR = $(SSLDIR)\inc32 |
| 48 | 51 | SSLLIBDIR = $(SSLDIR)\out32 |
| 49 | 52 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 53 | +!if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" | |
| 54 | +!message Using 'x64' platform for OpenSSL... | |
| 55 | +SSLCONFIG = VC-WIN64A no-asm | |
| 56 | +SSLSETUP = ms\do_win64a.bat | |
| 57 | +SSLNMAKE = ms\nt.mak all | |
| 58 | +!elseif "$(PLATFORM)"=="ia64" | |
| 59 | +!message Using 'ia64' platform for OpenSSL... | |
| 60 | +SSLCONFIG = VC-WIN64I no-asm | |
| 61 | +SSLSETUP = ms\do_win64i.bat | |
| 62 | +SSLNMAKE = ms\nt.mak all | |
| 63 | +!else | |
| 64 | +!message Assuming 'x86' platform for OpenSSL... | |
| 65 | +SSLCONFIG = VC-WIN32 no-asm | |
| 66 | +SSLSETUP = ms\do_ms.bat | |
| 67 | +SSLNMAKE = ms\nt.mak all | |
| 68 | +!endif | |
| 50 | 69 | !endif |
| 51 | 70 | |
| 52 | 71 | !ifdef FOSSIL_ENABLE_TCL |
| 53 | 72 | TCLDIR = $(B)\compat\tcl-8.6 |
| 54 | 73 | TCLSRCDIR = $(TCLDIR) |
| @@ -404,21 +423,23 @@ | ||
| 404 | 423 | openssl: |
| 405 | 424 | @echo Building OpenSSL from "$(SSLDIR)"... |
| 406 | 425 | !if "$(PERLDIR)" != "" |
| 407 | 426 | @set PATH=$(PERLDIR);$(PATH) |
| 408 | 427 | !endif |
| 409 | - @pushd "$(SSLDIR)" && $(PERL) Configure VC-WIN32 no-asm && popd | |
| 410 | - @pushd "$(SSLDIR)" && call ms\do_ms.bat && popd | |
| 411 | - @pushd "$(SSLDIR)" && $(MAKE) /f ms\nt.mak all && popd | |
| 428 | + @pushd "$(SSLDIR)" && $(PERL) Configure $(SSLCONFIG) && popd | |
| 429 | + @pushd "$(SSLDIR)" && call $(SSLSETUP) && popd | |
| 430 | + @pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) && popd | |
| 412 | 431 | !endif |
| 413 | 432 | |
| 414 | 433 | !ifndef FOSSIL_ENABLE_MINIZ |
| 415 | 434 | APPTARGETS = $(APPTARGETS) zlib |
| 416 | 435 | !endif |
| 417 | 436 | |
| 418 | 437 | !ifdef FOSSIL_ENABLE_SSL |
| 438 | +!ifdef FOSSIL_BUILD_SSL | |
| 419 | 439 | APPTARGETS = $(APPTARGETS) openssl |
| 440 | +!endif | |
| 420 | 441 | !endif |
| 421 | 442 | |
| 422 | 443 | $(APPNAME) : $(APPTARGETS) translate$E mkindex$E headers $(OBJ) $(OX)\linkopts |
| 423 | 444 | cd $(OX) |
| 424 | 445 | link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 425 | 446 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -30,10 +30,13 @@ | |
| 30 | # Uncomment to enable miniz usage |
| 31 | # FOSSIL_ENABLE_MINIZ = 1 |
| 32 | |
| 33 | # Uncomment to enable SSL support |
| 34 | # FOSSIL_ENABLE_SSL = 1 |
| 35 | |
| 36 | # Uncomment to enable TH1 scripts in embedded documentation files |
| 37 | # FOSSIL_ENABLE_TH1_DOCS = 1 |
| 38 | |
| 39 | # Uncomment to enable TH1 hooks |
| @@ -45,10 +48,26 @@ | |
| 45 | !ifdef FOSSIL_ENABLE_SSL |
| 46 | SSLDIR = $(B)\compat\openssl-1.0.1i |
| 47 | SSLINCDIR = $(SSLDIR)\inc32 |
| 48 | SSLLIBDIR = $(SSLDIR)\out32 |
| 49 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 50 | !endif |
| 51 | |
| 52 | !ifdef FOSSIL_ENABLE_TCL |
| 53 | TCLDIR = $(B)\compat\tcl-8.6 |
| 54 | TCLSRCDIR = $(TCLDIR) |
| @@ -404,21 +423,23 @@ | |
| 404 | openssl: |
| 405 | @echo Building OpenSSL from "$(SSLDIR)"... |
| 406 | !if "$(PERLDIR)" != "" |
| 407 | @set PATH=$(PERLDIR);$(PATH) |
| 408 | !endif |
| 409 | @pushd "$(SSLDIR)" && $(PERL) Configure VC-WIN32 no-asm && popd |
| 410 | @pushd "$(SSLDIR)" && call ms\do_ms.bat && popd |
| 411 | @pushd "$(SSLDIR)" && $(MAKE) /f ms\nt.mak all && popd |
| 412 | !endif |
| 413 | |
| 414 | !ifndef FOSSIL_ENABLE_MINIZ |
| 415 | APPTARGETS = $(APPTARGETS) zlib |
| 416 | !endif |
| 417 | |
| 418 | !ifdef FOSSIL_ENABLE_SSL |
| 419 | APPTARGETS = $(APPTARGETS) openssl |
| 420 | !endif |
| 421 | |
| 422 | $(APPNAME) : $(APPTARGETS) translate$E mkindex$E headers $(OBJ) $(OX)\linkopts |
| 423 | cd $(OX) |
| 424 | link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 425 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -30,10 +30,13 @@ | |
| 30 | # Uncomment to enable miniz usage |
| 31 | # FOSSIL_ENABLE_MINIZ = 1 |
| 32 | |
| 33 | # Uncomment to enable SSL support |
| 34 | # FOSSIL_ENABLE_SSL = 1 |
| 35 | |
| 36 | # Uncomment to build SSL libraries |
| 37 | # FOSSIL_BUILD_SSL = 1 |
| 38 | |
| 39 | # Uncomment to enable TH1 scripts in embedded documentation files |
| 40 | # FOSSIL_ENABLE_TH1_DOCS = 1 |
| 41 | |
| 42 | # Uncomment to enable TH1 hooks |
| @@ -45,10 +48,26 @@ | |
| 48 | !ifdef FOSSIL_ENABLE_SSL |
| 49 | SSLDIR = $(B)\compat\openssl-1.0.1i |
| 50 | SSLINCDIR = $(SSLDIR)\inc32 |
| 51 | SSLLIBDIR = $(SSLDIR)\out32 |
| 52 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 53 | !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" |
| 54 | !message Using 'x64' platform for OpenSSL... |
| 55 | SSLCONFIG = VC-WIN64A no-asm |
| 56 | SSLSETUP = ms\do_win64a.bat |
| 57 | SSLNMAKE = ms\nt.mak all |
| 58 | !elseif "$(PLATFORM)"=="ia64" |
| 59 | !message Using 'ia64' platform for OpenSSL... |
| 60 | SSLCONFIG = VC-WIN64I no-asm |
| 61 | SSLSETUP = ms\do_win64i.bat |
| 62 | SSLNMAKE = ms\nt.mak all |
| 63 | !else |
| 64 | !message Assuming 'x86' platform for OpenSSL... |
| 65 | SSLCONFIG = VC-WIN32 no-asm |
| 66 | SSLSETUP = ms\do_ms.bat |
| 67 | SSLNMAKE = ms\nt.mak all |
| 68 | !endif |
| 69 | !endif |
| 70 | |
| 71 | !ifdef FOSSIL_ENABLE_TCL |
| 72 | TCLDIR = $(B)\compat\tcl-8.6 |
| 73 | TCLSRCDIR = $(TCLDIR) |
| @@ -404,21 +423,23 @@ | |
| 423 | openssl: |
| 424 | @echo Building OpenSSL from "$(SSLDIR)"... |
| 425 | !if "$(PERLDIR)" != "" |
| 426 | @set PATH=$(PERLDIR);$(PATH) |
| 427 | !endif |
| 428 | @pushd "$(SSLDIR)" && $(PERL) Configure $(SSLCONFIG) && popd |
| 429 | @pushd "$(SSLDIR)" && call $(SSLSETUP) && popd |
| 430 | @pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) && popd |
| 431 | !endif |
| 432 | |
| 433 | !ifndef FOSSIL_ENABLE_MINIZ |
| 434 | APPTARGETS = $(APPTARGETS) zlib |
| 435 | !endif |
| 436 | |
| 437 | !ifdef FOSSIL_ENABLE_SSL |
| 438 | !ifdef FOSSIL_BUILD_SSL |
| 439 | APPTARGETS = $(APPTARGETS) openssl |
| 440 | !endif |
| 441 | !endif |
| 442 | |
| 443 | $(APPNAME) : $(APPTARGETS) translate$E mkindex$E headers $(OBJ) $(OX)\linkopts |
| 444 | cd $(OX) |
| 445 | link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 446 |
+2
-2
| --- www/build.wiki | ||
| +++ www/build.wiki | ||
| @@ -126,14 +126,14 @@ | ||
| 126 | 126 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 127 | 127 | "<b>compat/openssl-1.0.1i</b>"), then make sure that some recent |
| 128 | 128 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 129 | 129 | and finally run one of the following commands: |
| 130 | 130 | <blockquote><pre> |
| 131 | -nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 PERLDIR=C:\full\path\to\Perl\bin | |
| 131 | +nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin | |
| 132 | 132 | </pre></blockquote> |
| 133 | 133 | <blockquote><pre> |
| 134 | -buildmsvc.bat FOSSIL_ENABLE_SSL=1 PERLDIR=C:\full\path\to\Perl\bin | |
| 134 | +buildmsvc.bat FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin | |
| 135 | 135 | </pre></blockquote> |
| 136 | 136 | |
| 137 | 137 | <li><p><i>Cygwin</i> → The same as other unix-like systems. It is |
| 138 | 138 | recommended to configure using: "<b>configure --disable-internal-sqlite</b>", |
| 139 | 139 | making sure you have the "libsqlite3-devel" , "zlib-devel" and |
| 140 | 140 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -126,14 +126,14 @@ | |
| 126 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 127 | "<b>compat/openssl-1.0.1i</b>"), then make sure that some recent |
| 128 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 129 | and finally run one of the following commands: |
| 130 | <blockquote><pre> |
| 131 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 132 | </pre></blockquote> |
| 133 | <blockquote><pre> |
| 134 | buildmsvc.bat FOSSIL_ENABLE_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 135 | </pre></blockquote> |
| 136 | |
| 137 | <li><p><i>Cygwin</i> → The same as other unix-like systems. It is |
| 138 | recommended to configure using: "<b>configure --disable-internal-sqlite</b>", |
| 139 | making sure you have the "libsqlite3-devel" , "zlib-devel" and |
| 140 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -126,14 +126,14 @@ | |
| 126 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 127 | "<b>compat/openssl-1.0.1i</b>"), then make sure that some recent |
| 128 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 129 | and finally run one of the following commands: |
| 130 | <blockquote><pre> |
| 131 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 132 | </pre></blockquote> |
| 133 | <blockquote><pre> |
| 134 | buildmsvc.bat FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 135 | </pre></blockquote> |
| 136 | |
| 137 | <li><p><i>Cygwin</i> → The same as other unix-like systems. It is |
| 138 | recommended to configure using: "<b>configure --disable-internal-sqlite</b>", |
| 139 | making sure you have the "libsqlite3-devel" , "zlib-devel" and |
| 140 |