Libssh SFTP download inside a thread crashes on sftp_read - c++

I'm using the libssh library in MFC C++, specifically the SFTP wrapper. I have code working when not using threading, but I want to use AfxBeginThread to allow user action to continue.
I have confirmed I'm passing the exact same filenames and target save paths using the thread or not - I've tested this by hardcoding the file to save and the destination to save directly in BeginDownload().
Is there a specific way the SFTP wrapper for the libssh must be used to let the download perform within a thread? I would like to allow users to download multiple files at the same time from the same instance of the ssh objects, and just launch a new thread each time a file needs to be downloaded.
Here is what I'm using for the actual downloading. m_sftp_sesssion is my sftp_session object created upon logging in.
bool CSFtpManager::BeginDownload(CString filename, CString savefilename)
{
sftp_dir dir = sftp_opendir(m_sftp_session, "../../../directory");
sftp_attributes attrs = sftp_readdir(m_sftp_session, dir);
int access_type = O_RDONLY;
sftp_file file;
file = sftp_open(m_sftp_session, "../../../directory/myfile.zip", access_type, 0);
const char* x2 = ssh_get_error(m_ssh_session);
if(file == NULL)
{
int a;
}
int nbytes;
char buffer[1024];
FILE * pFile;
pFile = fopen(CStringA(savefilename), "wb");
nbytes = sftp_read(file, buffer, sizeof(buffer)); //this is crashing inside a thread
while(nbytes > 0)
{
fwrite(buffer, sizeof(char), sizeof(buffer), pFile);
nbytes = sftp_read(file, buffer, sizeof(buffer));
}
fclose(pFile);
sftp_close(file);
return true;
}
Here is my login method where my libssh objects are being set in member variables for later use. Included is a commented line calling my BeginDownload. Calling it outside a thread (i.e. clicking a login button) does work.
bool CSFtpManager::login()
{
int verbosity = SSH_LOG_PROTOCOL;
m_ssh_session = ssh_new();
ssh_options_set(m_ssh_session, SSH_OPTIONS_HOST, "ftp3 host..");
//setting username and password...
ssh_options_set(m_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(m_ssh_session, SSH_OPTIONS_PORT, &m_iPort);
int x = ssh_connect(m_ssh_session);
const char* x2 = ssh_get_error(m_ssh_session);
if(rc != SSH_AUTH_SUCCESS)
{
return false;
}
m_sftp_session;
m_sftp_session = sftp_new(m_ssh_session);
//Calling BeginDownload here does work
//BeginDownload(L"file.txt", L"T:\\file.txt");
... }
Here is what I'm using to call the exact same function via a thread
CWinThread* pThread2 = AfxBeginThread(BeginSFTPDownload, bundle);
And the method being ran via the above:
UINT CMainFrame::BeginSFTPDownload( LPVOID pParam)
{
pSft->BeginDownload(L"test.txt", L"T:\\test.txt");
return 0;
}

Related

ZwSetIniformationFile and FileRenameInformation

I am trying to rename files from kernel.
with this api https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwsetinformationfile
NTSTATUS Files::RenameFile(WCHAR* OriginalName, WCHAR* NewName)
{
// msdn says driver must be at IRQL PASSIVE_LEVEL to make calls to ZwSetInformationFile
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
{
printf("IRQL invalid\n");
return STATUS_INVALID_LEVEL;
}
// Open handle to file , providing OriginalName with full path, example: "\\DosDevices\\C:\\named_file.txt"
// also to be able to rename files u delete the DELETE permission, but i assume with GENERIC_ALL is already giving me enough of them
auto FileHandle = Files(OriginalName, Files::OpenExisting, GENERIC_ALL, 0);
if (FileHandle.CreationStatus != STATUS_SUCCESS)
{
printf("Failed to open handle to file %ws. ERR: 0x%x\n", OriginalName, FileHandle.CreationStatus);
return FileHandle.CreationStatus;
}
IO_STATUS_BLOCK ioStatusBlock;
// msnd info for ZwSetInformatonFile when using FileRenameInformation class says that size must be the size of the
// structure + size of new name in bytes
const auto size = sizeof(FILE_RENAME_INFORMATION) + sizeof(NewName);
// allocate resources for struct
const auto rename_info = static_cast<PFILE_RENAME_INFORMATION>(ExAllocatePool(PagedPool, size));
if(rename_info == nullptr)
{
printf("Failed allocating rename info structure\n");
FileHandle.Close();
return STATUS_INSUFFICIENT_RESOURCES;
}
memset(rename_info, 0, size);
wcscpy(rename_info->FileName, NewName);
rename_info->FileNameLength = sizeof(NewName);// size in bytes
rename_info->RootDirectory = nullptr; // msnd: must be null if the filename is the absolute path
rename_info->ReplaceIfExists = false; // i dont want to replace if exissts
const auto status = ZwSetInformationFile(FileHandle.hFile, &ioStatusBlock, rename_info, size, FileRenameInformation);
// free resources
ExFreePool(rename_info);
FileHandle.Close();
// ZwSetInformationFile fails with 0xc0000034 STATUS_OBJECT_NAME_NOT_FOUND
if(status != STATUS_SUCCESS)
{
printf("0x%x : %ws\n", status, rename_info->FileName); // -> 0xc0000034 : "\\DosDevices\\C:\\renamed_file.txt"
return status;
}
printf("Renamed %ws to %ws\n", OriginalName, NewName);
return STATUS_SUCCESS;
}
i commented a bit of the code, but TLDR; it gives me 0xc0000034 STATUS_OBJECT_NAME_NOT_FOUND.
I followed everything as stated in the MSDN but im always with the same issue, the originalname file does exist in disk since it opens handle succesfully.
any help is appreciated, thanks

Mounting memory buffer as a file without writing to disk

I have a server and needs to feed data from clients to a library; however, that library only supports reading files (it uses open to access the file).
Since the data can get pretty big, I rather not write it out to a temporary file, read it in with the library then delete it afterwards. Instead I would like to do something similar to a ramdisk where there's a file in which the content is actually in memory.
However, there can be multiple clients sending over large data, I don't think constantly calling mount and umount to create a ramdisk for each client is efficient. Is there a way for me to mount an existing memory buffer as a file without writing to disk?
The library does not support taking in a file descriptor nor FILE*. It will only accept a path which it feeds directly to open
I do have the library's source code and attempted to add in a function that uses fmemopen; however, fmemopen returns a FILE* with no file descriptor. The internals of the library works only with file descriptors and it is too complex to change/add support to use FILE*
I looked at mmap, but it appears to be no different than writing out the data to a file
Using mount requires sudo access and I prefer not to run the application as sudo
bool IS_EXITING = false;
ssize_t getDataSize( int clientFD ) { /* ... */}
void handleClient( int clientFD ) {
// Read in messages to get actual data size
ssize_t dataSize = getDataSize( clientFD );
auto* buffer = new char[ dataSize ];
// Read in all the data from the client
ssize_t bytesRead = 0;
while( bytesRead < dataSize ) {
int numRead = read( clientFD, buffer + bytesRead, dataSize - bytesRead );
bytesRead += numRead;
// Error handle if numRead is <= 0
if ( numRead <= 0 ) { /* ... */ }
}
// Mount the buffer and get a file path... How to do this
std::string filePath = mountBuffer( buffer );
// Library call to read the data
readData( filePath );
delete[ ] buffer;
}
void runServer( int socket )
while( !IS_EXITING ) {
auto clientFD = accept( socket, nullptr, nullptr );
// Error handle if clientFD <= 0
if ( clientFD <= 0 ) { /* ... */ }
std::thread clientThread( handleClient, clientFD );
clientThread.detach( );
}
}
Use /dev/fd. Get the file descriptor of the socket, and append that to /dev/fd/ to get the filename.
If the data is in a memory buffer, you could create a thread that writes to a pipe. Use the file descriptor of the read end of the pipe with /dev/fd.

COM port handle for reading data in c++ vs2012

I have a code to open and read serial COM port in vs2012 c++ which is working fine when I run the code separately in an individual solution.The code is as follow:
Serial* SP = new Serial("\\\\.\\COM3"); // adjust as needed
if (SP->IsConnected()) // check com port availability
printf("We're connected"); // send the result
char incomingData[512] = ""; // don't forget to pre-allocate memory
int dataLength = 256;
int readResult = 0; //if there is no reading it is -1
while(SP->IsConnected())
{
readResult = SP->ReadData(incomingData,dataLength);
//std::string test(incomingData);
res1=strtol(incomingData,&pos1,10); //receive data in right patern
res2=atof(pos1); //convert the character to integer
res3=(double)res2; // convert integer to double (as my desired output is a double)
printf("%f\n",res2); // print the result
Sleep(50); // pause so that I can see the coming data
}
in which Serial,ReadData and other functions and headers are defined in a separate header and .cpp file.
My problem occurs when I want to plug the code in my other solution (SOFA Simulation) which I want to use to make a graphical interface. but I get the INVALID_HANDLE_VALUE error and the get last error gives me ERROR_FILE_NOT_FOUND. this is my code in the solution I want to use:
namespace sofa
{
namespace component
{
namespace behaviormodel
{
MyBehaviorModel::MyBehaviorModel():
customUnsignedData(initData(&customUnsignedData, (unsigned)1,"Custom Unsigned Data","Example of unsigned data with custom widget")),
regularUnsignedData(initData(&regularUnsignedData, (unsigned)1,"Unsigned Data","Example of unsigned data with standard widget"))
{
customUnsignedData.setWidget("widget_myData");
}
MyBehaviorModel::~MyBehaviorModel()
{
}
void MyBehaviorModel::init()
{
}
void MyBehaviorModel::reinit()
{
}
void MyBehaviorModel::updatePosition(SReal dt)
{
Serial* SP = new Serial("\\\\.\\COM3"); // adjust as needed
if (SP->IsConnected())
printf("We're connected");
char incomingData[512] = ""; // don't forget to pre-allocate memory
int dataLength = 256;
int readResult = 0;
while(SP->IsConnected())
{
readResult = SP->ReadData(incomingData,dataLength);
//std::string test(incomingData);
res1=strtol(incomingData,&pos1,10);
res2=atof(pos1);
res3=(double)res2;
printf("%f\n",res2);
Sleep(50);
}
dx=0.01;
dy=0.01;
dz+=0.01;
using core::behavior::MechanicalState;
mState1 = dynamic_cast<MechanicalState<sofa::defaulttype::Rigid3dTypes> *> (this->getContext()->getMechanicalState());
helper::WriteAccessor<sofa::core::objectmodel:: Data<sofa::defaulttype::Rigid3dTypes::VecCoord> > xp = *mState1- >write(core::VecCoordId::position());
xp[0].getCenter()=sofa::defaulttype::Vec<3,Real>((Real)dx,(Real)dy,(Real)(res2);
}
SOFA_DECL_CLASS(MyBehaviorModel)
int MyBehaviorModelClass = core::RegisterObject("Dummy component with a custom widget.").add< MyBehaviorModel >();
} // namespace behaviormodel
} // namespace component
} // namespace sofa
I really can not figure out what the problem is because as I said the problem is not from my serial reader code as I tested it and I know it works fine separately.can you find out where the problem lies?
thanks in advance!
This is my Serial constructor:
Serial::Serial(char *portName)
{//We're not yet connected
this->connected = false;
//Try to connect to the given port throuh CreateFile
this->hSerial = CreateFile((LPCWSTR)portName,
GENERIC_READ ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
//Check if the connection was successfull
if(this->hSerial==INVALID_HANDLE_VALUE)
{
//If not success full display an Error
if(GetLastError()==ERROR_FILE_NOT_FOUND){
//Print Error if neccessary
printf("ERROR: Handle was not attached. Reason: %s not available.\n", portName);
}
else
{
printf("ERROR!!!");
}
}
else
{
//If connected we try to set the comm parameters
DCB dcbSerialParams = {0};
//Try to get the current
if (!GetCommState(this->hSerial, &dcbSerialParams))
{
//If impossible, show an error
printf("failed to get current serial parameters!");
}
else
{
//Define serial connection parameters for the arduino board
dcbSerialParams.BaudRate=CBR_9600;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
//Setting the DTR to Control_Enable ensures that the Arduino is properly
//reset upon establishing a connection
dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;
//Set the parameters and check for their proper application
if(!SetCommState(hSerial, &dcbSerialParams))
{
printf("ALERT: Could not set Serial Port parameters");
}
else
{
//If everything went fine we're connected
this->connected = true;
//Flush any remaining characters in the buffers
PurgeComm(this->hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR);
//We wait 2s as the arduino board will be reseting
Sleep(ARDUINO_WAIT_TIME);
}
}
}
}
The function CreateFile is actually a Macro that is mapped to either CreateFileA or CreateFileW depending on your project (unicode) configuration. As others have mentioned, you should not use a type cast to LPCWSTR to hide the fact that your code is not correct, you just need to use the right type of string.
If a function expects a widestring (LPCWSTR) and you pass it a chunk of memory that contains an ANSI string, it will never work. In this particular case, you can use the function CreateFileA directly so that you can pass your ANSI string to it.

FindFirstFile keeps on running the same file when encryption

I am trying to encrypt all the files in a particular folder and that folder has sub folders when i try to print all the files it works good but when i try to encrypt all the files it keeps on encrypting the same file again and again
void dirListFiles(wchar_t *startDir) {
HANDLE hFind;
WIN32_FIND_DATA wfd;
wchar_t path[99999];
char *enName;
const char *extension = ".enc";
int wcsChars;
wsprintf(path, L"%s\\*", startDir);
if ((hFind = FindFirstFile(path, &wfd)) == INVALID_HANDLE_VALUE) {
return;
}
do {
if ((wcsncmp(L".", wfd.cFileName, 1) !=0) && (wcsncmp(L"..", wfd.cFileName, 2) != 0) ) {
wsprintf(path, L"%s\\%s", startDir, wfd.cFileName);
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
dirListFiles(path);
} else {
wcsChars = wcslen(path);
char *szTo = new char[wcsChars + 1];
szTo[wcsChars] = '\0';
WideCharToMultiByte(CP_ACP, 0, path, -1, szTo, wcsChars, NULL, NULL);
enName = (char *)malloc(strlen(szTo) + 1 + 4);
strcpy(enName, szTo);
strcat(enName, extension);
// If i add this line it keeps on encrypting the same file
//fencrypt(szTo, enName, (unsigned const char*)"1234567812345678");
printf("%s\n", enName);
delete[] szTo;
free(enName);
}
}
} while(FindNextFile(hFind, &wfd));
if (GetLastError() != ERROR_NO_MORE_FILES) {
FindClose(hFind);
return;
}
}
If i add fencrypt(szTo, enName, (unsigned const char*)"1234567812345678"); then it encrypts the files in the main folder that is G:\WinApp but when it enters G:\WinApp\ipch\winapp-1918e0a3 it keeps on encrypting the same file again and again there is no file in G:\WinApp\ipch\ only a folder winapp-1918e0a3 this is my encryption function please tell where am i wrong
void fencrypt(char* read, char* write, const unsigned char* enc_key) {
RAND_bytes(iv, AES_BLOCK_SIZE);
readFile = fopen(read,"rb");
writeFile = fopen(write,"wb");
fwrite(iv, 1, 8, writeFile);
fwrite("\0\0\0\0\0\0\0\0", 1, 8, writeFile);
AES_set_encrypt_key(enc_key, 256, &key);
init_ctr(&state, iv);
while(1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);
bytes_written = fwrite(outdata, 1, bytes_read, writeFile);
if (bytes_read < AES_BLOCK_SIZE) {
break;
}
}
fclose(writeFile);
fclose(readFile);
}
I'm far from 100% sure, but I have a feeling that when you create a new file in your directory, the FindNextFile gets confused and finds the same filename again because it's not actually the same file. You may need to either collect a list of all files in a directory and process them one at a time, or keep a list of what you have already done and skip over the ones you have done.
The FindNextFile call will probably see the newly appeared encrypted file and therefore you will keep processing the new files also.
Since you are appending an ".enc" extension to your encrypted files, it would be relatively simple to skip files that already have this extension.

