From a73f1b95592617ac7484e16c2e642573a4d65644 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 9 Jul 2019 13:26:07 -0700 Subject: [PATCH] launcher: path transform language --- build/VStudio/testing/winfsp-tests.vcxproj | 1 + .../testing/winfsp-tests.vcxproj.filters | 3 + build/VStudio/tools/launcher.vcxproj | 1 + build/VStudio/tools/launcher.vcxproj.filters | 3 + src/launcher/launcher.c | 60 ++++-- src/launcher/ptrans.c | 182 ++++++++++++++++++ tst/launcher-tests/echo.c | 35 ++++ tst/launcher-tests/echo.reg | Bin 0 -> 454 bytes tst/{secret => launcher-tests}/secret.c | 0 tst/launcher-tests/secret.reg | Bin 0 -> 448 bytes tst/secret/secret.reg | Bin 476 -> 0 bytes tst/winfsp-tests/launcher-ptrans-test.c | 112 +++++++++++ tst/winfsp-tests/winfsp-tests.c | 1 + 13 files changed, 378 insertions(+), 20 deletions(-) create mode 100644 src/launcher/ptrans.c create mode 100644 tst/launcher-tests/echo.c create mode 100644 tst/launcher-tests/echo.reg rename tst/{secret => launcher-tests}/secret.c (100%) create mode 100644 tst/launcher-tests/secret.reg delete mode 100644 tst/secret/secret.reg create mode 100644 tst/winfsp-tests/launcher-ptrans-test.c diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj index 6790d6b0..c6e5651b 100644 --- a/build/VStudio/testing/winfsp-tests.vcxproj +++ b/build/VStudio/testing/winfsp-tests.vcxproj @@ -191,6 +191,7 @@ + diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters index 67ff3de6..b07e62e6 100644 --- a/build/VStudio/testing/winfsp-tests.vcxproj.filters +++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters @@ -100,6 +100,9 @@ Source + + Source + diff --git a/build/VStudio/tools/launcher.vcxproj b/build/VStudio/tools/launcher.vcxproj index 7c0cf280..ae17af2e 100644 --- a/build/VStudio/tools/launcher.vcxproj +++ b/build/VStudio/tools/launcher.vcxproj @@ -186,6 +186,7 @@ + diff --git a/build/VStudio/tools/launcher.vcxproj.filters b/build/VStudio/tools/launcher.vcxproj.filters index d2ad1d56..1aed461c 100644 --- a/build/VStudio/tools/launcher.vcxproj.filters +++ b/build/VStudio/tools/launcher.vcxproj.filters @@ -16,6 +16,9 @@ Source + + Source + diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index 5a7bbed9..373424d3 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -503,24 +503,19 @@ static SVC_INSTANCE *SvcInstanceLookup(PWSTR ClassName, PWSTR InstanceName) return 0; } -static ULONG SvcInstanceArgumentLength(PWSTR Arg) +static inline ULONG SvcInstanceArgumentLength(PWSTR Arg, PWSTR Pattern) { - ULONG Length; + PWSTR PathTransform(PWSTR Dest, PWSTR Arg, PWSTR Pattern); - Length = 2; /* for beginning and ending quotes */ - for (PWSTR P = Arg; *P; P++) - if (L'"' != *P) - Length++; - - return Length; + return 2 + (ULONG)(UINT_PTR)PathTransform(0, Arg, Pattern); } -static PWSTR SvcInstanceArgumentCopy(PWSTR Dest, PWSTR Arg) +static inline PWSTR SvcInstanceArgumentCopy(PWSTR Dest, PWSTR Arg, PWSTR Pattern) { + PWSTR PathTransform(PWSTR Dest, PWSTR Arg, PWSTR Pattern); + *Dest++ = L'"'; - for (PWSTR P = Arg; *P; P++) - if (L'"' != *P) - *Dest++ = *P; + Dest = PathTransform(Dest, Arg, Pattern); *Dest++ = L'"'; return Dest; @@ -532,6 +527,7 @@ static NTSTATUS SvcInstanceReplaceArguments(PWSTR String, ULONG Argc, PWSTR *Arg PWSTR NewString = 0, P, Q; PWSTR EmptyArg = L""; ULONG Length; + PWSTR Pattern; *PNewString = 0; @@ -541,24 +537,36 @@ static NTSTATUS SvcInstanceReplaceArguments(PWSTR String, ULONG Argc, PWSTR *Arg switch (*P) { case L'%': + Pattern = 0; P++; + if (L'\\' == *P) + { + Pattern = ++P; + while (!(L'\0' == *P || + (L'0' <= *P && *P <= '9') || + (L'A' <= *P && *P <= 'Z'))) + P++; + } if (L'0' <= *P && *P <= '9') { if (Argc > (ULONG)(*P - L'0')) - Length += SvcInstanceArgumentLength(Argv[*P - L'0']); + Length += SvcInstanceArgumentLength(Argv[*P - L'0'], Pattern); else - Length += SvcInstanceArgumentLength(EmptyArg); + Length += SvcInstanceArgumentLength(EmptyArg, 0); } else if (L'U' == *P) { if (0 != SvcInstanceUserName()) - Length += SvcInstanceArgumentLength(SvcInstanceUserName()); + Length += SvcInstanceArgumentLength(SvcInstanceUserName(), Pattern); else - Length += SvcInstanceArgumentLength(EmptyArg); + Length += SvcInstanceArgumentLength(EmptyArg, 0); } else + if (*P) Length++; + else + P--; break; default: Length++; @@ -575,24 +583,36 @@ static NTSTATUS SvcInstanceReplaceArguments(PWSTR String, ULONG Argc, PWSTR *Arg switch (*P) { case L'%': + Pattern = 0; P++; + if (L'\\' == *P) + { + Pattern = ++P; + while (!(L'\0' == *P || + (L'0' <= *P && *P <= '9') || + (L'A' <= *P && *P <= 'Z'))) + P++; + } if (L'0' <= *P && *P <= '9') { if (Argc > (ULONG)(*P - L'0')) - Q = SvcInstanceArgumentCopy(Q, Argv[*P - L'0']); + Q = SvcInstanceArgumentCopy(Q, Argv[*P - L'0'], Pattern); else - Q = SvcInstanceArgumentCopy(Q, EmptyArg); + Q = SvcInstanceArgumentCopy(Q, EmptyArg, 0); } else if (L'U' == *P) { if (0 != SvcInstanceUserName()) - Q = SvcInstanceArgumentCopy(Q, SvcInstanceUserName()); + Q = SvcInstanceArgumentCopy(Q, SvcInstanceUserName(), Pattern); else - Q = SvcInstanceArgumentCopy(Q, EmptyArg); + Q = SvcInstanceArgumentCopy(Q, EmptyArg, 0); } else + if (*P) *Q++ = *P; + else + P--; break; default: *Q++ = *P; diff --git a/src/launcher/ptrans.c b/src/launcher/ptrans.c new file mode 100644 index 00000000..15320d1f --- /dev/null +++ b/src/launcher/ptrans.c @@ -0,0 +1,182 @@ +/** + * @file launcher/ptrans.c + * + * @copyright 2015-2019 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +/** + * Path Transformation Language + * + * Syntax: + * PERCENT BACKLASH replace-sep *(*sep component) [*sep UNDERSCORE] arg + * + * Arg: + * The command line argument that a rule is applied on. These are denoted + * by digits [0-9] or a capital letter (A-Z). + * + * Component: + * A path component denoted by a small letter (a-z). The letter a denotes + * the first path component, the letter b the second path component, etc. + * + * UNDERSCORE: + * The UNDERSCORE (_) denotes the "rest of the path". This is any path + * left after any path components explicitly mentioned by using small + * letters (a-z). + * + * Sep: + * A separator symbol that is used to separated components. + * E.g. slash (/), colon (:), etc. + * + * Replace-sep: + * A separator symbol that replaces the backslash (\) separator in the + * "rest of the path" (UNDERSCORE). E.g. slash (/), colon (:), etc. + * + * + * Examples: + * - %\/b:_1 + * - Transforms \rclone\REMOTE\PATH\TO\FILES to REMOTE:PATH/TO/FILES + * - %\/b:/_1 + * - Transforms \rclone\REMOTE\PATH\TO\FILES to REMOTE:/PATH/TO/FILES + * - %\/_1 + * - Transforms \P1\P2\P3 to /P1/P2/P3 + * - %\+_1 + * - Transforms \P1\P2\P3 to +P1+P2+P3 + * - %\\_1 + * - Transforms \P1\P2\P3 to \\P1\\P2\\P3 + * (Backslash is doubled up when used as a replacement separator!) + */ + +#include +#include + +static PWSTR PathCopy(PWSTR Dest, PWSTR Arg, PWSTR ArgEnd, BOOLEAN WriteDest, WCHAR Replacement) +{ + if (0 != Replacement) + { + for (PWSTR P = Arg, EndP = (0 != ArgEnd ? ArgEnd : (PWSTR)(UINT_PTR)~0); EndP > P && *P; P++) + if (L'\\' == *P) + { + if (L'\\' == Replacement) + { + if (WriteDest) + *Dest = Replacement; + Dest++; + } + + if (WriteDest) + *Dest = Replacement; + Dest++; + } + else if (L'"' != *P) + { + if (WriteDest) + *Dest = *P; + Dest++; + } + } + else + { + for (PWSTR P = Arg, EndP = (0 != ArgEnd ? ArgEnd : (PWSTR)(UINT_PTR)~0); EndP > P && *P; P++) + if (L'"' != *P) + { + if (WriteDest) + *Dest = *P; + Dest++; + } + } + + return Dest; +} + +static inline BOOLEAN PatternEnd(WCHAR C) +{ + return L'\0' == C || + (L'0' <= C && C <= '9') || + (L'A' <= C && C <= 'Z'); +} + +PWSTR PathTransform(PWSTR Dest, PWSTR Arg, PWSTR Pattern) +{ + BOOLEAN WriteDest = 0 != Dest; + WCHAR Replacement; + PWSTR Components[26][2]; + PWSTR Remainder = Arg; + ULONG RemainderIndex = 0; + PWSTR P; + + if (0 == Pattern) + return PathCopy(Dest, Arg, 0, WriteDest, 0); + + for (ULONG I = 0; 26 > I; I++) + Components[I][0] = 0; + + Replacement = *Pattern++; + if (PatternEnd(Replacement)) + return Dest; + + while (!PatternEnd(*Pattern)) + { + if (L'a' <= *Pattern && *Pattern <= 'z') + { + ULONG I = *Pattern - 'a', J; + if (0 == Components[I][0]) + { + P = Remainder; + J = RemainderIndex; + + while (L'\\' == *P) + P++; + + for (;;) + { + Components[J][0] = P; + while (*P && L'\\' != *P) + P++; + Components[J][1] = P; + + while (L'\\' == *P) + P++; + + if (I == J) + { + Remainder = P; + RemainderIndex = I + 1; + break; + } + + J++; + } + } + + Dest = PathCopy(Dest, Components[I][0], Components[I][1], WriteDest, Replacement); + } + else + if (L'_' == *Pattern) + Dest = PathCopy(Dest, Remainder, 0, WriteDest, Replacement); + else + { + if (WriteDest) + *Dest = *Pattern; + Dest++; + } + + Pattern++; + } + + return Dest; +} diff --git a/tst/launcher-tests/echo.c b/tst/launcher-tests/echo.c new file mode 100644 index 00000000..4780b85d --- /dev/null +++ b/tst/launcher-tests/echo.c @@ -0,0 +1,35 @@ +/* + * Compile: + * - cl -I"%ProgramFiles(x86)%\WinFsp\inc" "%ProgramFiles(x86)%\WinFsp\lib\winfsp-x64.lib" echo.c + * + * Register: + * - echo.reg (fix Executable path first) + * + * Run: + * - launchctl-x64 start echo 1 \foo\bar\baz + * + * Expect: + * - "\foo\bar\baz" "bar:baz" "DOMAIN\\USERNAME" + */ + +#include + +int wmain(int argc, wchar_t *argv[]) +{ + WCHAR buf[512], *bufp; + int len; + + bufp = buf; + for (int i = 0; argc > i; i++) + { + len = lstrlenW(argv[i]); + memcpy(bufp, argv[i], len * sizeof(WCHAR)); + bufp += len; + *bufp++ = '\n'; + } + *bufp = '\0'; + + FspServiceLog(EVENTLOG_INFORMATION_TYPE, L"%s", buf); + + return 0; +} diff --git a/tst/launcher-tests/echo.reg b/tst/launcher-tests/echo.reg new file mode 100644 index 0000000000000000000000000000000000000000..0f5210aa6ec5a658065717596325a2aca64da848 GIT binary patch literal 454 zcmYL`TT8=G5QOJh@IRD5AFX#E3VkTSqF67aYA7BFRij2~BTeh&&sV?QG|1s_vNO9o zvzy;lszNh8snnM$wUj8=3s|Lv=K29ShAoIn;+AgV=dcCwjQCI;?Qk}=;rC7_I-_Q+ zOha8NR-ZT1Id2Sisteel5!^u6O2HFHQjk<6-F9TzQ*@Sa=EM4Kft$CcvT@h}HE+?{X^h4@XX1Zg==~e7ZSAt%w)fv=iY`0+ LM@%(ij=Iwyss2pu literal 0 HcmV?d00001 diff --git a/tst/secret/secret.c b/tst/launcher-tests/secret.c similarity index 100% rename from tst/secret/secret.c rename to tst/launcher-tests/secret.c diff --git a/tst/launcher-tests/secret.reg b/tst/launcher-tests/secret.reg new file mode 100644 index 0000000000000000000000000000000000000000..f55351d1e951524695aa34ea8b4e43f2d7c146fc GIT binary patch literal 448 zcmYk3TT8=06okKL!T(V5Zt?yg_)vmH@k(k9Vx(wWHBu63TC0Cv{dQYXmSr`3 ze1GMXRE(%u(9j}d%(Gm>j47WoN2;Z$7Pq)jpQ@JPO1wv#P1ibWao=&wsh(Q~oN-A? z$8*5BXR3C>g=#@ht;;pJd={BpCTE{rcG$M6cJA29Wx}pA-=Z(fe|P>+wUKE&hjL>x z-p34y{XQZ)mwVumi74UFopPY3pdWqVCQH=QlR33x(vYv<%APf^vCnK(w{UL7lPF{< z>>l1Jd`+A)R~z!8W6Fkn$oK2~pR%nI@+GUZX-pq(TUlS)X`(aa9r;(nyN^sAUioir GMeh$zdre{h literal 0 HcmV?d00001 diff --git a/tst/secret/secret.reg b/tst/secret/secret.reg deleted file mode 100644 index d508c0cc53ff9eaebf6ca2476b5a66a9415c33a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 476 zcmZvZ+e*Vg6h-&5;6IdnK+y+Z1Ru(vRNAUEUJyr0+r&0f5=l}oKd-KHq6kukVdh-- zIx}CNcPdmWQ>8aG>S?N6D}0Sw)%w75O0>YLuzR{CUK1^_OY9@{b->!!p6>?@HKOKT zGmUkrpaJJh6V5>FTo**6QZiCqbDv-ZZRuLmyF@8?Z_}@|AuGpc6{h@(@{(JFr(-|k z{sP5VbGnp7*Pgknq9L+$omP*|n?UgH-YqI~El|vr=!6|~j7roE3f9>$aW&;_T>@Yu@~5N@b9z6=HXlMG7x JfxmU=wcowgR89Z@ diff --git a/tst/winfsp-tests/launcher-ptrans-test.c b/tst/winfsp-tests/launcher-ptrans-test.c new file mode 100644 index 00000000..9ee39b9d --- /dev/null +++ b/tst/winfsp-tests/launcher-ptrans-test.c @@ -0,0 +1,112 @@ +/** + * @file launcher-ptrans-test.c + * + * @copyright 2015-2019 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include +#include + +#include "winfsp-tests.h" + +#include + +static void launcher_ptrans_test(void) +{ + PWSTR ipaths[] = + { + L"", 0, + L"\\foo\\bar", 0, + L"", L"", + L"\\foo\\bar", L"", + L"\\foo\\bar", L"/", + L"\\foo\\bar", L"/_", + L"\\foo\\bar", L"/_1", + L"\\foo\\bar", L"\\_", + L"\\foo\\bar", L"\\_A", + L"\\foo\\bar", L"/a", + L"\\foo\\bar", L"/b", + L"\\foo\\bar", L"/c", + L"\\foo\\bar", L"/d", + L"\\foo\\bar", L"/a:b", + L"\\foo\\bar", L"/a:b&c!d", + L"\\foo\\bar", L"/b:a", + L"\\foo\\bar", L"/d!c&b:a", + L"\\foo\\bar", L"/a:_", + L"\\foo\\bar", L"/b:_", + L"\\foo\\bar", L"/c:_", + L"\\foo\\bar\\baz", L"/b:_", + L"\\foo\\bar\\baz\\bag", L"/b:_", + L"\\foo\\bar\\baz", L"/b:/_", + L"\\foo\\bar\\baz\\bag", L"/b:/_", + L"\\foo\\bar\\baz\\bag", L"/a:_:b:_", + L"\\foo\\bar\\baz\\bag", L"/_:_", + }; + PWSTR opaths[] = + { + L"", + L"\\foo\\bar", + L"", + L"", + L"", + L"/foo/bar", + L"/foo/bar", + L"\\\\foo\\\\bar", + L"\\\\foo\\\\bar", + L"foo", + L"bar", + L"", + L"", + L"foo:bar", + L"foo:bar&!", + L"bar:foo", + L"!&bar:foo", + L"foo:bar", + L"bar:", + L":", + L"bar:baz", + L"bar:baz/bag", + L"bar:/baz", + L"bar:/baz/bag", + L"foo:bar/baz/bag:bar:baz/bag", + L"/foo/bar/baz/bag:/foo/bar/baz/bag", + }; + + for (size_t i = 0; sizeof ipaths / (sizeof ipaths[0] * 2) > i; i++) + { + WCHAR Buf[1024]; + ULONG Length; + PWSTR Dest; + + Length = (ULONG)(UINT_PTR)PathTransform(0, ipaths[2 * i + 0], ipaths[2 * i + 1]); + ASSERT(Length == wcslen(opaths[i]) * sizeof(WCHAR)); + + Dest = PathTransform(Buf, ipaths[2 * i + 0], ipaths[2 * i + 1]); + *Dest = L'\0'; + ASSERT(Dest == Buf + wcslen(opaths[i])); + ASSERT(0 == wcscmp(Buf, opaths[i])); + } +} + +void launcher_ptrans_tests(void) +{ + if (OptExternal) + return; + + TEST_OPT(launcher_ptrans_test); +} diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c index 57480565..06d9a61f 100644 --- a/tst/winfsp-tests/winfsp-tests.c +++ b/tst/winfsp-tests/winfsp-tests.c @@ -193,6 +193,7 @@ int main(int argc, char *argv[]) TESTSUITE(dirbuf_tests); TESTSUITE(version_tests); TESTSUITE(launch_tests); + TESTSUITE(launcher_ptrans_tests); TESTSUITE(mount_tests); TESTSUITE(timeout_tests); TESTSUITE(memfs_tests);