How can I get a UNC path for a file that is accessed through a network drive? - c++

I am working on a application in VC++ where network drives are used to access files. The drives are assigned manually by the users and then select the drive in the application. This results in drives not always being mapped to the same servers.
How would I go about obtaining the UNC path to such a file? This is mostly for identification purposes.

here's the function I use to convert a normal path to an UNC path:
wstring ConvertToUNC(wstring sPath)
{
WCHAR temp;
UNIVERSAL_NAME_INFO * puni = NULL;
DWORD bufsize = 0;
wstring sRet = sPath;
//Call WNetGetUniversalName using UNIVERSAL_NAME_INFO_LEVEL option
if (WNetGetUniversalName(sPath.c_str(),
UNIVERSAL_NAME_INFO_LEVEL,
(LPVOID) &temp,
&bufsize) == ERROR_MORE_DATA)
{
// now we have the size required to hold the UNC path
WCHAR * buf = new WCHAR[bufsize+1];
puni = (UNIVERSAL_NAME_INFO *)buf;
if (WNetGetUniversalName(sPath.c_str(),
UNIVERSAL_NAME_INFO_LEVEL,
(LPVOID) puni,
&bufsize) == NO_ERROR)
{
sRet = wstring(puni->lpUniversalName);
}
delete [] buf;
}
return sRet;;
}

Suggest you use WNetGetConnection.
API description is here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa385453(v=vs.85).aspx

Related

Get Document Directory

