This experimental patch allows to reduce the footprint of a Cygwin
installation on Windows 10.
The I/O-control itself is in a new file compactos.cc because
WIN32_LEAN_AND_MEAN (win32.h) must not be used and some required
definitions are still missing in the current headers.
Test results with 64bit Cygwin (Disk space used without / with
--compact-os):
Base installation: 135MiB / 66,1 MiB (-51%)
Installation with g++, Mingw, Perl, Python, Tex, ...: 2.19GiB / 854MiB
(-62%)
Base installation with NTFS compression: 78.7MiB (results in significant
file fragmentation, Compact OS does not)
--
Regards,
Christian
From 05b92f618371ea05d381aacf842f74fb2083d027 Mon Sep 17 00:00:00 2001
From: Christian Franke <christian.fra...@t-online.de>
Date: Sat, 8 May 2021 21:25:07 +0200
Subject: [PATCH] Add new option '--compact-os'.
If specified, Compact OS LZX compression is applied to files below
/bin, /sbin and /usr. DLL files are excluded because rebase will
open these files again for writing.
---
Makefile.am | 2 ++
compactos.cc | 62 ++++++++++++++++++++++++++++++++++++++++++++
compactos.h | 26 +++++++++++++++++++
io_stream_cygfile.cc | 46 ++++++++++++++++++++++++++++++--
io_stream_cygfile.h | 2 ++
5 files changed, 136 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..2f1d1df
--- /dev/null
+++ b/compactos.cc
@@ -0,0 +1,62 @@
+//
+// compactos.cc
+//
+// Copyright (C) 2021 Christian Franke
+//
+// SPDX-License-Identifier: MIT
+//
+
+#include "compactos.h"
+
+#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
+#define WOF_PROVIDER_FILE 2
+
+typedef struct _WOF_EXTERNAL_INFO {
+ DWORD Version;
+ DWORD Provider;
+} WOF_EXTERNAL_INFO;
+
+#endif
+
+#ifndef FILE_PROVIDER_CURRENT_VERSION
+#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..c1470f1
--- /dev/null
+++ b/compactos.h
@@ -0,0 +1,26 @@
+//
+// compactos.h
+//
+// Copyright (C) 2021 Christian Franke
+//
+// SPDX-License-Identifier: MIT
+//
+
+#ifndef COMPACTOS_H
+#define COMPACTOS_H
+
+#ifndef _INC_WINDOWS
+#include <windows.h>
+#endif
+
+#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..6be2940 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++/BoolOption.h"
#include <stdlib.h>
#include <errno.h>
@@ -27,6 +30,7 @@
#include "IOStreamProvider.h"
#include "LogSingleton.h"
+static BoolOption CompactOsOption (false, '\0', "compact-os", "Compress
installed files with Compact OS LZX");
/* completely private iostream registration class */
class CygFileProvider : public IOStreamProvider
@@ -59,7 +63,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 +125,22 @@ 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"))
+ return true; /* Rebase will open file for writing which uncompresses the
file. */
+ if (!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 +173,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
+ && !compactos_is_useless (name))
+ compact_os_algorithm = FILE_PROVIDER_COMPRESSION_LZX; /* best */
}
}
@@ -367,6 +391,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 during last GENERIC_WRITE access
+ 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