Monitoring file using inotify

I am using inotify to monitor a local file, for example "/root/temp" using
inotify_add_watch(fd, "/root/temp", mask).
When this file is deleted, the program will be blocked by read(fd, buf, bufSize) function. Even if I create a new "/root/temp" file, the program is still block by read function. I am wondering if inotify can detect that the monitored file is created and the read function can get something from fd so that read will not be blocked forever.
Here is my code:
uint32_t mask = IN_ALL_EVENTS;
int fd = inotify_init();
int wd = inotify_add_watch(fd, "/root/temp", mask);
char *buf = new char[1000];
int nbytes = read(fd, buf, 500);
I monitored all events.
The problem is that read is a blocking operation by default.
If you don't want it to block, use select or poll before read. For example:
struct pollfd pfd = { fd, POLLIN, 0 };
int ret = poll(&pfd, 1, 50); // timeout of 50ms
if (ret < 0) {
fprintf(stderr, "poll failed: %s\n", strerror(errno));
} else if (ret == 0) {
// Timeout with no events, move on.
} else {
// Process the new event.
struct inotify_event event;
int nbytes = read(fd, &event, sizeof(event));
// Do what you need...
}
Note: untested code.
In order to see a new file created, you need to watch the directory, not the file. Watching a file should see when it is deleted (IN_DELETE_SELF) but may not spot if a new file is created with the same name.
You should probably watch the directory for IN_CREATE | IN_MOVED_TO to see newly created files (or files moved in from another place).
Some editors and other tools (e.g. rsync) may create a file under a different name, then rename it.