From 7edfe7dd563c746ab2eb896b36dc9f6cafb13a91 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Fri, 1 Apr 2016 01:29:51 +0200
Subject: [PATCH] fsutil: Add fsutil program with support for creating hard
 links.

---
 programs/fsutil/Makefile.in |   5 +-
 programs/fsutil/fsutil.rc   |  34 ++++++++++
 programs/fsutil/main.c      | 131 ++++++++++++++++++++++++++++++++++--
 programs/fsutil/resources.h |  25 +++++++
 4 files changed, 188 insertions(+), 7 deletions(-)
 create mode 100644 programs/fsutil/fsutil.rc
 create mode 100644 programs/fsutil/resources.h

diff --git a/programs/fsutil/Makefile.in b/programs/fsutil/Makefile.in
index 64307e83a..1cc7f87e2 100644
--- a/programs/fsutil/Makefile.in
+++ b/programs/fsutil/Makefile.in
@@ -1,6 +1,9 @@
 MODULE    = fsutil.exe
 
-EXTRADLLFLAGS = -mconsole -municode -mno-cygwin
+EXTRADLLFLAGS = -mconsole -municode
+IMPORTS   = user32
 
 C_SRCS = \
 	main.c
+
+RC_SRCS = fsutil.rc
diff --git a/programs/fsutil/fsutil.rc b/programs/fsutil/fsutil.rc
new file mode 100644
index 000000000..593f8175a
--- /dev/null
+++ b/programs/fsutil/fsutil.rc
@@ -0,0 +1,34 @@
+/*
+ * FSUTIL.EXE - Wine-compatible fsutil program
+ *
+ * Copyright 2016 Michael Müller
+ *
+ * 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 "resources.h"
+
+#pragma makedep po
+
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    STRING_USAGE, "---- Commands Supported ----\n\nhardlink      Hardlink management\n\n"
+    STRING_UNSUPPORTED_CMD, "%1 is an unsupported command\n"
+    STRING_UNSUPPORTED_PARAM, "%1 is an unsupported parameter\n"
+    STRING_HARDLINK_USAGE, "---- Hardlink - Commands Supported ----\n\ncreate      Create a hardlink.\n\n"
+    STRING_HARDLINK_CREATE_USAGE, "Syntax: fsutil hardlink create old new\n\n"
+}
diff --git a/programs/fsutil/main.c b/programs/fsutil/main.c
index 2bce87edc..508419682 100644
--- a/programs/fsutil/main.c
+++ b/programs/fsutil/main.c
@@ -1,5 +1,6 @@
 /*
  * Copyright 2016 Austin English
+ * Copyright 2016 Michael Müller
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -16,18 +17,136 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#include "wine/debug.h"
+#include <windows.h>
+#include <wine/unicode.h>
+#include <wine/debug.h>
+
+#include "resources.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(fsutil);
 
+static void output_write(const WCHAR *str, DWORD wlen)
+{
+    DWORD count, ret;
+
+    ret = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, wlen, &count, NULL);
+    if (!ret)
+    {
+        DWORD len;
+        char  *msgA;
+
+        /* On Windows WriteConsoleW() fails if the output is redirected. So fall
+         * back to WriteFile(), assuming the console encoding is still the right
+         * one in that case.
+         */
+        len = WideCharToMultiByte(GetConsoleOutputCP(), 0, str, wlen, NULL, 0, NULL, NULL);
+        msgA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
+        if (!msgA) return;
+
+        WideCharToMultiByte(GetConsoleOutputCP(), 0, str, wlen, msgA, len, NULL, NULL);
+        WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE);
+        HeapFree(GetProcessHeap(), 0, msgA);
+    }
+}
+
+static int output_vprintf(const WCHAR* fmt, __ms_va_list va_args)
+{
+    WCHAR str[8192];
+    int len;
+
+    SetLastError(NO_ERROR);
+    len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, fmt, 0, 0, str,
+                         sizeof(str)/sizeof(*str), &va_args);
+    if (len == 0 && GetLastError() != NO_ERROR)
+        WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt));
+    else
+        output_write(str, len);
+    return 0;
+}
+
+static int WINAPIV output_string(int msg, ...)
+{
+    WCHAR fmt[8192];
+    __ms_va_list arguments;
+
+    LoadStringW(GetModuleHandleW(NULL), msg, fmt, sizeof(fmt)/sizeof(fmt[0]));
+    __ms_va_start(arguments, msg);
+    output_vprintf(fmt, arguments);
+    __ms_va_end(arguments);
+    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[])
+{
+    static const WCHAR createW[]={'c','r','e','a','t','e',0};
+    int ret;
+
+    if (argc > 2)
+    {
+        if (!strcmpiW(argv[2], createW))
+            return create_hardlink(argc, argv);
+        else
+        {
+            FIXME("unsupported parameter %s\n", debugstr_w(argv[2]));
+            output_string(STRING_UNSUPPORTED_PARAM, argv[2]);
+            ret = 1;
+        }
+    }
+
+    output_string(STRING_HARDLINK_USAGE);
+    return ret;
+}
+
 int wmain(int argc, WCHAR *argv[])
 {
-    int i;
+    static const WCHAR hardlinkW[]={'h','a','r','d','l','i','n','k',0};
+    int i, ret = 0;
 
-    WINE_FIXME("stub:");
     for (i = 0; i < argc; i++)
-        WINE_FIXME(" %s", wine_dbgstr_w(argv[i]));
-    WINE_FIXME("\n");
+        WINE_TRACE(" %s", wine_dbgstr_w(argv[i]));
+    WINE_TRACE("\n");
 
-    return 0;
+    if (argc > 1)
+    {
+        if (!strcmpiW(argv[1], hardlinkW))
+            return hardlink(argc, argv);
+        else
+        {
+            FIXME("unsupported command %s\n", debugstr_w(argv[1]));
+            output_string(STRING_UNSUPPORTED_CMD, argv[1]);
+            ret = 1;
+        }
+    }
+
+    output_string(STRING_USAGE);
+    return ret;
 }
diff --git a/programs/fsutil/resources.h b/programs/fsutil/resources.h
new file mode 100644
index 000000000..b85826ac4
--- /dev/null
+++ b/programs/fsutil/resources.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 Michael Müller
+ *
+ * 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 <windef.h>
+
+#define STRING_USAGE                    101
+#define STRING_UNSUPPORTED_CMD          102
+#define STRING_UNSUPPORTED_PARAM        103
+#define STRING_HARDLINK_USAGE           104
+#define STRING_HARDLINK_CREATE_USAGE    105
-- 
2.21.0

