public inbox for cygwin-apps@cygwin.com
 help / color / mirror / Atom feed
From: Christian Franke <Christian.Franke@t-online.de>
To: "cygwin-apps@cygwin.com" <cygwin-apps@cygwin.com>
Subject: Re: [PATCH setup] Add new option '--compact-os'
Date: Fri, 14 May 2021 09:55:08 +0200	[thread overview]
Message-ID: <bde490c1-65b1-4aa8-5c46-243ac85edb38@t-online.de> (raw)
In-Reply-To: <4b45c450-75af-1ac4-0bd6-86ad0211dcba@t-online.de>

[-- Attachment #1: Type: text/plain, Size: 1042 bytes --]

Christian Franke wrote:
> 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.
>

Sorry - I missed a possible segfault in the check for cygwin1.dll.

Fixed version attached.



[-- Attachment #2: 0001-Add-new-option-compact-os-ALGORITHM.patch --]
[-- Type: text/plain, Size: 8129 bytes --]

From 109190447edfb1abab61942c3661def62014fe24 Mon Sep 17 00:00:00 2001
From: Christian Franke <christian.franke@t-online.de>
Date: Fri, 14 May 2021 09:50:12 +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 | 89 +++++++++++++++++++++++++++++++++++++++++++-
 io_stream_cygfile.h  |  2 +
 5 files changed, 179 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..a9150e7 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,27 @@ 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 ((len >= 5 + 11 && !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 +216,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 +434,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	other threads:[~2021-05-14  7:55 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-08 20:03 Christian Franke
2021-05-12 15:14 ` Jon Turney
2021-05-12 17:50   ` Christian Franke
2021-05-12 18:35     ` ASSI
2021-05-12 18:48       ` Achim Gratz
2021-05-13 15:09         ` Christian Franke
2021-05-13 14:55     ` Jon Turney
2021-05-14  7:27       ` Christian Franke
2021-05-14  7:55         ` Christian Franke [this message]
2021-07-18 13:44           ` Jon Turney
2021-05-12 18:04   ` Corinna Vinschen
2021-05-13 14:42     ` Christian Franke
2021-05-13 14:45       ` Christian Franke
2021-05-17 10:17       ` Corinna Vinschen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=bde490c1-65b1-4aa8-5c46-243ac85edb38@t-online.de \
    --to=christian.franke@t-online.de \
    --cc=cygwin-apps@cygwin.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).