Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Windows API plugin #54

Open
nemequ opened this issue Sep 16, 2013 · 2 comments
Open

Add Windows API plugin #54

nemequ opened this issue Sep 16, 2013 · 2 comments

Comments

@nemequ
Copy link
Member

nemequ commented Sep 16, 2013

http://msdn.microsoft.com/en-us/library/windows/hardware/ff552127%28v=vs.85%29.aspx (RtlCompressBuffer)

Depends on #38.

@nemequ nemequ added windows and removed windows labels Oct 24, 2015
nemequ added a commit that referenced this issue Jun 25, 2016
This is completely untested; it probably won't even compile.  However,
most of the work should be done, so if someone wants to pick it up and
finish it it should be pretty straightforward.  AFAIK the only thing
that really needs to be done is to fix the max_compressed_size
functions to better match whatever RtlCompressBuffer actually does.

See #54
@nemequ
Copy link
Member Author

nemequ commented Jun 25, 2016

I've added a Windows plugin to the wip/windows branch. There is still some work to be done (which is hard for me to do without a Windows development environment), but it should be pretty straightforward.

Basically, someone just needs to get it to compile, then provide more realistic implementations of the get_max_compressed_size functions.

Just in case I end up deleting the branch, here is the diff:

diff --git a/docs/index.md b/docs/index.md
index efe7167..ef38773 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -60,6 +60,7 @@ Squash currently contains plugins for the following libraries:
 - [QuickLZ](@ref md_plugins_quicklz_quicklz)
 - [Snappy](@ref md_plugins_snappy_snappy)
 - [wfLZ](@ref md_plugins_wflz_wflz)
