Cygwin support (#499)

This commit is contained in:
Ben RUBSON 2018-03-26 23:36:16 +02:00 committed by GitHub
commit a220c395f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 167 additions and 28 deletions

View File

@ -1,3 +1,5 @@
set(CMAKE_LEGACY_CYGWIN_WIN32 0)
# 3.0.2 preferred, but we can often get by with 2.8.
cmake_minimum_required(VERSION 2.8)
if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} LESS 3.0.2)
@ -73,16 +75,27 @@ if (APPLE)
endif()
endif()
if (WIN32 OR APPLE)
if (CYGWIN OR WIN32 OR APPLE)
set(DEFAULT_CASE_INSENSITIVE TRUE)
else()
set(DEFAULT_CASE_INSENSITIVE FALSE)
endif()
if (CYGWIN)
find_program(PKILL NAMES "pkill")
if(NOT PKILL)
message(FATAL_ERROR "pkill not found, please install procps-ng package.")
endif()
endif()
# Check for FUSE.
find_package (FUSE REQUIRED)
include_directories (SYSTEM ${FUSE_INCLUDE_DIR})
add_definitions (-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=29)
if (CYGWIN)
# Cygwin build is intended to use WinFsp
add_definitions(-DCYGFUSE)
endif()
# Check for OpenSSL.
find_package (OpenSSL REQUIRED)
@ -92,8 +105,10 @@ find_program (POD2MAN pod2man)
# Check for include files and stdlib properties.
include (CheckIncludeFileCXX)
check_include_file_cxx (attr/xattr.h HAVE_ATTR_XATTR_H)
check_include_file_cxx (sys/xattr.h HAVE_SYS_XATTR_H)
if (NOT CYGWIN)
check_include_file_cxx (attr/xattr.h HAVE_ATTR_XATTR_H)
check_include_file_cxx (sys/xattr.h HAVE_SYS_XATTR_H)
endif()
include(CheckStructHasMember)
check_struct_has_member("struct dirent" d_type dirent.h HAVE_DIRENT_D_TYPE LANGUAGE CXX)

View File

