From: Micheal Müller michael@fds-team.de
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=22749 Signed-off-by: Vijay Kiran Kamuju infyquest@gmail.com Signed-off-by: Arkadiusz Hiler ahiler@codeweavers.com --- configure | 1 + configure.ac | 1 + programs/fsutil/fsutil.mc | 14 ++++- programs/fsutil/main.c | 57 +++++++++++++++++- programs/fsutil/resources.h | 2 + programs/fsutil/tests/Makefile.in | 5 ++ programs/fsutil/tests/fsutil.c | 98 +++++++++++++++++++++++++++++++ 7 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 programs/fsutil/tests/Makefile.in create mode 100644 programs/fsutil/tests/fsutil.c
diff --git a/configure b/configure index 13c2078221..0088e95817 100755 --- a/configure +++ b/configure @@ -21423,6 +21423,7 @@ wine_fn_config_makefile programs/find enable_find wine_fn_config_makefile programs/find/tests enable_tests wine_fn_config_makefile programs/findstr enable_findstr wine_fn_config_makefile programs/fsutil enable_fsutil +wine_fn_config_makefile programs/fsutil/tests enable_tests wine_fn_config_makefile programs/hh enable_hh wine_fn_config_makefile programs/hostname enable_hostname wine_fn_config_makefile programs/icacls enable_icacls diff --git a/configure.ac b/configure.ac index d54857181e..cccb9ef423 100644 --- a/configure.ac +++ b/configure.ac @@ -3965,6 +3965,7 @@ WINE_CONFIG_MAKEFILE(programs/find) WINE_CONFIG_MAKEFILE(programs/find/tests) WINE_CONFIG_MAKEFILE(programs/findstr) WINE_CONFIG_MAKEFILE(programs/fsutil) +WINE_CONFIG_MAKEFILE(programs/fsutil/tests) WINE_CONFIG_MAKEFILE(programs/hh) WINE_CONFIG_MAKEFILE(programs/hostname) WINE_CONFIG_MAKEFILE(programs/icacls) diff --git a/programs/fsutil/fsutil.mc b/programs/fsutil/fsutil.mc index 54c801cb2b..e904bb8644 100644 --- a/programs/fsutil/fsutil.mc +++ b/programs/fsutil/fsutil.mc @@ -23,5 +23,17 @@ SymbolicName=STRING_USAGE Language=ENU - Supported Commands -
-[NONE] +hardlink hardlink management +. +MessageId=102 +SymbolicName=STRING_HARDLINK_USAGE +Language=ENU +- Hardlink - Supported Commands - + +create create a hardlink +. +MessageId=103 +SymbolicName=STRING_HARDLINK_CREATE_USAGE +Language=ENU +Syntax: fsutil hardlink create <new> <existing> . diff --git a/programs/fsutil/main.c b/programs/fsutil/main.c index 1d61edab75..bde07ac2df 100644 --- a/programs/fsutil/main.c +++ b/programs/fsutil/main.c @@ -66,6 +66,54 @@ static int WINAPIV output_string(int msg, ...) return 0; }
+static BOOL output_error_string(DWORD error) +{ + LPWSTR pBuffer; + if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, error, 0, (LPWSTR)&pBuffer, 0, NULL)) + { + output_write(pBuffer, lstrlenW(pBuffer)); + LocalFree(pBuffer); + return TRUE; + } + return FALSE; +} + +static int create_hardlink(int argc, WCHAR *argv[]) +{ + if (argc != 5) + { + output_string(STRING_HARDLINK_CREATE_USAGE); + return 1; + } + + if (CreateHardLinkW(argv[3], argv[4], NULL)) + return 0; + + output_error_string(GetLastError()); + return 1; +} + +static int hardlink(int argc, WCHAR *argv[]) +{ + int ret; + + if (argc > 2) + { + if (!wcsicmp(argv[2], L"create")) + return create_hardlink(argc, argv); + else + { + FIXME("unsupported parameter %s\n", debugstr_w(argv[2])); + ret = 1; + } + } + + output_string(STRING_HARDLINK_USAGE); + return ret; +} + int __cdecl wmain(int argc, WCHAR *argv[]) { int i, ret = 0; @@ -77,8 +125,13 @@ int __cdecl wmain(int argc, WCHAR *argv[])
if (argc > 1) { - FIXME("unsupported command %s\n", debugstr_w(argv[1])); - ret = 1; + if (!wcsicmp(argv[1], L"hardlink")) + return hardlink(argc, argv); + else + { + FIXME("unsupported command %s\n", debugstr_w(argv[1])); + ret = 1; + } }
output_string(STRING_USAGE); diff --git a/programs/fsutil/resources.h b/programs/fsutil/resources.h index 36b0ffc35f..56280d958a 100644 --- a/programs/fsutil/resources.h +++ b/programs/fsutil/resources.h @@ -19,3 +19,5 @@ #include <windef.h>
#define STRING_USAGE 101 +#define STRING_HARDLINK_USAGE 102 +#define STRING_HARDLINK_CREATE_USAGE 103 diff --git a/programs/fsutil/tests/Makefile.in b/programs/fsutil/tests/Makefile.in new file mode 100644 index 0000000000..5a69e47dc6 --- /dev/null +++ b/programs/fsutil/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = fsutil.exe +IMPORTS = user32 + +C_SRCS = \ + fsutil.c diff --git a/programs/fsutil/tests/fsutil.c b/programs/fsutil/tests/fsutil.c new file mode 100644 index 0000000000..9867788274 --- /dev/null +++ b/programs/fsutil/tests/fsutil.c @@ -0,0 +1,98 @@ +/* + * Copyright 2020 Arkadiusz Hiler for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windows.h> +#include <stdio.h> + +#include "wine/test.h" + +static DWORD runcmd(const char* cmd) +{ + STARTUPINFOA si = { sizeof(STARTUPINFOA) }; + PROCESS_INFORMATION pi; + char* wcmd; + DWORD rc; + + /* Create a writable copy for CreateProcessA() */ + wcmd = HeapAlloc(GetProcessHeap(), 0, strlen(cmd) + 1); + strcpy(wcmd, cmd); + + rc = CreateProcessA(NULL, wcmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + HeapFree(GetProcessHeap(), 0, wcmd); + + if (!rc) + return 260; + + rc = WaitForSingleObject(pi.hProcess, 5000); + + if (rc == WAIT_OBJECT_0) + GetExitCodeProcess(pi.hProcess, &rc); + else + TerminateProcess(pi.hProcess, 1); + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + return rc; +} + +static void test_hardlink(void) +{ + DWORD rc; + BOOL boolrc; + HANDLE hfile; + BY_HANDLE_FILE_INFORMATION info; + + hfile = CreateFileA("file", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(hfile != INVALID_HANDLE_VALUE, "failed to create a file\n"); + CloseHandle(hfile); + + rc = runcmd("fsutil hardlink create link file"); + ok(rc == 0, "failed to create a hardlink\n"); + + hfile = CreateFileA("link", GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(hfile != INVALID_HANDLE_VALUE, "failed to open the hardlink\n"); + boolrc = GetFileInformationByHandle(hfile, &info); + ok(boolrc, "failed to get info about hardlink, error %#x\n", GetLastError()); + CloseHandle(hfile); + + ok(info.nNumberOfLinks == 2, "our link is not a hardlink"); + + rc = runcmd("fsutil hardlink create link file"); + ok(rc == 1, "fsutil didn't complain about already existing destination\n"); + + rc = runcmd("fsutil hardlink create newlink nonexistingfile"); + ok(rc == 1, "fsutil didn't complain about nonexisting source file\n"); + + boolrc = DeleteFileA("link"); + ok(boolrc, "failed to delete the hardlink, error %#x\n", GetLastError()); + boolrc = DeleteFileA("file"); + ok(boolrc, "failed to delete the file, error %#x\n", GetLastError()); +} + +START_TEST(fsutil) +{ + char tmpdir[MAX_PATH]; + + GetTempPathA(MAX_PATH, tmpdir); + SetCurrentDirectoryA(tmpdir); + + test_hardlink(); +}