From 1ff7404f9d038461643e8a0d0e14f761a593fbef Mon Sep 17 00:00:00 2001 From: bingbing8 Date: Tue, 9 May 2017 18:22:17 -0700 Subject: [PATCH 1/6] update fileio_stat to replace _wstat64 with GetFileAttributesExW --- contrib/win32/win32compat/fileio.c | 69 ++++++++++++++++---- contrib/win32/win32compat/inc/sys/time.h | 6 +- contrib/win32/win32compat/misc.c | 77 +++++++++++++++++++++-- contrib/win32/win32compat/misc_internal.h | 3 + 4 files changed, 135 insertions(+), 20 deletions(-) diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c index 7a6cc107b3a8..a6ba6aeb0af8 100644 --- a/contrib/win32/win32compat/fileio.c +++ b/contrib/win32/win32compat/fileio.c @@ -634,26 +634,71 @@ fileio_fstat(struct w32_io* pio, struct _stat64 *buf) return _fstat64(fd, buf); } +static int +get_drive() { + wchar_t buffer[MAX_PATH]; + if (GetCurrentDirectoryW(MAX_PATH, buffer) >= 2) { + buffer[0] = towupper(buffer[0]); + if (buffer[0] >= L'A' && buffer[0] <= L'Z' && buffer[1] == L':') + return buffer[0] - L'A' + 1; + } + return 0; +} + int fileio_stat(const char *path, struct _stat64 *buf) { wchar_t* wpath = NULL; - int r = -1; - - if ((wpath = utf8_to_utf16(path)) == NULL) - fatal("failed to covert input arguments"); - - r = _wstat64(wpath, buf); - - /* - * If we doesn't have sufficient permissions then _wstat64() is returning "file not found" - * TODO - Replace the above call with GetFileAttributesEx - */ + WIN32_FILE_ATTRIBUTE_DATA attributes = { 0 }; + int ret = -1, len = 0; + debug("enterring fileio_stat. Path %s", path); + if ((wpath = utf8_to_utf16(path)) == NULL) { + errno = errno_from_Win32LastError(); + debug3("utf8_to_utf16 failed for file:%s error:%d", path, GetLastError()); + return -1; + } + memset(buf, 0, sizeof(struct _stat64)); + if (GetFileAttributesExW(wpath, GetFileExInfoStandard, &attributes) == FALSE) { + errno = errno_from_Win32LastError(); + debug3("GetFileAttributesExW with last error %d", GetLastError()); + goto cleanup; + } + + len = wcslen(wpath); + + buf->st_ino = 0; /* Has no meaning in the FAT, HPFS, or NTFS file systems*/ + buf->st_gid = 0; /* UNIX - specific; has no meaning on windows */ + buf->st_uid = 0; /* UNIX - specific; has no meaning on windows */ + buf->st_nlink = 1; /* number of hard links. Always 1 on non - NTFS file systems.*/ + buf->st_mode |= file_attr_to_st_mode(wpath, attributes.dwFileAttributes); + buf->st_size = attributes.nFileSizeLow | (((off_t)attributes.nFileSizeHigh) << 32); + if (len > 1 && __ascii_iswalpha(*wpath) && (*(wpath + 1) == ':')) + buf->st_dev = buf->st_rdev = towupper(*wpath) - L'A'; /* drive num */ + else + buf->st_dev = buf->st_rdev = get_drive() - 1; + debug("fileio_stat. buf->st_dev %d", buf->st_dev); + file_time_to_unix_time(&(attributes.ftLastAccessTime), &(buf->st_atime)); + file_time_to_unix_time(&(attributes.ftLastWriteTime), &(buf->st_mtime)); + file_time_to_unix_time(&(attributes.ftCreationTime), &(buf->st_ctime)); + + if (attributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + WIN32_FIND_DATAW findbuf = { 0 }; + HANDLE handle = FindFirstFileW(wpath, &findbuf); + if (handle != INVALID_HANDLE_VALUE) { + if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) { + buf->st_mode |= S_IFLNK; + } + FindClose(handle); + } + } + ret = 0; cleanup: if (wpath) free(wpath); - return r; + debug("exiting fileio_stat. Path %d", ret); + return ret; } long diff --git a/contrib/win32/win32compat/inc/sys/time.h b/contrib/win32/win32compat/inc/sys/time.h index 27b08c34b4d1..9d1b1ee10729 100644 --- a/contrib/win32/win32compat/inc/sys/time.h +++ b/contrib/win32/win32compat/inc/sys/time.h @@ -4,6 +4,6 @@ #define utimes w32_utimes int usleep(unsigned int); -int gettimeofday(struct timeval *tv, void *tz); -int nanosleep(const struct timespec *req, struct timespec *rem); -int w32_utimes(const char *filename, struct timeval *tvp); \ No newline at end of file +int gettimeofday(struct timeval *, void *); +int nanosleep(const struct timespec *, struct timespec *); +int w32_utimes(const char *, struct timeval *); \ No newline at end of file diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c index 068fb4bf327d..cf0c8c2dd109 100644 --- a/contrib/win32/win32compat/misc.c +++ b/contrib/win32/win32compat/misc.c @@ -59,6 +59,10 @@ static char* s_programdir = NULL; #define IO_REPARSE_TAG_SIS (0x80000007L) /* winnt ntifs */ #define REPARSE_MOUNTPOINT_HEADER_SIZE 8 + /* Difference in us between UNIX Epoch and Win32 Epoch */ +#define EPOCH_DELTA_US 11644473600000000ULL +#define RATE_DIFF 10000000 /* 100 nsecs */ + typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; USHORT ReparseDataLength; @@ -174,9 +178,6 @@ nanosleep(const struct timespec *req, struct timespec *rem) } } -/* Difference in us between UNIX Epoch and Win32 Epoch */ -#define EPOCH_DELTA_US 11644473600000000ULL - /* This routine is contributed by * Author: NoMachine * Copyright (c) 2009, 2010 NoMachine * All rights reserved @@ -551,16 +552,82 @@ w32_chown(const char *pathname, unsigned int owner, unsigned int group) return -1; } -static void +/* Convert a UNIX time into a Windows file time */ +void unix_time_to_file_time(ULONG t, LPFILETIME pft) { ULONGLONG ull; - ull = UInt32x32To64(t, 10000000) + 116444736000000000; + ull = UInt32x32To64(t, RATE_DIFF) + EPOCH_DELTA_US; pft->dwLowDateTime = (DWORD)ull; pft->dwHighDateTime = (DWORD)(ull >> 32); } +/* Convert a Windows file time into a UNIX time_t */ +void +file_time_to_unix_time(const LPFILETIME pft, time_t * winTime) +{ + *winTime = ((long long)pft->dwHighDateTime << 32) + pft->dwLowDateTime; + *winTime -= EPOCH_DELTA_US; + *winTime /= RATE_DIFF; /* Nano to seconds resolution */ +} + +static BOOL +is_root_or_empty(wchar_t * path) +{ + wchar_t * path_start; + BOOL has_drive_letter_and_colon; + int len; + if (!path) + return FALSE; + len = wcslen(path); + if((len > 1) && __ascii_iswalpha(path[0]) && path[1] == L':') + path_start = path + 2; + else + path_start = path; + if ((*path_start == L'\0') || ((*path_start == L'\\' || *path_start == L'/' ) && path_start[1] == L'\0')) + return TRUE; + return FALSE; +} + +static BOOL +has_executable_extension(wchar_t * path) +{ + wchar_t * last_dot; + if (!path) + return FALSE; + + last_dot = wcsrchr(path, L'.'); + if (!last_dot) + return FALSE; + if (_wcsnicmp(last_dot, L".exe", 4) != 0 && _wcsnicmp(last_dot, L".cmd", 4) != 0 && + _wcsnicmp(last_dot, L".bat", 4) != 0 && _wcsnicmp(last_dot, L".com", 4) != 0) + return FALSE; + return TRUE; +} + +int +file_attr_to_st_mode(wchar_t * path, DWORD attributes) +{ + int mode = S_IREAD; + if ((attributes & FILE_ATTRIBUTE_DIRECTORY != 0) || is_root_or_empty(path)) + mode |= S_IFDIR | _S_IEXEC; + else { + mode |= S_IFREG; + /* See if file appears to be an executable by checking its extension */ + if (has_executable_extension(path)) + mode |= _S_IEXEC; + + } + if (!(attributes & FILE_ATTRIBUTE_READONLY)) + mode |= S_IWRITE; + + // propagate owner read/write/execute bits to group/other fields. + mode |= (mode & 0700) >> 3; + mode |= (mode & 0700) >> 6; + return mode; +} + static int settimes(wchar_t * path, FILETIME *cretime, FILETIME *acttime, FILETIME *modtime) { diff --git a/contrib/win32/win32compat/misc_internal.h b/contrib/win32/win32compat/misc_internal.h index fa9a0d7b5d09..2640ba074a27 100644 --- a/contrib/win32/win32compat/misc_internal.h +++ b/contrib/win32/win32compat/misc_internal.h @@ -14,3 +14,6 @@ char* w32_programdir(); void convertToBackslash(char *str); void convertToForwardslash(char *str); + +void unix_time_to_file_time(ULONG, LPFILETIME); +void file_time_unix_time(const LPFILETIME, time_t *); From 02e46e98ba099a6c89a0aa08b8a0e00f23f5f133 Mon Sep 17 00:00:00 2001 From: bingbing8 Date: Wed, 10 May 2017 15:42:11 -0700 Subject: [PATCH 2/6] fix the timing issue --- contrib/win32/win32compat/fileio.c | 5 +++-- contrib/win32/win32compat/misc.c | 19 ++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c index a6ba6aeb0af8..473ab39233b5 100644 --- a/contrib/win32/win32compat/fileio.c +++ b/contrib/win32/win32compat/fileio.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "w32fd.h" #include "inc\utf.h" @@ -676,7 +677,7 @@ fileio_stat(const char *path, struct _stat64 *buf) if (len > 1 && __ascii_iswalpha(*wpath) && (*(wpath + 1) == ':')) buf->st_dev = buf->st_rdev = towupper(*wpath) - L'A'; /* drive num */ else - buf->st_dev = buf->st_rdev = get_drive() - 1; + buf->st_dev = buf->st_rdev = _getdrive() - 1; debug("fileio_stat. buf->st_dev %d", buf->st_dev); file_time_to_unix_time(&(attributes.ftLastAccessTime), &(buf->st_atime)); file_time_to_unix_time(&(attributes.ftLastWriteTime), &(buf->st_mtime)); @@ -692,7 +693,7 @@ fileio_stat(const char *path, struct _stat64 *buf) } FindClose(handle); } - } + } ret = 0; cleanup: if (wpath) diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c index cf0c8c2dd109..61269f13fe9e 100644 --- a/contrib/win32/win32compat/misc.c +++ b/contrib/win32/win32compat/misc.c @@ -60,8 +60,8 @@ static char* s_programdir = NULL; #define REPARSE_MOUNTPOINT_HEADER_SIZE 8 /* Difference in us between UNIX Epoch and Win32 Epoch */ -#define EPOCH_DELTA_US 11644473600000000ULL -#define RATE_DIFF 10000000 /* 100 nsecs */ +#define EPOCH_DELTA_US 116444736000000000ULL +#define RATE_DIFF 10000000ULL /* 1000 nsecs */ typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; @@ -192,17 +192,14 @@ gettimeofday(struct timeval *tv, void *tz) unsigned long long us; /* Fetch time since Jan 1, 1601 in 100ns increments */ - GetSystemTimeAsFileTime(&timehelper.ft); - - /* Convert to microseconds from 100 ns units */ - us = timehelper.ns / 10; + GetSystemTimeAsFileTime(&timehelper.ft); /* Remove the epoch difference */ - us -= EPOCH_DELTA_US; + us = timehelper.ns - EPOCH_DELTA_US; /* Stuff result into the timeval */ - tv->tv_sec = (long)(us / 1000000ULL); - tv->tv_usec = (long)(us % 1000000ULL); + tv->tv_sec = (long)(us / RATE_DIFF); + tv->tv_usec = (long)(us % RATE_DIFF); return 0; } @@ -609,8 +606,8 @@ has_executable_extension(wchar_t * path) int file_attr_to_st_mode(wchar_t * path, DWORD attributes) { - int mode = S_IREAD; - if ((attributes & FILE_ATTRIBUTE_DIRECTORY != 0) || is_root_or_empty(path)) + int mode = S_IREAD; + if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0 || is_root_or_empty(path)) mode |= S_IFDIR | _S_IEXEC; else { mode |= S_IFREG; From 05fcfe9420bfe0851be28b59250a7ca8362b7bbd Mon Sep 17 00:00:00 2001 From: bingbing8 Date: Wed, 10 May 2017 17:54:40 -0700 Subject: [PATCH 3/6] add unittest for fileio_stat --- contrib/win32/win32compat/fileio.c | 7 +--- regress/unittests/win32compat/file_tests.c | 44 +++++++++++++++++++++- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c index 473ab39233b5..c98fde78a35a 100644 --- a/contrib/win32/win32compat/fileio.c +++ b/contrib/win32/win32compat/fileio.c @@ -651,8 +651,7 @@ fileio_stat(const char *path, struct _stat64 *buf) { wchar_t* wpath = NULL; WIN32_FILE_ATTRIBUTE_DATA attributes = { 0 }; - int ret = -1, len = 0; - debug("enterring fileio_stat. Path %s", path); + int ret = -1, len = 0; if ((wpath = utf8_to_utf16(path)) == NULL) { errno = errno_from_Win32LastError(); debug3("utf8_to_utf16 failed for file:%s error:%d", path, GetLastError()); @@ -678,7 +677,6 @@ fileio_stat(const char *path, struct _stat64 *buf) buf->st_dev = buf->st_rdev = towupper(*wpath) - L'A'; /* drive num */ else buf->st_dev = buf->st_rdev = _getdrive() - 1; - debug("fileio_stat. buf->st_dev %d", buf->st_dev); file_time_to_unix_time(&(attributes.ftLastAccessTime), &(buf->st_atime)); file_time_to_unix_time(&(attributes.ftLastWriteTime), &(buf->st_mtime)); file_time_to_unix_time(&(attributes.ftCreationTime), &(buf->st_ctime)); @@ -697,8 +695,7 @@ fileio_stat(const char *path, struct _stat64 *buf) ret = 0; cleanup: if (wpath) - free(wpath); - debug("exiting fileio_stat. Path %d", ret); + free(wpath); return ret; } diff --git a/regress/unittests/win32compat/file_tests.c b/regress/unittests/win32compat/file_tests.c index 2c20edb82736..8dbda01e66d8 100644 --- a/regress/unittests/win32compat/file_tests.c +++ b/regress/unittests/win32compat/file_tests.c @@ -102,8 +102,49 @@ void file_simple_fileio() ASSERT_INT_EQ(ret, 0); close(f); TEST_DONE(); +} + +void file_simple_fileio_mode() +{ + char * small_write_buf = "sample payload", *c, small_read_buf[SMALL_RECV_BUF_SIZE]; + int ret; + FILE* f; + struct stat st; + TEST_START("file mode"); + f = fopen("tmp.txt", "w"); + ASSERT_PTR_NE(f, NULL); + fclose(f); + f = fopen("tmp.txt", "r"); + ASSERT_PTR_NE(f, NULL); + c = fgets(small_read_buf, sizeof(small_read_buf), f); + ASSERT_PTR_EQ(c, NULL); + fclose(f); + + ret = stat("tmp.txt", &st); + ASSERT_INT_EQ(ret, 0); + ASSERT_INT_EQ(st.st_size, 0); + + f = fopen("tmp.txt", "w"); + ASSERT_PTR_NE(f, NULL); + ret = fputs(small_write_buf, f); + ASSERT_INT_EQ(ret, 0); + fclose(f); + ret = stat("tmp.txt", &st); + ASSERT_INT_EQ(ret, 0); + ASSERT_INT_EQ(st.st_size, strlen(small_write_buf)); + + f = fopen("tmp.txt", "r"); + ASSERT_PTR_NE(f, NULL); + c = fgets(small_read_buf, sizeof(small_read_buf), f); + ASSERT_PTR_NE(c, NULL); + ASSERT_STRING_EQ(small_write_buf, small_read_buf); + + c = fgets(small_read_buf, sizeof(small_read_buf), f); + ASSERT_PTR_EQ(c, NULL); + fclose(f); + TEST_DONE(); } void @@ -240,7 +281,8 @@ void file_tests() { //console_io_test(); - //file_simple_fileio(); + file_simple_fileio(); + file_simple_fileio_mode(); file_blocking_io_tests(); file_nonblocking_io_tests(); file_select_tests(); From 63148b713c1e05b49f98f2a1f4d991f073a4850f Mon Sep 17 00:00:00 2001 From: bingbing8 Date: Wed, 10 May 2017 18:37:35 -0700 Subject: [PATCH 4/6] minor update on test script --- contrib/win32/openssh/OpenSSHBuildHelper.psm1 | 10 +++++++--- regress/pesterTests/Hostkey_fileperm.Tests.ps1 | 9 +++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/contrib/win32/openssh/OpenSSHBuildHelper.psm1 b/contrib/win32/openssh/OpenSSHBuildHelper.psm1 index 0e5e22213bf0..18b946380ede 100644 --- a/contrib/win32/openssh/OpenSSHBuildHelper.psm1 +++ b/contrib/win32/openssh/OpenSSHBuildHelper.psm1 @@ -339,7 +339,7 @@ function Package-OpenSSH if ($DestinationPath -ne "") { if (Test-Path $DestinationPath) { - Remove-Item $DestinationPath\* -Force -Recurse + Remove-Item $DestinationPath\* -Force -Recurse -ErrorAction SilentlyContinue } else { New-Item -ItemType Directory $DestinationPath -Force | Out-Null @@ -542,18 +542,22 @@ function UnInstall-OpenSSH [string]$OpenSSHDir = "$env:SystemDrive\OpenSSH" ) + if (-not (Test-Path $OpenSSHDir)) + { + return + } + Push-Location $OpenSSHDir if((Get-Service ssh-agent -ErrorAction Ignore) -ne $null) { Stop-Service ssh-agent -Force } &( "$OpenSSHDir\uninstall-sshd.ps1") - &( "$OpenSSHDir\uninstall-sshlsa.ps1") $machinePath = [Environment]::GetEnvironmentVariable('Path', 'MACHINE') $newMachineEnvironmentPath = $machinePath if ($machinePath.ToLower().Contains($OpenSSHDir.ToLower())) { - $newMachineEnvironmentPath.Replace("$OpenSSHDir;", '') + $newMachineEnvironmentPath = $newMachineEnvironmentPath.Replace("$OpenSSHDir;", '') $env:Path = $env:Path.Replace("$OpenSSHDir;", '') } diff --git a/regress/pesterTests/Hostkey_fileperm.Tests.ps1 b/regress/pesterTests/Hostkey_fileperm.Tests.ps1 index 7a0d2da32592..7ebb8a8e5bb9 100644 --- a/regress/pesterTests/Hostkey_fileperm.Tests.ps1 +++ b/regress/pesterTests/Hostkey_fileperm.Tests.ps1 @@ -63,6 +63,15 @@ Describe "Tests for host keys file permission" -Tags "CI" { Remove-Item -Path $filePath -Force -ErrorAction ignore } + AfterAll { + if(Test-path $hostKeyFilePath -PathType Leaf){ + Set-SecureFileACL -filepath $hostKeyFilePath + } + if(Test-path "$hostKeyFilePath.pub" -PathType Leaf){ + Set-SecureFileACL -filepath "$hostKeyFilePath.pub" + } + } + It 'Host keys -- positive (Secured private key and sshd can access to public key file)' { #setup to have current user as owner and grant it full control Set-SecureFileACL -filepath $hostKeyFilePath From 37eb024cbc908587cd5452369ee4f40182199941 Mon Sep 17 00:00:00 2001 From: bingbing8 Date: Wed, 10 May 2017 18:46:50 -0700 Subject: [PATCH 5/6] remove getdriver function --- contrib/win32/win32compat/fileio.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c index c98fde78a35a..3fc9241909fd 100644 --- a/contrib/win32/win32compat/fileio.c +++ b/contrib/win32/win32compat/fileio.c @@ -635,17 +635,6 @@ fileio_fstat(struct w32_io* pio, struct _stat64 *buf) return _fstat64(fd, buf); } -static int -get_drive() { - wchar_t buffer[MAX_PATH]; - if (GetCurrentDirectoryW(MAX_PATH, buffer) >= 2) { - buffer[0] = towupper(buffer[0]); - if (buffer[0] >= L'A' && buffer[0] <= L'Z' && buffer[1] == L':') - return buffer[0] - L'A' + 1; - } - return 0; -} - int fileio_stat(const char *path, struct _stat64 *buf) { From 898ce049608fd6ef859689b8c65047b02a812893 Mon Sep 17 00:00:00 2001 From: bingbing8 Date: Tue, 16 May 2017 13:40:22 -0700 Subject: [PATCH 6/6] add one line comments --- contrib/win32/win32compat/misc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c index 9c9c875bd4f7..bfe1b85a50cc 100644 --- a/contrib/win32/win32compat/misc.c +++ b/contrib/win32/win32compat/misc.c @@ -581,6 +581,7 @@ is_root_or_empty(wchar_t * path) path_start = path + 2; else path_start = path; + /*path like c:\, /, \ are root directory*/ if ((*path_start == L'\0') || ((*path_start == L'\\' || *path_start == L'/' ) && path_start[1] == L'\0')) return TRUE; return FALSE;