[PATCH v2 2/2] fsutil: Add support for creating hardlinks

Arkadiusz Hiler ahiler at codeweavers.com
Wed Aug 5 04:18:33 CDT 2020


From: Micheal Müller <michael at fds-team.de>

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=22749
Signed-off-by: Vijay Kiran Kamuju <infyquest at gmail.com>
Signed-off-by: Arkadiusz Hiler <ahiler at 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();
+}
-- 
2.28.0




More information about the wine-devel mailing list