Jon Turney wrote:
On 12/05/2021 18:50, Christian Franke wrote:
Jon Turney wrote:
On 08/05/2021 21:03, Christian Franke wrote:
...
+#include "compactos.h"
+
+#ifndef FSCTL_SET_EXTERNAL_BACKING

There should be a comment here saying "not yet provided by w32api" or similar.

... or we wait for a release of w32api headers with the patch mentioned above :-)

No, I think this way is better, since I build the setup releases on Fedora, and so don't have any control about when the w32api package I'm building against gets updated

(and furthermore it's an old Fedora at the moment, since the x86 MinGW toolchain in recent Fedora isn't built with SJLJ exception handling...)


I see. BTW: Mingw-w64 upstream pushed my patch yesterday.

Attached is a new patch for setup which also allows to select the compression algorithm.

From d65db8dcbe3b07a06adbf5484dcb5ab98e165b04 Mon Sep 17 00:00:00 2001
From: Christian Franke <christian.fra...@t-online.de>
Date: Fri, 14 May 2021 09:10:06 +0200
Subject: [PATCH] Add new option '--compact-os ALGORITHM'.

If specified, selected Compact OS compression algorithm is applied
to files below /bin, /sbin and /usr.  Most DLL files are excluded
because rebase will open these files again for writing.
---
 Makefile.am          |  2 +
 compactos.cc         | 63 +++++++++++++++++++++++++++++++
 compactos.h          | 25 +++++++++++++
 io_stream_cygfile.cc | 88 +++++++++++++++++++++++++++++++++++++++++++-
 io_stream_cygfile.h  |  2 +
 5 files changed, 178 insertions(+), 2 deletions(-)
 create mode 100644 compactos.cc
 create mode 100644 compactos.h

diff --git a/Makefile.am b/Makefile.am
index d10ad6b..63e96da 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -108,6 +108,8 @@ inilint_SOURCES = \
        archive_tar_file.cc \
        choose.cc \
        choose.h \
+       compactos.cc \
+       compactos.h \
        compress.cc \
        compress.h \
        compress_bz.cc \
diff --git a/compactos.cc b/compactos.cc
new file mode 100644
index 0000000..9ed2a73
--- /dev/null
+++ b/compactos.cc
@@ -0,0 +1,63 @@
+//
+// compactos.cc
+//
+// Copyright (C) 2021 Christian Franke
+//
+// SPDX-License-Identifier: MIT
+//
+
+#include "compactos.h"
+
+/* Not yet provided by w32api headers. */
+#ifndef FSCTL_SET_EXTERNAL_BACKING
+#define FSCTL_SET_EXTERNAL_BACKING \
+  CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 195, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
+#endif
+
+#ifndef WOF_CURRENT_VERSION
+#define WOF_CURRENT_VERSION 1
+
+typedef struct _WOF_EXTERNAL_INFO {
+  DWORD Version;
+  DWORD Provider;
+} WOF_EXTERNAL_INFO;
+
+#endif
+
+#ifndef WOF_PROVIDER_FILE
+#define WOF_PROVIDER_FILE 2
+#define FILE_PROVIDER_CURRENT_VERSION 1
+
+typedef struct _FILE_PROVIDER_EXTERNAL_INFO_V1 {
+  DWORD Version;
+  DWORD Algorithm;
+  DWORD Flags;
+} FILE_PROVIDER_EXTERNAL_INFO_V1;
+
+#endif
+
+#ifndef ERROR_COMPRESSION_NOT_BENEFICIAL
+#define ERROR_COMPRESSION_NOT_BENEFICIAL 344
+#endif
+
+int CompactOsCompressFile(HANDLE h, DWORD algorithm)
+{
+  struct {
+    WOF_EXTERNAL_INFO Wof;
+    FILE_PROVIDER_EXTERNAL_INFO_V1 FileProvider;
+  } wfp;
+  wfp.Wof.Version = WOF_CURRENT_VERSION;
+  wfp.Wof.Provider = WOF_PROVIDER_FILE;
+  wfp.FileProvider.Version = FILE_PROVIDER_CURRENT_VERSION;
+  wfp.FileProvider.Algorithm = algorithm;
+  wfp.FileProvider.Flags = 0;
+
+  if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING, &wfp, sizeof(wfp), 0, 0, 
0, 0))
+    {
+      if (GetLastError() != ERROR_COMPRESSION_NOT_BENEFICIAL)
+        return -1;
+      return 0;
+    }
+
+  return 1;
+}
diff --git a/compactos.h b/compactos.h
new file mode 100644
index 0000000..f187718
--- /dev/null
+++ b/compactos.h
@@ -0,0 +1,25 @@
+//
+// compactos.h
+//
+// Copyright (C) 2021 Christian Franke
+//
+// SPDX-License-Identifier: MIT
+//
+
+#ifndef COMPACTOS_H
+#define COMPACTOS_H
+
+#include <windows.h>
+
+/* Not yet provided by w32api headers. */
+#ifndef FILE_PROVIDER_COMPRESSION_XPRESS4K
+#define FILE_PROVIDER_COMPRESSION_XPRESS4K  0
+#define FILE_PROVIDER_COMPRESSION_LZX       1
+#define FILE_PROVIDER_COMPRESSION_XPRESS8K  2
+#define FILE_PROVIDER_COMPRESSION_XPRESS16K 3
+#endif
+
+// Returns: 1=compressed, 0=not compressed, -1=error
+int CompactOsCompressFile(HANDLE h, DWORD algorithm);
+
+#endif // COMPACTOS_H
diff --git a/io_stream_cygfile.cc b/io_stream_cygfile.cc
index 2d0716f..97e70db 100644
--- a/io_stream_cygfile.cc
+++ b/io_stream_cygfile.cc
@@ -18,6 +18,9 @@
 #include "filemanip.h"
 #include "mkdir.h"
 #include "mount.h"