I'm working on a little singleplayer cheat for a game and am planning on making it public, I have written the API and it all works well but I want to save the data that the user enters to a .ini file so that they will only have to log in one time unless their credentials change. I have done this and it works with a relative path C:\Users\Name\Documents\Cheat\Authorise.ini Although When check the result, it doesn't seem to save nor read the data in the file.
I'm wondering if there is a better way to get the Documents Directory.
Function:
std::string authFile = "C:\\Users\\%USERNAME%\\Documents\\Cheats\\Authorise.ini";
std::string username = GUI::Files::ReadStringFromIni(authFile, "Login", "Username");
std::string password = GUI::Files::ReadStringFromIni(authFile, "Login", "Password");`
Since you're on Windows, you should use the Windows API call available for this very purpose in <ShlObj.h> called SHGetKnownFolderPath. Note that you had best use a std::wstring instead for this purpose, since there is no variant of SHGetKnownFolderPath that accepts an MBCS or ANSI string. Also, this will get you the entire path to the user's profile directory, not just the username.
PWSTR path;
SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, NULL, &path);
std::wstring strpath(path);
CoTaskMemFree(path);
Also, once you're done with path, free it with CoTaskMemFree.
Different versions of Windows store user profiles in different locations, and even the default name of the Documents folder can differ. In fact, the name and location of the user's Documents folder is fully customizable by the user, and may not even be located under the user's profile at all. So, you should not assume the Documents folder is always located at C:\\Users\\%USERNAME%\\Documents.
The best way to get the correct path to a user's Documents folder on all versions of Windows is to simply ask Windows itself. Use SHGetFolderPath(CSIDL_MYDOCUMENTS) (pre-Vista) or SHGetKnownFolderPath(FOLDERID_Documents) (Vista+) for that, eg:
#include <shlobj.h>
#include <shlwapi.h>
std::string GetDocumentsFolder()
{
std::string path;
char szPath[MAX_PATH+1] = {};
if (SHGetFolderPathA(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, szPath) == S_OK)
path = PathAddBackslashA(szPath);
/*
PWSTR pPath = NULL;
if (SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, NULL, &pPath) == S_OK)
{
int wlen = lstrlenW(pPath);
int len = WideCharToMultiByte(CP_ACP, 0, pPath, wlen, NULL, 0, NULL, NULL);
if (len > 0)
{
path.resize(len+1);
WideCharToMultiByte(CP_ACP, 0, pPath, wlen, &path[0], len, NULL, NULL);
path[len] = '\\';
}
CoTaskMemFree(pPath);
}
*/
return path;
}
std::string GetAuthFilePath()
{
std::string path = GetDocumentsFolder();
if (!path.empty())
path += "Cheats\\Authorise.ini";
return path;
}
std::string authFile = GetAuthFilePath();
...

Libssh SFTP download inside a thread crashes on sftp_read

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;
}

Why is RemoveDirectory function not deleting the top most folder?

refer: codeguru.com/forum/showthread.php?t=239271
When using the function below to delete folders, all folders, subfolders and files are getting deleted except for the top most folder. Say for the path c:\folder1\folder2 every thing under folder2 is deleted except for folder2.
BOOL DeleteDirectory(const TCHAR* sPath)
{
HANDLE hFind; // file handle
WIN32_FIND_DATA FindFileData;
TCHAR DirPath[MAX_PATH];
TCHAR FileName[MAX_PATH];
_tcscpy(DirPath,sPath);
_tcscat(DirPath,_T("\\"));
_tcscpy(FileName,sPath);
_tcscat(FileName,_T("\\*")); // searching all files
int nRet = 0;
hFind = FindFirstFile(FileName, &FindFileData); // find the first file
if( hFind != INVALID_HANDLE_VALUE )
{
do
{
if( IsDots(FindFileData.cFileName) )
continue; //if not directory continue
_tcscpy(FileName + _tcslen(DirPath), FindFileData.cFileName);
if((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
// we have found a directory, recurse
if( !DeleteDirectory(FileName) )
break; // directory couldn't be deleted
}
else
{
if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
_wchmod(FileName, _S_IWRITE); // change read-only file mode
if( !DeleteFile(FileName) )
break; // file couldn't be deleted
}
}while( FindNextFile(hFind, &FindFileData) );
nRet = FindClose(hFind); // closing file handle
}
return RemoveDirectory(sPath); // remove the empty (maybe not) directory and returns zero when RemoveDirectory function fails
}
Any help in finding the issue is appreciated.
During debugging I noticed that the FindClose function was successfully closing the file handle but GetLastError was returning 32 ("The process cannot access the file because it is being used by another process") However I have no clue after trying with process explorer.
Whilst you can delete a directory this way, it's simpler to let the system do it for you by calling SHFileOperation passing FO_DELETE. Remember that you must double null-terminate the string you pass to this API.
I believe you have to close the file handle before the recursive call. Which means after exiting the recursive call you must again set your your file handle to something appropriate.
SHFileOperation may be a better solution; I am just answering the OP's question of why their code wasn't working as intended.
Refer:http://www.codeguru.com/forum/archive/index.php/t-337897.html
Given below is the code to delete directory using SHFileOperation
bool DeleteDirectory(LPCTSTR lpszDir, bool noRecycleBin = true)
{
int len = _tcslen(lpszDir);
TCHAR* pszFrom = new TCHAR[len+4]; //4 to handle wide char
//_tcscpy(pszFrom, lpszDir); //todo:remove warning//;//convet wchar to char*
wcscpy_s (pszFrom, len+2, lpszDir);
pszFrom[len] = 0;
pszFrom[len+1] = 0;
SHFILEOPSTRUCT fileop;
fileop.hwnd = NULL; // no status display
fileop.wFunc = FO_DELETE; // delete operation
fileop.pFrom = pszFrom; // source file name as double null terminated string
fileop.pTo = NULL; // no destination needed
fileop.fFlags = FOF_NOCONFIRMATION|FOF_SILENT; // do not prompt the user
if(!noRecycleBin)
fileop.fFlags |= FOF_ALLOWUNDO;
fileop.fAnyOperationsAborted = FALSE;
fileop.lpszProgressTitle = NULL;
fileop.hNameMappings = NULL;
int ret = SHFileOperation(&fileop); //SHFileOperation returns zero if successful; otherwise nonzero
delete [] pszFrom;
return (0 == ret);
}

How can I detect only deleted, changed, and created files on a volume?

I need to know if there is an easy way of detecting only the files that were deleted, modified or created on an NTFS volume.
I have written a program for offsite backup in C++. After the first backup, I check the archive bit of each file to see if there was any change made, and back up only the files that were changed. Also, it backs up from the VSS snapshot in order to prevent file locks.
This seems to work fine on most file systems, but for some with lots of files and directories, this process takes too long and often the backup takes more than a day to finish backing up.
I tried using the change journal to easily detect changes made on an NTFS volume, but the change journal would show a lot of records, most of them relating to small temporary files created and destroyed. Also, I could the file name, file reference number, and the parent file reference number, but I could not get the full file path. The parent file reference number is somehow supposed to give you the parent directory path.
EDIT: This needs to run everyday, so at the beginning of every scan, it should record only the changes that took place since the last scan. Or atleast, there should be a way to say changes since so and so time and date.
You can enumerate all the files on a volume using FSCTL_ENUM_USN_DATA. This is a fast process (my tests returned better than 6000 records per second even on a very old machine, and 20000+ is more typical) and only includes files that currently exist.
The data returned includes the file flags as well as the USNs so you could check for changes whichever way you prefer.
You will still need to work out the full path for the files by matching the parent IDs with the file IDs of the directories. One approach would be to use a buffer large enough to hold all the file records simultaneously, and search through the records to find the matching parent for each file you need to back up. For large volumes you would probably need to process the directory records into a more efficient data structure, perhaps a hash table.
Alternately, you can read/reread the records for the parent directories as needed. This would be less efficient, but the performance might still be satisfactory depending on how many files are being backed up. Windows does appear to cache the data returned by FSCTL_ENUM_USN_DATA.
This program searches the C volume for files named test.txt and returns information about any files found, as well as about their parent directories.
#include <Windows.h>
#include <stdio.h>
#define BUFFER_SIZE (1024 * 1024)
HANDLE drive;
USN maxusn;
void show_record (USN_RECORD * record)
{
void * buffer;
MFT_ENUM_DATA mft_enum_data;
DWORD bytecount = 1;
USN_RECORD * parent_record;
WCHAR * filename;
WCHAR * filenameend;
printf("=================================================================\n");
printf("RecordLength: %u\n", record->RecordLength);
printf("MajorVersion: %u\n", (DWORD)record->MajorVersion);
printf("MinorVersion: %u\n", (DWORD)record->MinorVersion);
printf("FileReferenceNumber: %lu\n", record->FileReferenceNumber);
printf("ParentFRN: %lu\n", record->ParentFileReferenceNumber);
printf("USN: %lu\n", record->Usn);
printf("Timestamp: %lu\n", record->TimeStamp);
printf("Reason: %u\n", record->Reason);
printf("SourceInfo: %u\n", record->SourceInfo);
printf("SecurityId: %u\n", record->SecurityId);
printf("FileAttributes: %x\n", record->FileAttributes);
printf("FileNameLength: %u\n", (DWORD)record->FileNameLength);
filename = (WCHAR *)(((BYTE *)record) + record->FileNameOffset);
filenameend= (WCHAR *)(((BYTE *)record) + record->FileNameOffset + record->FileNameLength);
printf("FileName: %.*ls\n", filenameend - filename, filename);
buffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (buffer == NULL)
{
printf("VirtualAlloc: %u\n", GetLastError());
return;
}
mft_enum_data.StartFileReferenceNumber = record->ParentFileReferenceNumber;
mft_enum_data.LowUsn = 0;
mft_enum_data.HighUsn = maxusn;
if (!DeviceIoControl(drive, FSCTL_ENUM_USN_DATA, &mft_enum_data, sizeof(mft_enum_data), buffer, BUFFER_SIZE, &bytecount, NULL))
{
printf("FSCTL_ENUM_USN_DATA (show_record): %u\n", GetLastError());
return;
}
parent_record = (USN_RECORD *)((USN *)buffer + 1);
if (parent_record->FileReferenceNumber != record->ParentFileReferenceNumber)
{
printf("=================================================================\n");
printf("Couldn't retrieve FileReferenceNumber %u\n", record->ParentFileReferenceNumber);
return;
}
show_record(parent_record);
}
void check_record(USN_RECORD * record)
{
WCHAR * filename;
WCHAR * filenameend;
filename = (WCHAR *)(((BYTE *)record) + record->FileNameOffset);
filenameend= (WCHAR *)(((BYTE *)record) + record->FileNameOffset + record->FileNameLength);
if (filenameend - filename != 8) return;
if (wcsncmp(filename, L"test.txt", 8) != 0) return;
show_record(record);
}
int main(int argc, char ** argv)
{
MFT_ENUM_DATA mft_enum_data;
DWORD bytecount = 1;
void * buffer;
USN_RECORD * record;
USN_RECORD * recordend;
USN_JOURNAL_DATA * journal;
DWORDLONG nextid;
DWORDLONG filecount = 0;
DWORD starttick, endtick;
starttick = GetTickCount();
printf("Allocating memory.\n");
buffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (buffer == NULL)
{
printf("VirtualAlloc: %u\n", GetLastError());
return 0;
}
printf("Opening volume.\n");
drive = CreateFile(L"\\\\?\\c:", GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_FLAG_NO_BUFFERING, NULL);
if (drive == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
return 0;
}
printf("Calling FSCTL_QUERY_USN_JOURNAL\n");
if (!DeviceIoControl(drive, FSCTL_QUERY_USN_JOURNAL, NULL, 0, buffer, BUFFER_SIZE, &bytecount, NULL))
{
printf("FSCTL_QUERY_USN_JOURNAL: %u\n", GetLastError());
return 0;
}
journal = (USN_JOURNAL_DATA *)buffer;
printf("UsnJournalID: %lu\n", journal->UsnJournalID);
printf("FirstUsn: %lu\n", journal->FirstUsn);
printf("NextUsn: %lu\n", journal->NextUsn);
printf("LowestValidUsn: %lu\n", journal->LowestValidUsn);
printf("MaxUsn: %lu\n", journal->MaxUsn);
printf("MaximumSize: %lu\n", journal->MaximumSize);
printf("AllocationDelta: %lu\n", journal->AllocationDelta);
maxusn = journal->MaxUsn;
mft_enum_data.StartFileReferenceNumber = 0;
mft_enum_data.LowUsn = 0;
mft_enum_data.HighUsn = maxusn;
for (;;)
{
// printf("=================================================================\n");
// printf("Calling FSCTL_ENUM_USN_DATA\n");
if (!DeviceIoControl(drive, FSCTL_ENUM_USN_DATA, &mft_enum_data, sizeof(mft_enum_data), buffer, BUFFER_SIZE, &bytecount, NULL))
{
printf("=================================================================\n");
printf("FSCTL_ENUM_USN_DATA: %u\n", GetLastError());
printf("Final ID: %lu\n", nextid);
printf("File count: %lu\n", filecount);
endtick = GetTickCount();
printf("Ticks: %u\n", endtick - starttick);
return 0;
}
// printf("Bytes returned: %u\n", bytecount);
nextid = *((DWORDLONG *)buffer);
// printf("Next ID: %lu\n", nextid);
record = (USN_RECORD *)((USN *)buffer + 1);
recordend = (USN_RECORD *)(((BYTE *)buffer) + bytecount);
while (record < recordend)
{
filecount++;
check_record(record);
record = (USN_RECORD *)(((BYTE *)record) + record->RecordLength);
}
mft_enum_data.StartFileReferenceNumber = nextid;
}
}
Additional notes
As discussed in the comments, you may need to replace MFT_ENUM_DATA with MFT_ENUM_DATA_V0 on versions of Windows later than Windows 7. (This may also depend on what compiler and SDK you are using.)
I'm printing the 64-bit file reference numbers as if they were 32-bit. That was just a mistake on my part. Probably in production code you won't be printing them anyway, but FYI.
The change journal is your best bet. You can use the file reference numbers to match file creation/deletion pairs and thus ignore temporary files, without having to process them any further.
I think you have to scan the Master File Table to make sense of ParentFileReferenceNumber. Of course you only need to keep track of directories when doing this, and use a data structure that will allow you to quickly lookup the information, so you only need to scan the MFT once.
You can use ReadDirectoryChanges and surrounding windows API.
I know how to achieve this in java. It will help you if you implement Java code inside C++.
In Java you can achieve this using Jnotify API.It looks for changes in sub-directory also.

How do you retrieve the original location of a mounted path?

In C++, how can I retrieve the location of a mounted drive?
for example, if I have mounted drive s: to c:\temp (using subst in the command line)
"subst c:\temp s:"
how can I get "c:\temp" by passing "s:"
I would also like to know how can it be done for a network drive.
(if s: is mounted to "\MyComputer\Hello", then I want to retrieve "\MyComputer\Hello" and then to retrieve "c:\Hello" from that)
It might be a very easy question but I just couldn't find information about it.
Thanks,
Adam
If you've used SUBST, the API you want is QueryDosDevice. You can SUBST things yourself by using DefineDosDevice.
You can probably use the GetVolumeInformation function. From the documentation:
Symbolic link behavior
If the path points to a symbolic link, the function returns volume information for the
target.
Haven't tested it myself, though.
To find the path of a mounted network share, you have to use the WNet APIs:
wstring ConvertToUNC(wstring sPath)
{
WCHAR temp;
UNIVERSAL_NAME_INFO * puni = NULL;
DWORD bufsize = 0;
wstring sRet = sPath;
//Call WNetGetUniversalName using UNIVERSAL_NAME_INFO_LEVEL option
if (WNetGetUniversalName(sPath.c_str(),
UNIVERSAL_NAME_INFO_LEVEL,
(LPVOID) &temp,
&bufsize) == ERROR_MORE_DATA)
{
// now we have the size required to hold the UNC path
WCHAR * buf = new WCHAR[bufsize+1];
puni = (UNIVERSAL_NAME_INFO *)buf;
if (WNetGetUniversalName(sPath.c_str(),
UNIVERSAL_NAME_INFO_LEVEL,
(LPVOID) puni,
&bufsize) == NO_ERROR)
{
sRet = wstring(puni->lpUniversalName);
}
delete [] buf;
}
return sRet;;
}