+- [Windows](@ref md_plugins_windows_windows)
 - [yalz77](@ref md_plugins_yalz77_yalz77)
 - [zlib](@ref md_plugins_zlib_zlib)
 - [zlib-ng](@ref md_plugins_zlib-ng_zlib-ng)
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index f4a4a4e..21443fa 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -27,6 +27,7 @@ set (plugins_available
   quicklz
   snappy
   wflz
+  windows
   yalz77
   zlib
   zlib-ng
diff --git a/plugins/windows/CMakeLists.txt b/plugins/windows/CMakeLists.txt
new file mode 100644
index 0000000..b35ba3c
--- /dev/null
+++ b/plugins/windows/CMakeLists.txt
@@ -0,0 +1,13 @@
+include (SquashPlugin)
+
+check_include_file("Ntifs.h" HAVE_NTIFS_H)
+if(HAVE_NTIFS_H)
+  set(SQUASH_WINDOWS_ENABLED_DEFAULT "")
+else()
+  set(SQUASH_WINDOWS_ENABLED_DEFAULT "DEFAULT_DISABLED")
+endif(HAVE_NTIFS_H)
+
+squash_plugin (
+  NAME windows
+  ${SQUASH_WINDOWS_ENABLED_DEFAULT}
+  SOURCES squash-windows.c)
diff --git a/plugins/windows/squash-windows.c b/plugins/windows/squash-windows.c
new file mode 100644
index 0000000..03280e7
--- /dev/null
+++ b/plugins/windows/squash-windows.c
@@ -0,0 +1,194 @@
+/* Copyright (c) 2016 The Squash Authors
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Evan Nemerson <evan@nemerson.com>
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <Ntifs.h>
+
+#include <squash/squash.h>
+
+SQUASH_PLUGIN_EXPORT
+SquashStatus squash_plugin_init_codec (SquashCodec* codec, SquashCodecImpl* impl);
+
+static size_t
+squash_windows_lznt1_get_max_compressed_size (SquashCodec* codec, size_t uncompressed_size) {
+  return uncompressed_size * 2 + 256;
+}
+
+static size_t
+squash_windows_xpress_get_max_compressed_size (SquashCodec* codec, size_t uncompressed_size) {
+  return uncompressed_size * 2 + 256;
+}
+
+static size_t
+squash_windows_xpress_huffman_get_max_compressed_size (SquashCodec* codec, size_t uncompressed_size) {
+  return uncompressed_size * 2 + 256;
+}
+
+enum SquashWindowsOptIndex {
+  SQUASH_WINDOWS_OPT_LEVEL = 0
+};
+
+static SquashOptionInfo squash_windows_options[] = {
+  { "level",
+    SQUASH_OPTION_TYPE_RANGE_INT,
+    .info.range_int = {
+      .min = 1,
+      .max = 2 },
+    .default_value.int_value = 1 },
+};
+
+static USHORT squash_windows_get_format_from_codec (SquashCodec* codec) {
+  const char* name = squash_codec_get_name (codec);
+
+  if (name[0] == 'l')
+    return COMPRESSION_FORMAT_LZNT1;
+  else if (name[6] == '-')
+    return COMPRESSION_FORMAT_XPRESS_HUFF;
+  else
+    return COMPRESSION_FORMAT_XPRESS;
+}
+
+static SquashStatus
+squash_windows_compress_buffer (SquashCodec* codec,
+                                size_t* compressed_size,
+                                uint8_t compressed[SQUASH_ARRAY_PARAM(*compressed_size)],
+                                size_t uncompressed_size,
+                                const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_size)],
+                                SquashOptions* options) {
+  const USHORT engine = squash_options_get_int_at (options, codec, SQUASH_ZLIB_OPT_LEVEL) == 1 ?
+    COMPRESSION_ENGINE_STANDARD : COMPRESSION_ENGINE_MAXIMUM;
+  const USHORT format = squash_windows_get_format_from_codec (codec);
+  const USHORT format_and_engine = format | engine;
+  uint8_t* workmem = NULL;
+  ULONG workmem_size;
+  NTSTATUS r;
+  ULONG out_size;
+
+#if ULONG_MAX < SIZE_MAX
+  if (SQUASH_UNLIKELY(ULONG_MAX < *compressed_size) ||
+      SQUASH_UNLIKELY(ULONG_MAX < decompressed_size))
+    return squash_error (SQUASH_RANGE);
+#endif
+
+  r = RtlGetCompressionWorkSpaceSize (format_and_engine, &workmem_size, NULL);
+  if (SQUASH_UNLIKELY(r != STATUS_SUCCESS))
+    return squash_error (SQUASH_FAILED);
+
+  workmem = squash_malloc(workmem_size);
+  if (SQUASH_UNLIKELY(workmem == NULL))
+    return squash_error (SQUASH_MEMORY);
+
+  r = RtlCompressBuffer (format_and_engine,
+                         (PUCHAR) uncompressed, (ULONG) uncompressed_size,
+                         (PUCHAR) compressed, (ULONG) *compressed_size,
+                         4096, &out_size, workmem);
+
+  squash_free (workmem);
+
+  if (SQUASH_UNLIKELY(r != STATUS_SUCCCESS)) {
+    switch (r) {
+      case STATUS_BUFFER_ALL_ZEROS:
+        break;
+      case STATUS_BUFFER_TOO_SMALL:
+        return squash_error (SQUASH_BUFFER_FULL);
+      default:
+        return squash_error (SQUASH_FAILED);
+    }
+  }
+
+#if SIZE_MAX < ULONG_MAX
+  if (SQUASH_UNLIKELY(SIZE_MAX < out_size))
+    return squash_error (SQUASH_RANGE);
+#endif
+
+  *compressed_size = (size_t) out_size;
+
+  return SQUASH_OK;
+}
+
+static SquashStatus
+squash_windows_decompress_buffer (SquashCodec* codec,
+                                  size_t* decompressed_size,
+                                  uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_size)],
+                                  size_t compressed_size,
+                                  const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_size)],
+                                  SquashOptions* options) {
+  const USHORT format = squash_windows_get_format_from_codec (codec);
+  ULONG out_size;
+  NTSTATUS r;
+
+#if ULONG_MAX < SIZE_MAX
+  if (SQUASH_UNLIKELY(ULONG_MAX < compressed_size) ||
+      SQUASH_UNLIKELY(ULONG_MAX < *decompressed_size))
+    return squash_error (SQUASH_RANGE);
+#endif
+
+  r = RtlDecompressBuffer(format,
+                          (PUCHAR) decompressed, (ULONG) *decompressed_size,
+                          (PUCHAR) compressed, (ULONG) compressed_size,
+                          &out_size);
+
+#if SIZE_MAX < ULONG_MAX
+  if (SQUASH_UNLIKELY(SIZE_MAX < out_size))
+    return squash_error (SQUASH_RANGE);
+#endif
+
+  if (SQUASH_UNLIKELY(r != STATUS_SUCCESS)) {
+    switch (r) {
+      case STATUS_BAD_COMPRESSION_BUFFER:
+        return squash_error (SQUASH_BUFFER_FULL);
+      default:
+        return squash_error (SQUASH_FAILED);
+    }
+  }
+
+  *decompressed_size = (size_t) out_size;
+
+  return SQUASH_OK;
+}
+
+SquashStatus
+squash_plugin_init_codec (SquashCodec* codec, SquashCodecImpl* impl) {
+  const char* name = squash_codec_get_name (codec);
+
+  if (strcmp ("lznt1", name) == 0)
+    impl->get_max_compressed_size = squash_windows_lznt1_get_max_compressed_size;
+  else if (strcmp ("xpress", name) == 0)
+    impl->get_max_compressed_size = squash_windows_xpress_get_max_compressed_size;
+  else if (strcmp ("xpress-huffman", name) == 0)
+    impl->get_max_compressed_size = squash_windows_xpress_huffman_get_max_compressed_size;
+  else
+    return squash_error (SQUASH_UNABLE_TO_LOAD);
+
+  impl->options = squash_windows_options;
+  impl->decompress_buffer = squash_windows_decompress_buffer;
+  impl->compress_buffer = squash_windows_compress_buffer;
+
+  return SQUASH_OK;
+}
diff --git a/plugins/windows/squash.ini b/plugins/windows/squash.ini
new file mode 100644
index 0000000..7ebb826
--- /dev/null
+++ b/plugins/windows/squash.ini
@@ -0,0 +1,5 @@
+license=Proprietary
+
+[lznt1]
+[xpress]
+[xpress-huffman]
diff --git a/plugins/windows/windows.md b/plugins/windows/windows.md
new file mode 100644
index 0000000..10715df
--- /dev/null
+++ b/plugins/windows/windows.md
@@ -0,0 +1,29 @@
+# windows Plugin #
+
+This plugin uses the Windows compression API (RtlCompressBuffer et. al.).
+
+For more information about this API, see the
+[RtlCompressBuffer documentation](https://msdn.microsoft.com/en-us/library/windows/hardware/ff552127(v=vs.85).aspx)
+
+Note: this plugin is only available on Windows.  For a cross-platform
+implementation of LZNT1 and Xpress-huffman, see the ms-compress
+plugin.
+
+## Codecs ##
+
+- **lznt1** — LZNT1 data.
+- **xpress** — Xpress data.
+- **xpress-huffman** — Xpress-huffman data.
+
+## Options ##
+
+### Compression Only ###
+
+- **level** (integer, 1 or 2, default 1): whether to use the standard
+  or maximume compression engine.
+
+## License ##
+
+The windows plugin is licensed under the
+[MIT License](http://opensource.org/licenses/MIT).  The Windows API is
+proprietary.

@nemequ
Copy link
Member Author

nemequ commented Jun 25, 2016

Note that the random-data test provides a convenient way to figure out those get_max_compressed_size functions. Just do something like:

./tests/test-squash --log-visible debug --show-stderr --param codec windows:$codec /random

Where $codec is lznt1, xpress, and xpress-huffman.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant