Artur Zaprzala's rdp2tcp channel driver
This commit is contained in:
Родитель
8dcac1adad
Коммит
2cfb2839f4
|
@ -0,0 +1,22 @@
|
|||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel("rdp2tcp")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
|
@ -0,0 +1,10 @@
|
|||
set(OPTION_DEFAULT OFF)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT OFF)
|
||||
|
||||
define_channel_options(NAME "rdp2tcp" TYPE "static"
|
||||
DESCRIPTION "Tunneling TCP over RDP"
|
||||
DEFAULT ${OPTION_DEFAULT})
|
||||
|
||||
define_channel_client_options(${OPTION_CLIENT_DEFAULT})
|
||||
define_channel_server_options(${OPTION_SERVER_DEFAULT})
|
|
@ -0,0 +1,27 @@
|
|||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel_client("rdp2tcp")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
rdp2tcp_main.c)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "VirtualChannelEntryEx")
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
|
@ -0,0 +1,245 @@
|
|||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* rdp2tcp Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2017 Artur Zaprzala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/pipe.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include <freerdp/svc.h>
|
||||
|
||||
#define RDP2TCP_CHAN_NAME "rdp2tcp"
|
||||
|
||||
static int const debug = 0;
|
||||
|
||||
typedef struct {
|
||||
HANDLE hStdOutputRead;
|
||||
HANDLE hStdInputWrite;
|
||||
HANDLE hProcess;
|
||||
HANDLE copyThread;
|
||||
HANDLE writeComplete;
|
||||
DWORD openHandle;
|
||||
void *initHandle;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
|
||||
char buffer[16*1024];
|
||||
} Plugin;
|
||||
|
||||
|
||||
static int init_external_addin(Plugin *plugin) {
|
||||
SECURITY_ATTRIBUTES saAttr = {
|
||||
.nLength = sizeof(SECURITY_ATTRIBUTES),
|
||||
.bInheritHandle = TRUE,
|
||||
.lpSecurityDescriptor = NULL
|
||||
};
|
||||
STARTUPINFO siStartInfo = {
|
||||
.cb = sizeof(STARTUPINFO),
|
||||
.hStdError = GetStdHandle(STD_ERROR_HANDLE),
|
||||
.dwFlags = STARTF_USESTDHANDLES
|
||||
};
|
||||
PROCESS_INFORMATION procInfo = {};
|
||||
|
||||
// Create pipes
|
||||
if (!CreatePipe(&plugin->hStdOutputRead, &siStartInfo.hStdOutput, &saAttr, 0)) {
|
||||
perror("stdout CreatePipe");
|
||||
return -1;
|
||||
}
|
||||
if (!SetHandleInformation(plugin->hStdOutputRead, HANDLE_FLAG_INHERIT, 0)) {
|
||||
perror("stdout SetHandleInformation");
|
||||
return -1;
|
||||
}
|
||||
if (!CreatePipe(&siStartInfo.hStdInput, &plugin->hStdInputWrite, &saAttr, 0)) {
|
||||
perror("stdin CreatePipe");
|
||||
return -1;
|
||||
}
|
||||
if (!SetHandleInformation(plugin->hStdInputWrite, HANDLE_FLAG_INHERIT, 0)) {
|
||||
perror("stdin SetHandleInformation");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Execute plugin
|
||||
if (!CreateProcess(NULL,
|
||||
plugin->channelEntryPoints.pExtendedData, // command line
|
||||
NULL, // process security attributes
|
||||
NULL, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
0, // creation flags
|
||||
NULL, // use parent's environment
|
||||
NULL, // use parent's current directory
|
||||
&siStartInfo, // STARTUPINFO pointer
|
||||
&procInfo // receives PROCESS_INFORMATION
|
||||
)) {
|
||||
perror("fork for addin");
|
||||
return -1;
|
||||
}
|
||||
|
||||
plugin->hProcess = procInfo.hProcess;
|
||||
CloseHandle(procInfo.hThread);
|
||||
CloseHandle(siStartInfo.hStdOutput);
|
||||
CloseHandle(siStartInfo.hStdInput);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dumpData(char *data, unsigned length) {
|
||||
unsigned const limit = 98;
|
||||
unsigned l = length>limit ? limit/2 : length;
|
||||
for (unsigned i=0; i<l; ++i) {
|
||||
printf("%02hhx", data[i]);
|
||||
}
|
||||
if (length>limit) {
|
||||
printf("...");
|
||||
for (unsigned i=length-l; i<length; ++i)
|
||||
printf("%02hhx", data[i]);
|
||||
}
|
||||
puts("");
|
||||
}
|
||||
|
||||
static DWORD copyThread(void *data) {
|
||||
Plugin *plugin = (Plugin *)data;
|
||||
size_t const bufsize = 16*1024;
|
||||
while (1) {
|
||||
DWORD dwRead;
|
||||
char *buffer = malloc(bufsize);
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "rdp2tcp copyThread: malloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
//if (!ReadFile(plugin->hStdOutputRead, plugin->buffer, sizeof plugin->buffer, &dwRead, NULL))
|
||||
if (!ReadFile(plugin->hStdOutputRead, buffer, bufsize, &dwRead, NULL))
|
||||
return -1;
|
||||
if (debug>1) {
|
||||
printf(">%8u ", (unsigned)dwRead);
|
||||
dumpData(buffer, dwRead);
|
||||
}
|
||||
if (plugin->channelEntryPoints.pVirtualChannelWriteEx(plugin->initHandle, plugin->openHandle, buffer, dwRead, NULL) != CHANNEL_RC_OK) {
|
||||
fprintf(stderr, "rdp2tcp copyThread failed %i\n", (int)dwRead);
|
||||
return -1;
|
||||
}
|
||||
WaitForSingleObject(plugin->writeComplete, INFINITE);
|
||||
ResetEvent(plugin->writeComplete);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void closeChannel(Plugin *plugin) {
|
||||
if (debug)
|
||||
puts("rdp2tcp closing channel");
|
||||
plugin->channelEntryPoints.pVirtualChannelCloseEx(plugin->initHandle, plugin->openHandle);
|
||||
}
|
||||
|
||||
static void dataReceived(Plugin *plugin, void *pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags) {
|
||||
DWORD dwWritten;
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_SUSPEND) {
|
||||
if (debug)
|
||||
puts("rdp2tcp Channel Suspend");
|
||||
return;
|
||||
}
|
||||
if (dataFlags & CHANNEL_FLAG_RESUME) {
|
||||
if (debug)
|
||||
puts("rdp2tcp Channel Resume");
|
||||
return;
|
||||
}
|
||||
if (debug>1) {
|
||||
printf("<%c%3u/%3u ", dataFlags & CHANNEL_FLAG_FIRST ? ' ': '+', totalLength, dataLength);
|
||||
dumpData(pData, dataLength);
|
||||
}
|
||||
if (dataFlags & CHANNEL_FLAG_FIRST) {
|
||||
if (!WriteFile(plugin->hStdInputWrite, &totalLength, sizeof(totalLength), &dwWritten, NULL))
|
||||
closeChannel(plugin);
|
||||
}
|
||||
if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten, NULL))
|
||||
closeChannel(plugin);
|
||||
}
|
||||
|
||||
static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam, DWORD openHandle, UINT event, LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags) {
|
||||
Plugin *plugin = (Plugin*)lpUserParam;
|
||||
switch (event) {
|
||||
case CHANNEL_EVENT_DATA_RECEIVED:;
|
||||
dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
|
||||
break;
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
SetEvent(plugin->writeComplete);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event, LPVOID pData, UINT dataLength) {
|
||||
Plugin *plugin = (Plugin*)lpUserParam;
|
||||
switch (event) {
|
||||
case CHANNEL_EVENT_CONNECTED:
|
||||
if (debug)
|
||||
puts("rdp2tcp connected");
|
||||
plugin->writeComplete = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
plugin->copyThread = CreateThread(NULL, 0, copyThread, plugin, 0, NULL);
|
||||
if (plugin->channelEntryPoints.pVirtualChannelOpenEx(pInitHandle, &plugin->openHandle, RDP2TCP_CHAN_NAME, VirtualChannelOpenEventEx) != CHANNEL_RC_OK)
|
||||
return;
|
||||
break;
|
||||
case CHANNEL_EVENT_DISCONNECTED:
|
||||
if (debug)
|
||||
puts("rdp2tcp disconnected");
|
||||
break;
|
||||
case CHANNEL_EVENT_TERMINATED:
|
||||
if (debug)
|
||||
puts("rdp2tcp terminated");
|
||||
if (plugin->copyThread) {
|
||||
TerminateThread(plugin->copyThread, 0);
|
||||
CloseHandle(plugin->writeComplete);
|
||||
}
|
||||
CloseHandle(plugin->hStdInputWrite);
|
||||
CloseHandle(plugin->hStdOutputRead);
|
||||
TerminateProcess(plugin->hProcess, 0);
|
||||
CloseHandle(plugin->hProcess);
|
||||
free(plugin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
#define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
|
||||
#else
|
||||
#define VirtualChannelEntryEx FREERDP_API VirtualChannelEntryEx
|
||||
#endif
|
||||
BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle) {
|
||||
Plugin *plugin = (Plugin *)calloc(1, sizeof(Plugin));
|
||||
if (!plugin)
|
||||
return FALSE;
|
||||
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX *pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX *)pEntryPoints;
|
||||
assert(pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX) && pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
|
||||
plugin->initHandle = pInitHandle;
|
||||
plugin->channelEntryPoints = *pEntryPointsEx;
|
||||
|
||||
if (init_external_addin(plugin) < 0)
|
||||
return FALSE;
|
||||
|
||||
CHANNEL_DEF channelDef = {
|
||||
.name = RDP2TCP_CHAN_NAME,
|
||||
.options =
|
||||
CHANNEL_OPTION_INITIALIZED |
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP
|
||||
};
|
||||
if (pEntryPointsEx->pVirtualChannelInitEx(plugin, NULL, pInitHandle, &channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, VirtualChannelInitEventEx) != CHANNEL_RC_OK)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
|
@ -262,6 +262,7 @@ BOOL freerdp_client_print_command_line_help_ex(int argc, char** argv,
|
|||
printf("Serial Port Redirection: /serial:COM1,/dev/ttyS0\n");
|
||||
printf("Parallel Port Redirection: /parallel:<name>,<device>\n");
|
||||
printf("Printer Redirection: /printer:<device>,<driver>\n");
|
||||
printf("TCP redirection: /rdp2tcp:/usr/bin/rdp2tcp\n");
|
||||
printf("\n");
|
||||
printf("Audio Output Redirection: /sound:sys:oss,dev:1,format:1\n");
|
||||
printf("Audio Output Redirection: /sound:sys:alsa\n");
|
||||
|
@ -2942,6 +2943,13 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
|
|||
if (!copy_value(arg->Value, &settings->ActionScript))
|
||||
return COMMAND_LINE_ERROR_MEMORY;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "rdp2tcp")
|
||||
{
|
||||
free(settings->RDP2TCPArgs);
|
||||
|
||||
if (!(settings->RDP2TCPArgs = _strdup(arg->Value)))
|
||||
return COMMAND_LINE_ERROR_MEMORY;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "fipsmode")
|
||||
{
|
||||
settings->FIPSMode = enable;
|
||||
|
@ -3247,6 +3255,11 @@ BOOL freerdp_client_load_addins(rdpChannels* channels, rdpSettings* settings)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (settings->RDP2TCPArgs) {
|
||||
if (!freerdp_client_load_static_channel_addin(channels, settings, "rdp2tcp", settings->RDP2TCPArgs))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (index = 0; index < settings->StaticChannelCount; index++)
|
||||
{
|
||||
args = settings->StaticChannelArray[index];
|
||||
|
|
|
@ -146,6 +146,7 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
|||
},
|
||||
{ "pth", COMMAND_LINE_VALUE_REQUIRED, "<password-hash>", NULL, NULL, -1, "pass-the-hash", "Pass the hash (restricted admin mode)" },
|
||||
{ "pwidth", COMMAND_LINE_VALUE_REQUIRED, "<width>", NULL, NULL, -1, NULL, "Physical width of display (in millimeters)" },
|
||||
{ "rdp2tcp", COMMAND_LINE_VALUE_REQUIRED, "<executable path[:arg...]>", NULL, NULL, -1, NULL, "TCP redirection" },
|
||||
{ "reconnect-cookie", COMMAND_LINE_VALUE_REQUIRED, "<base64-cookie>", NULL, NULL, -1, NULL, "Pass base64 reconnect cookie to the connection" },
|
||||
{ "redirect-prefer", COMMAND_LINE_VALUE_REQUIRED, "<FQDN|IP|NETBIOS>,[...]", NULL, NULL, -1, NULL, "Override the preferred redirection order" },
|
||||
{ "relax-order-checks", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "relax-order-checks", "Do not check if a RDP order was announced during capability exchange, only use when connecting to a buggy server" },
|
||||
|
|
|
@ -878,6 +878,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL;
|
|||
#define FreeRDP_SupportGeometryTracking (5186)
|
||||
#define FreeRDP_SupportSSHAgentChannel (5187)
|
||||
#define FreeRDP_SupportVideoOptimized (5188)
|
||||
#define FreeRDP_RDP2TCPArgs (5189)
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1517,7 +1518,8 @@ struct rdp_settings
|
|||
ALIGN64 BOOL SupportGeometryTracking; /* 5186 */
|
||||
ALIGN64 BOOL SupportSSHAgentChannel; /* 5187 */
|
||||
ALIGN64 BOOL SupportVideoOptimized; /* 5188 */
|
||||
UINT64 padding5312[5312 - 5189]; /* 5189 */
|
||||
ALIGN64 char *RDP2TCPArgs; /* 5189 */
|
||||
UINT64 padding5312[5312 - 5190]; /* 5190 */
|
||||
|
||||
/**
|
||||
* WARNING: End of ABI stable zone!
|
||||
|
|
|
@ -481,12 +481,11 @@ static BOOL FileUnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfByte
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT64 FileTimeToUS(const FILETIME* ft)
|
||||
static UINT64 FileTimeTo100ns(const FILETIME* ft)
|
||||
{
|
||||
const UINT64 EPOCH_DIFF = 11644473600ULL * 1000000ULL;
|
||||
const UINT64 EPOCH_DIFF = 11644473600ULL * 10000000;
|
||||
UINT64 tmp = ((UINT64)ft->dwHighDateTime) << 32
|
||||
| ft->dwLowDateTime;
|
||||
tmp /= 10; /* 100ns steps to 1us step */
|
||||
tmp -= EPOCH_DIFF;
|
||||
return tmp;
|
||||
}
|
||||
|
@ -534,13 +533,13 @@ static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
|
|||
}
|
||||
else
|
||||
{
|
||||
UINT64 tmp = FileTimeToUS(lpLastAccessTime);
|
||||
UINT64 tmp = FileTimeTo100ns(lpLastAccessTime);
|
||||
#if defined(ANDROID) || defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
|
||||
timevals[0].tv_sec = tmp / 1000000ULL;
|
||||
timevals[0].tv_usec = tmp % 1000000ULL;
|
||||
timevals[0].tv_sec = tmp / 10000000;
|
||||
timevals[0].tv_usec = tmp % 10000000 / 10;
|
||||
#else
|
||||
times[0].tv_sec = tmp / 1000000ULL;
|
||||
times[0].tv_nsec = (tmp % 1000000ULL) * 1000ULL;
|
||||
times[0].tv_sec = tmp / 10000000;
|
||||
times[0].tv_nsec = tmp % 10000000 * 100;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -563,13 +562,13 @@ static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
|
|||
}
|
||||
else
|
||||
{
|
||||
UINT64 tmp = FileTimeToUS(lpLastWriteTime);
|
||||
UINT64 tmp = FileTimeTo100ns(lpLastWriteTime);
|
||||
#if defined(ANDROID) || defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
|
||||
timevals[1].tv_sec = tmp / 1000000ULL;
|
||||
timevals[1].tv_usec = tmp % 1000000ULL;
|
||||
timevals[1].tv_sec = tmp / 10000000;
|
||||
timevals[1].tv_usec = tmp % 10000000 / 10;
|
||||
#else
|
||||
times[1].tv_sec = tmp / 1000000ULL;
|
||||
times[1].tv_nsec = (tmp % 1000000ULL) * 1000ULL;
|
||||
times[1].tv_sec = tmp / 10000000;
|
||||
times[1].tv_nsec = tmp % 10000000 * 100;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -178,6 +178,11 @@
|
|||
|
||||
#define EPOCH_DIFF 11644473600LL
|
||||
#define STAT_TIME_TO_FILETIME(_t) (((UINT64)(_t) + EPOCH_DIFF) * 10000000LL)
|
||||
#ifdef _STAT_VER_LINUX
|
||||
#define STAT_TIME_TO_FILETIME2(_t, _tl) (((UINT64)(_tl.tv_sec) + EPOCH_DIFF) * 10000000LL + _tl.tv_nsec/100)
|
||||
#else
|
||||
#define STAT_TIME_TO_FILETIME2(_t, _tl) STAT_TIME_TO_FILETIME(_t)
|
||||
#endif
|
||||
|
||||
|
||||
static wArrayList* _HandleCreators;
|
||||
|
@ -818,14 +823,14 @@ static BOOL FindDataFromStat(const char* path, const struct stat* fileStat,
|
|||
#ifdef _DARWIN_FEATURE_64_BIT_INODE
|
||||
ft = STAT_TIME_TO_FILETIME(fileStat->st_birthtime);
|
||||
#else
|
||||
ft = STAT_TIME_TO_FILETIME(fileStat->st_ctime);
|
||||
ft = STAT_TIME_TO_FILETIME2(fileStat->st_ctime, fileStat->st_ctim);
|
||||
#endif
|
||||
lpFindFileData->ftCreationTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
|
||||
lpFindFileData->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF;
|
||||
ft = STAT_TIME_TO_FILETIME(fileStat->st_mtime);
|
||||
ft = STAT_TIME_TO_FILETIME2(fileStat->st_mtime, fileStat->st_mtim);
|
||||
lpFindFileData->ftLastWriteTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
|
||||
lpFindFileData->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF;
|
||||
ft = STAT_TIME_TO_FILETIME(fileStat->st_atime);
|
||||
ft = STAT_TIME_TO_FILETIME2(fileStat->st_atime, fileStat->st_atim);
|
||||
lpFindFileData->ftLastAccessTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
|
||||
lpFindFileData->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF;
|
||||
lpFindFileData->nFileSizeHigh = ((UINT64)fileStat->st_size) >> 32ULL;
|
||||
|
|
Загрузка…
Ссылка в новой задаче