+#include "compactos.h"
+
+#include "getopt++/StringOption.h"
 
 #include <stdlib.h>
 #include <errno.h>
@@ -27,6 +30,45 @@
 #include "IOStreamProvider.h"
 #include "LogSingleton.h"
 
+/* Option '--compact-os ALGORITHM' */
+class CompactOsStringOption : public StringOption
+{
+public:
+  CompactOsStringOption ();
+  virtual Result Process (char const *optarg, int prefixIndex) /* override */;
+  operator int () const { return intval; }
+private:
+  int intval;
+};
+
+CompactOsStringOption::CompactOsStringOption ()
+: StringOption ("", '\0', "compact-os",
+    "Compress installed files with Compact OS "
+    "(xpress4k, xpress8k, xpress16k, lzx)", false),
+  intval (-1)
+{
+}
+
+Option::Result CompactOsStringOption::Process (char const *optarg, int 
prefixIndex)
+{
+  Result res = StringOption::Process (optarg, prefixIndex);
+  if (res != Ok)
+    return res;
+  const std::string& strval = *this;
+  if (strval == "xpress4k")
+    intval = FILE_PROVIDER_COMPRESSION_XPRESS4K;
+  else if (strval == "xpress8k")
+    intval = FILE_PROVIDER_COMPRESSION_XPRESS8K;
+  else if (strval == "xpress16k")
+    intval = FILE_PROVIDER_COMPRESSION_XPRESS16K;
+  else if (strval == "lzx")
+    intval = FILE_PROVIDER_COMPRESSION_LZX;
+  else
+    return Failed;
+  return Ok;
+}
+
+static CompactOsStringOption CompactOsOption;
 
 /* completely private iostream registration class */
 class CygFileProvider : public IOStreamProvider
@@ -59,7 +101,8 @@ CygFileProvider CygFileProvider::theInstance = 
CygFileProvider();
 
 
 std::string io_stream_cygfile::cwd("/");
-  
+bool io_stream_cygfile::compact_os_is_available = (OSMajorVersion () >= 10);
+
 // Normalise a unix style path relative to 
 // cwd.
 std::string
@@ -120,7 +163,26 @@ get_root_dir_now ()
   read_mounts (std::string ());
 }
 
-io_stream_cygfile::io_stream_cygfile (const std::string& name, const 
std::string& mode, mode_t perms) : fp(), lasterr (0), fname(), wname (NULL)
+static bool
+compactos_is_useless (const std::string& name)
+{
+  const char * const p = name.c_str();
+  if (!(!strncmp (p, "/bin/", 5) || !strncmp (p, "/sbin/", 6) || !strncmp (p, 
"/usr/", 5)))
+    return true; /* File is not in R/O tree. */
+  const size_t len = name.size(); /* >= 5 */
+  if (!strcmp (p + (len - 4), ".dll") || !strcmp (p + (len - 3), ".so")) {
+    if (!strcmp (p + (len - 11), "cygwin1.dll") || strstr (p + 5, 
"/sys-root/mingw/"))
+      return false; /* Ignored by rebase. */
+    return true; /* Rebase will open file for writing which uncompresses the 
file. */
+  }
+  if (!strcmp (p + (len - 4), ".bz2") || !strcmp (p + (len - 3), ".gz")
+      || !strcmp (p + (len - 3), ".xz"))
+    return true; /* File is already compressed. */
+  return false;
+}
+
+io_stream_cygfile::io_stream_cygfile (const std::string& name, const 
std::string& mode, mode_t perms)
+: fp(), lasterr (0), fname(), wname (NULL), compact_os_algorithm(-1)
 {
   errno = 0;
   if (!name.size())
@@ -153,6 +215,10 @@ io_stream_cygfile::io_stream_cygfile (const std::string& 
name, const std::string
        Log (LOG_TIMESTAMP) << "io_stream_cygfile: fopen(" << name << ") failed 
" << errno << " "
          << strerror(errno) << endLog;
       }
+
+      if (mode[0] == 'w' && compact_os_is_available && CompactOsOption >= 0
+         && !compactos_is_useless (name))
+       compact_os_algorithm = CompactOsOption;
     }
 }
 
@@ -367,6 +433,24 @@ io_stream_cygfile::set_mtime (time_t mtime)
                   FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
   if (h == INVALID_HANDLE_VALUE)
     return 1;
+
+  if (compact_os_algorithm >= 0)
+    {
+      /* Compact OS must be applied after last WriteFile()
+        and before SetFileTime(). */
+      int rc = CompactOsCompressFile (h, compact_os_algorithm);
+      if (rc < 0)
+       {
+         DWORD err = GetLastError();
+         Log (LOG_TIMESTAMP) << "Compact OS disabled after error " << err
+                             << " on " << fname << endLog;
+         compact_os_is_available = false;
+       }
+      else
+       Log (LOG_BABBLE) << "Compact OS algorithm " << compact_os_algorithm
+                        << (rc == 0 ? " not" : "") << " applied to " << fname 
<< endLog;
+    }
+
   SetFileTime (h, 0, 0, &ftime);
   CloseHandle (h);
   return 0;
diff --git a/io_stream_cygfile.h b/io_stream_cygfile.h
index 1ece242..b977909 100644
--- a/io_stream_cygfile.h
+++ b/io_stream_cygfile.h
@@ -61,7 +61,9 @@ private:
   std::string fname;
   wchar_t *wname;
   wchar_t *w_str ();
+  int compact_os_algorithm;
   static std::string cwd;
+  static bool compact_os_is_available;
 };
 
 #endif /* SETUP_IO_STREAM_CYGFILE_H */
-- 
2.31.1

Reply via email to