@ -81,7 +81,10 @@ How about a nice email instead?
## Windows
A Windows port exists and is maintained by JetWhiz in the GitHub
EncFS works on Cygwin, see [the wiki](https://github.com/vgough/encfs/wiki)
for additional info.
A Windows port also exists and is maintained by JetWhiz in the GitHub
[jetwhiz/encfs4win](https://github.com/jetwhiz/encfs4win) repository.
## FAQ

View File

@ -171,6 +171,15 @@ void EncFS_Context::eraseNode(const char *path,
Lock lock(contextMutex);
auto it = openFiles.find(std::string(path));
#ifdef __CYGWIN__
// When renaming a file, Windows first opens it, renames it and then closes it
// Filenode may have then been renamed too
if (it == openFiles.end()) {
RLOG(WARNING) << "Filenode to erase not found, file has certainly be renamed: "
<< path;
return;
}
#endif
rAssert(it != openFiles.end());
auto &list = it->second;

View File

@ -599,11 +599,26 @@ int DirNode::rename(const char *fromPlaintext, const char *toPlaintext) {
if (renameOp) {
renameOp->undo();
}
} else if (preserve_mtime) {
struct utimbuf ut;
ut.actime = st.st_atime;
ut.modtime = st.st_mtime;
::utime(toCName.c_str(), &ut);
}
else {
#ifdef __CYGWIN__
// When renaming a file, Windows first opens it, renames it and then closes it
// We then must decrease the target openFiles count
// We could recreate the source so that close will not (silently) fails,
// however it will update modification time of the file, so break what we do below.
// Let's simply warn in eraseNode().
if (!isDirectory(toCName.c_str())) {
std::shared_ptr<FileNode> toNode = findOrCreate(toPlaintext);
ctx->eraseNode(toPlaintext, toNode);
//ctx->putNode(fromPlaintext, toNode);
}
#endif
if (preserve_mtime) {
struct utimbuf ut;
ut.actime = st.st_atime;
ut.modtime = st.st_mtime;
::utime(toCName.c_str(), &ut);
}
}
} catch (encfs::Error &err) {
// exception from renameNode, just show the error and continue..
@ -739,7 +754,9 @@ int DirNode::unlink(const char *plaintextName) {
Lock _lock(mutex);
int res = 0;
// Windows does not allow deleting opened files, so no need to check
// There is this "issue" however : https://github.com/billziss-gh/winfsp/issues/157
#ifndef __CYGWIN__
if ((ctx != nullptr) && ctx->lookupNode(plaintextName)) {
// If FUSE is running with "hard_remove" option where it doesn't
// hide open files for us, then we can't allow an unlink of an open
@ -747,14 +764,16 @@ int DirNode::unlink(const char *plaintextName) {
RLOG(WARNING) << "Refusing to unlink open file: " << cyName
<< ", hard_remove option "
"is probably in effect";
res = -EBUSY;
} else {
string fullName = rootDir + cyName;
res = ::unlink(fullName.c_str());
if (res == -1) {
res = -errno;
VLOG(1) << "unlink error: " << strerror(-res);
}
return -EBUSY;
}
#endif
int res = 0;
string fullName = rootDir + cyName;
res = ::unlink(fullName.c_str());
if (res == -1) {
res = -errno;
VLOG(1) << "unlink error: " << strerror(-res);
}
return res;

View File

@ -6,6 +6,13 @@
#include "easylogging++.h"
#include <stdexcept>
// Cygwin / WinFsp does not support EBADMSG yet
// https://github.com/billziss-gh/winfsp/issues/156
#ifdef __CYGWIN__
#undef EBADMSG
#define EBADMSG EINVAL
#endif
namespace encfs {
class Error : public std::runtime_error {

View File

@ -1089,7 +1089,7 @@ RootPtr createV6Config(EncFS_Context *ctx,
alg = findCipherAlgorithm("AES", keySize);
// If case-insensitive system, opt for Block32 filename encoding
#if defined(__APPLE__) || defined(WIN32)
#if defined(DEFAULT_CASE_INSENSITIVE)
nameIOIface = BlockNameIO::CurrentInterface(true);
#else
nameIOIface = BlockNameIO::CurrentInterface();
@ -1739,6 +1739,14 @@ void unmountFS(const char *mountPoint) {
// fuse_unmount does not work on Mac OS, see #428
unmount(mountPoint, MNT_FORCE);
#endif
#ifdef __CYGWIN__
if(fork() == 0)
{
execl("/usr/bin/pkill", "/usr/bin/pkill", "-f", string("(^|/)encfs .*/.* ").append(mountPoint).append("?( |$)").c_str(), (char *)0);
}
int status;
wait(&status);
#endif
}
int remountFS(EncFS_Context *ctx) {

View File

@ -72,6 +72,7 @@ enum ConfigMode { Config_Prompt, Config_Standard, Config_Paranoia };
struct EncFS_Opts {
std::string rootDir;
std::string mountPoint; // where to make filesystem visible
std::string cygDrive; // Cygwin mount drive
bool createIfNotFound; // create filesystem if not found
bool idleTracking; // turn on idle monitoring of filesystem
bool mountOnDemand; // mounting on-demand

View File

@ -167,6 +167,13 @@ static int withFileNode(const char *opName, const char *path,
if (fi != nullptr && fi->fh != 0) {
auto node = ctx->lookupFuseFh(fi->fh);
if (node == nullptr) {
#ifdef __CYGWIN__
if (strcmp(opName, "flush") == 0) {
RLOG(WARNING) << "Filenode to flush not found, file has certainly be renamed: "
<< path;
return 0;
}
#endif
auto msg = "fh=" + std::to_string(fi->fh) + " not found in fuseFhMap";
throw Error(msg.c_str());
}

View File

@ -649,6 +649,8 @@ Support, bug reports... : B<https://github.com/vgough/encfs>.
Mailing list : none.
Cygwin : B<https://github.com/vgough/encfs/wiki>.
Windows port : B<https://github.com/jetwhiz/encfs4win>.
=head1 SEE ALSO

View File

@ -380,6 +380,11 @@ static bool processArgs(int argc, char *argv[],
/* Disable kernel dentry cache
* Fallout unknown, disabling for safety */
PUSHARG("-oentry_timeout=0");
#ifdef __CYGWIN__
// Should be enforced due to attr_timeout=0, but does not seem to work correctly
// https://github.com/billziss-gh/winfsp/issues/155
PUSHARG("-oFileInfoTimeout=0");
#endif
break;
case 'm':
out->opts->mountOnDemand = true;
@ -542,18 +547,38 @@ static bool processArgs(int argc, char *argv[],
if (!isDirectory(out->opts->rootDir.c_str()) &&
!userAllowMkdir(out->opts->annotate ? 1 : 0, out->opts->rootDir.c_str(),
0700)) {
cerr << _("Unable to locate root directory, aborting.");
cerr << _("Unable to locate root directory, aborting.") << endl;
return false;
}
#ifdef __CYGWIN__
if (isDirectory(out->opts->mountPoint.c_str())) {
cerr << _("Mount point must not exist before mouting, aborting.") << endl;
return false;
}
if ((strncmp(out->opts->mountPoint.c_str(), "/cygdrive/", 10) != 0) ||
(out->opts->mountPoint.length() != 12)) {
cerr << _("A drive is prefered for mouting, ")
<< _("so a path like /cygdrive/x should rather be used. ")
<< _("Mounting anyway.") << endl;
}
#else
if (!isDirectory(out->opts->mountPoint.c_str()) &&
!userAllowMkdir(out->opts->annotate ? 2 : 0,
out->opts->mountPoint.c_str(), 0700)) {
cerr << _("Unable to locate mount point, aborting.");
cerr << _("Unable to locate mount point, aborting.") << endl;
return false;
}
#endif
// fill in mount path for fuse
out->fuseArgv[1] = out->opts->mountPoint.c_str();
#ifdef __CYGWIN__
if ((strncmp(out->opts->mountPoint.c_str(), "/cygdrive/", 10) == 0) &&
(out->opts->mountPoint.length() == 12)) {
out->opts->cygDrive = out->opts->mountPoint.substr(10,1).append(":");
out->fuseArgv[1] = out->opts->cygDrive.c_str();
}
#endif
return true;
}

View File

@ -107,6 +107,10 @@ sub newWorkingDir
our $raw = "$workingDir/raw";
our $crypt = "$workingDir/crypt";
if ($^O eq "cygwin")
{
$crypt = "/cygdrive/x";
}
}
# Test Corruption
@ -124,7 +128,8 @@ sub corruption
ok( open(IN, "< $crypt/corrupt"), "open corrupted file");
my $content;
$result = read(IN, $content, 20);
ok($!{EBADMSG} && (! defined $result), "corrupted file with MAC returns read error: $!");
# Cygwin returns EINVAL for now
ok(($!{EBADMSG} || $!{EINVAL}) && (! defined $result), "corrupted file with MAC returns read error: $!");
}
# Test internal modification
@ -363,7 +368,10 @@ sub mount
# When these fail, the rest of the tests makes no sense
mkdir($raw) || BAIL_OUT("Could not create $raw: $!");
mkdir($crypt) || BAIL_OUT("Could not create $crypt: $!");
if ($^O ne "cygwin")
{
mkdir($crypt) || BAIL_OUT("Could not create $crypt: $!");
}
delete $ENV{"ENCFS6_CONFIG"};
remount($args);
@ -425,8 +433,15 @@ sub create_unmount_remount
{
my $crypt = "$workingDir/create_remount.crypt";
my $mnt = "$workingDir/create_remount.mnt";
if ($^O eq "cygwin")
{
$mnt = "/cygdrive/y";
}
mkdir($crypt) || BAIL_OUT($!);
mkdir($mnt) || BAIL_OUT($!);
if ($^O ne "cygwin")
{
mkdir($mnt) || BAIL_OUT($!);
}
system("./build/encfs --standard --extpass=\"echo test\" $crypt $mnt 2>&1");
ok( $? == 0, "encfs command returns 0") || return;
@ -472,8 +487,15 @@ sub checkWriteError
else {
my $crypt = "$workingDir/checkWriteError.crypt";
my $mnt = "$workingDir/checkWriteError.mnt";
if ($^O eq "cygwin")
{
$mnt = "/cygdrive/z";
}
mkdir($crypt) || BAIL_OUT($!);
mkdir($mnt) || BAIL_OUT($!);
if ($^O ne "cygwin")
{
mkdir($mnt) || BAIL_OUT($!);
}
system("$sudo_cmd mount -t tmpfs -o size=1m tmpfs $crypt");
ok( $? == 0, "mount command returns 0") || return;
system("./build/encfs --standard --extpass=\"echo test\" $crypt $mnt 2>&1");

View File

@ -49,9 +49,23 @@ sub newWorkingDir
our $plain = "$workingDir/plain";
mkdir($plain);
our $ciphertext = "$workingDir/ciphertext";
mkdir($ciphertext);
if ($^O ne "cygwin")
{
mkdir($ciphertext);
}
else
{
$ciphertext = "/cygdrive/x";
}
our $decrypted = "$workingDir/decrypted";
mkdir($decrypted);
if ($^O ne "cygwin")
{
mkdir($decrypted);
}
else
{
$decrypted = "/cygdrive/y";
}
}
# Helper function
@ -231,7 +245,14 @@ encfsctl_cat_test();
symlink_test("/"); # absolute
symlink_test("foo"); # relative
symlink_test("/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/15/17/18"); # long
symlink_test("!§\$%&/()\\<>#+="); # special characters
if ($^O ne "cygwin")
{
symlink_test("!§\$%&/()\\<>#+="); # special characters
}
else
{
symlink_test("!§\$%&/()//<>#+="); # special characters but without \ which is not Windows compliant
} # Absolute symlinks may failed on Windows : https://github.com/billziss-gh/winfsp/issues/153
symlink_test("$plain/foo");
writesDenied();