Writing USB Disk Sectors Results in Permission Error 5 - c++

The code below is required by my app to write disk sectors of external USB drives. It works on most Win10 PCs, but it's returning error 5 for permission denied on a couple PCs. I have exclusions created for both Windows Defender and Malwarebytes. There's nothing in the event viewer related to the failure. The read function works without error.
I tried adding calls to FSCTL_LOCK_VOLUME and FSCTL_DISMOUNT_VOLUME, but this doesn't help. Probably not needed anyway since I'm accessing the physical disk after it's been cleaned, and not any volumes.
Any idea what could cause this, or how to resolve?
Would be great to learn if there's any alternate methods of reading and writing disk sectors.
BOOL Partitioner::BlockWrite(wchar_t* devIdentifier, unsigned __int64 lngStartbyte, DWORD bytesToRead, BYTE* buf)
{
BOOL ret = FALSE;
HANDLE devHan = CreateFile(devIdentifier, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (devHan != INVALID_HANDLE_VALUE)
{
// Seek to the starting block to write
LARGE_INTEGER startByte;
startByte.QuadPart = lngStartbyte;
SetFilePointer(devHan, startByte.LowPart, (long*)&startByte.HighPart, FILE_BEGIN);
// Write the data (this is where error 5 is returned)
DWORD bytesWritten = 0;
ret = WriteFile(devHan, buf, bytesToRead, &bytesWritten, NULL);
FlushFileBuffers(devHan);
CloseHandle(devHan);
}
else
{
ret = GetLastError();
wchar_t msg[PATH_BUFFER_SIZE] = {0};
swprintf_s(msg, WCSIZE_FULL(msg), L"Error= %d, byte= %llu", ret, lngStartbyte);
mLog->LogError(msg);
}
return ret;
}

I found the answer where I wasn't expecting. I thought this had to be something related to how the file handles were being opened. Instead, turning off Real-time protection in the Virus threat protection settings for Windows 10 caused the error 5s to go away. To resolve without disabling real-time protection, you need to add an allowed app exclusion for each of the installed EXEs.
You can do this in code by scripting PowerShell:
string script = "powershell -Command \"Add-MpPreference -ControlledFolderAccessAllowedApplications '" + GetAppPath() + "\\AppServer.exe" + "'";
Process.Start(new ProcessStartInfo() { FileName = "cmd.exe", Arguments = "/c " + script, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden }).WaitForExit();

Related

Enumerate all partitions and test if they are NTFS

I'm using:
DWORD d = GetLogicalDrives();
for (int i = 0; i < 26; i++)
{
if ((1 << i) & d) // drive letter 'A' + i present on computer
{
wstring s = std::wstring(L"\\\\.\\") + wchar_t('A' + i) + L":";
PARTITION_INFORMATION diskInfo;
DWORD dwResult;
HANDLE dev = CreateFile(LPWSTR(s.c_str()), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &dwResult, NULL);
CloseHandle(dev);
if (diskInfo.PartitionType == PARTITION_IFS)
{
...
}
}
}
to enumerate all NTFS partitions of a computer.
It works on my Windows 7, on a Windows 8.1 I tried it on, and on a Windows 10 computer.
But it fails on another Windows 10 computer: on this one, the volume C: has a diskInfo.PartitionType value equal to 0x00, instead of 0x07 (PARTITION_IFS).
This value is (see the doc here):
PARTITION_ENTRY_UNUSED : 0x00 : An unused entry partition.
This is strange, since, I can confirm, the partition is really NTFS.
Questions:
Is it well-known that IOCTL_DISK_GET_PARTITION_INFO is not 100% reliable to get the partition type?
What would be a more reliable way to enumerate all NTFS volumes?
Note: I also looked at using IOCTL_DISK_GET_PARTITION_INFO_EX instead of IOCTL_DISK_GET_PARTITION_INFO but then the structure PARTITION_INFORMATION_EX does not seem to give informations about PartitionType, whereas the structure PARTITION_INFORMATION does give access to PartitionType.
As #RemyLebeau says, you are not checking the return value for each call.
PARTITION_ENTRY_UNUSED often means the DeviceIoControl() call failed. It depends on the permissions of your user. You should check your user's access rights to see if it has the FILE_READ_DATA permission (included in GENERIC_READ) on volume C:. In my test environment, if you have no access to open volume C: with GENERIC_READ, CreateFile() returns INVALID_HANDLE_VALUE, and then DeviceIoControl() fails as well.
EDIT:
I suggest using GetVolumeInformation(), for example:
wchar_t fs[MAX_PATH + 1] = { 0 };
GetVolumeInformationW(L"C:\\", NULL, 0, NULL, NULL, NULL, fs, MAX_PATH + 1);
And you will see the Type info in the fs buffer.
I did further investigation thanks to #RemyLebeau's comments with:
HANDLE dev = CreateFile(..., GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
if (dev == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError(); // then MessageBox
}
else
{
BOOL ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &dwResult, NULL);
if (ret == FALSE)
{
DWORD err = GetLastError(); // then MessageBox
}
CloseHandle(dev);
}
on the computer where it failed (computer with Windows 10). I found that CreateFile succeeded but then DeviceIoControl failed with GetLastError being 1 i.e. ERROR_INVALID_FUNCTION (see System Error Codes (0-499)).
Conclusion (I quote Remy's comment):
That means IOCTL_DISK_GET_PARTITION_INFO is not supported by the device you passed to DeviceIoControl().
I then tried with IOCTL_DISK_GET_PARTITION_INFO_EX:
PARTITION_INFORMATION_EX diskInfo;
BOOL ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &diskInfo, sizeof(diskInfo), &lpBytesReturned, NULL);
and then it worked. I could see that diskInfo.PartitionStyle was PARTITION_STYLE_GPT (=1), and this was the reason why IOCTL_DISK_GET_PARTITION_INFO failed. I quote Remy's comment again:
IOCTL_DISK_GET_PARTITION_INFO is not supported on GPT partitioned drives.
So here's the conclusion:
use IOCTL_DISK_GET_PARTITION_INFO_EX instead of IOCTL_DISK_GET_PARTITION_INFO
if diskInfo.PartitionStyle is 0 (PARTITION_STYLE_MBR) then diskInfo.Mbr.PartitionType can be tested. If it's 0x07, it's NTFS.
if diskInfo.PartitionStyle is 1 (PARTITION_STYLE_GPT) then diskInfo.Gpt.PartitionType can be tested, see here: https://learn.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-_partition_information_gpt. Even if the NTFS Wikipedia page mentions the GUID EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 for NTFS in the GPT case, this GUID is in fact unrelated to file system (see comment below about this).
it's probably easier to use GetVolumeInformation() instead and just compare if the result is the "NTFS" string, as in the other answer
in my particular case, I initially wanted to test if a volume is NTFS or not before attempting an indexing with DeviceIoControl(hVol, FSCTL_ENUM_USN_DATA, ...) because I thought such MFT querying would be limited to NTFS volumes. In fact, an easier solution would be to NOT TEST if it's NTFS or not, and just do the FSCTL_ENUM_USN_DATA. The worst that can happen is that FSCTL_ENUM_USN_DATA fails with ERROR_INVALID_FUNCTION error, per the documentation:
"ERROR_INVALID_FUNCTION The file system on the specified volume does not support this control code."

A more robust WriteFile

Our problem:
We are capturing video and writing the data to a FileSystem, usually a NAS device. So we have sequential data to be written, and this code will be writing to multiple files on the same NAS - for 99% of the time it works, however occasionally this function will just hang.
I have this function that writes data to a file.
STDMETHODIMP FileWriter::Receive(LONG lStreamId, IStreamPacket* pPacket)
{
CMutexAutoLock lock(&m_csAccessControl);
HRESULT hr = S_OK;
// AddRef the packet to prevent loss
pPacket->AddRef();
if (m_hFile != INVALID_HANDLE_VALUE && pPacket) {
DWORD written = 0;
BYTE* pData = NULL;
LONG lLen = 0;
pPacket->get_Pointer(&pData);
pPacket->get_ActualDataLength(&lLen);
if (pData && lLen) {
WriteFile(m_hFile, pData, lLen, &written, NULL);
m_llPosition += written;
if (m_llPosition > m_llLength) {
m_llLength = m_llPosition;
}
}
m_xiPosition = (LONG)m_llPosition;
m_xiLength = (LONG)m_llLength;
m_xiLengthInMB = (LONG)(m_llLength/1024/1024);
}
// Release packet
pPacket->Release();
return hr;
}
Now, I cannot test this code easily, as it is used internally to a framework I am using. I do however have this code handy.
I have read online that the WriteFile() function can freeze the application, and this happens to us when the target file system is a NAS, but only very occasionally. When writing to the local HDD, this is never an issue. Unfortunately we require a very robust solution that we can write to a NAS without issue.
How could I modify this code so that the WriteFile() function always completes? I have read about using Overlapped IO, but I am not sure how I would go about this and if this is suitable.

FILE_NOT_FOUND when trying to open COM port C++

I am trying to open a com port for reading and writing using C++ but I can't seem to pass the first stage of actually opening it. I get an INVALID_HANDLE_VALUE on the handle
with GetLastError FILE_NOT_FOUND. I have searched around the web for a couple of days I'm fresh out of ideas. I have searched through all the questions regarding COM on this website too.
I have scanned through the existing ports (or so I believe) to get the name of the port right.
I also tried combinations of _T("COM1") with the slashes, without the slashes, with colon, without colon and without the _T
I'm using windows 7 on 64 bit machine.
this is the code i got
I'll be glad for any input on this
void SendToCom(char* data, int len)
{
DWORD cbNeeded = 0;
DWORD dwPorts = 0;
EnumPorts(NULL, 1, NULL, 0, &cbNeeded, &dwPorts);
//What will be the return value
BOOL bSuccess = FALSE;
LPCSTR COM1 ;
BYTE* pPorts = static_cast<BYTE*>(malloc(cbNeeded));
bSuccess = EnumPorts(NULL, 1, pPorts, cbNeeded, &cbNeeded, &dwPorts);
if (bSuccess){
PORT_INFO_1* pPortInfo = reinterpret_cast<PORT_INFO_1*>(pPorts);
for (DWORD i=0; i<dwPorts; i++)
{
//If it looks like "COMX" then
size_t nLen = _tcslen(pPortInfo->pName);
if (nLen > 3)
{
if ((_tcsnicmp(pPortInfo->pName, _T("COM"), 3) == 0) ){
COM1 =pPortInfo->pName;
//COM1 ="\\\\.\\COM1";
HANDLE m_hCommPort = CreateFile( COM1 ,
GENERIC_READ|GENERIC_WRITE, // access ( read and write)
0, // (share) 0:cannot share the COM port
NULL, // security (None)
OPEN_EXISTING, // creation : open_existing
FILE_FLAG_OVERLAPPED, // we want overlapped operation
NULL // no templates file for COM port...
);
if (m_hCommPort==INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
if (err == ERROR_FILE_NOT_FOUND) {
MessageBox(hWnd,"ERROR_FILE_NOT_FOUND",NULL,MB_ABORTRETRYIGNORE);
}
else
if(err == ERROR_INVALID_NAME) {
MessageBox(hWnd,"ERROR_INVALID_NAME",NULL,MB_ABORTRETRYIGNORE);
}
else
{
MessageBox(hWnd,"unkown error",NULL,MB_ABORTRETRYIGNORE);
}
}
else{
WriteAndReadPort(m_hCommPort,data);
}
}
pPortInfo++;
}
}
}
}
The Solution is to use
The Problem is, if your port is Bigger then 9 then you have to use the Syntax
LPCWSTR szPortName = L"\\\\.\\COM11";.
If you are on Windows 10 - running all system updates might help !
I had the same issue that opening port "COM4" returned an error ERROR_FILE_NOT_FOUND. When running the program as "Administrator" it worked. Now after a updating to 1511 the program can open "COM4" even not running as "Administrator".
http://www.cplusplus.com/forum/windows/163855/
Use CreateFileA(...) instead of CreateFile(...)
ERROR_FILE_NOT_FOUND can be produced from CreateFile(L"\\\\.\\COM1", ...) and CreateFile(L"COM1:", ...) after using the Device Manager to change the assigned COM Port number. Disabling and re-enabling the device, or unplugging and reconnecting the USB adapter resolves the issue.
A useful test to confirm whether it is your program or the system is to send data to the port in command prompt. A successful test will show an empty line. A failed test will show an error message.
C:\drop>echo > \\.\COM1
The system cannot find the file specified.
C:\drop>echo > \\.\COM1
C:\drop>

Why WNetAddConnection2 still returns 1219 after successfully calling WNetCancelConnection2?

I wrote some code to connect with some share on a remote server. If WNetAddConnection2 returns ERROR_SESSION_CREDENTIAL_CONFLICT (1219), I will first cancel the connection by WNetCancelConnection2 (return NO_ERROR). And then reconnect. But WNetAddConnection2 still returns 1219.
Why this and how to fix it?
Here's my code
BOOL ADDirectorySearch::IPCConnect(CString strServerName, CString strDomainName, CString strUserName, CString strPassWord)
{
CString strServerNameWithSlash = _T("\\\\") + strServerName; //actually is \\klbnt
CString strFullUserName = strDomainName + _T("\\") + strUserName; //is domaintest\administrator
_bstr_t bstrServerNameWithSlash = strServerNameWithSlash;
_bstr_t bstrFullUserName = strFullUserName;
_bstr_t bstrPassWord = strPassWord;
DWORD dwResult;
NETRESOURCEW netResource;
memset(&netResource, 0, sizeof(netResource));
netResource.dwScope = RESOURCE_GLOBALNET;
netResource.dwType = RESOURCETYPE_DISK;
netResource.dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
netResource.dwUsage = RESOURCEUSAGE_CONNECTABLE;
netResource.lpProvider = L"";
netResource.lpRemoteName = bstrServerNameWithSlash;//Remote IP like:\\192.168.1.11
dwResult = WNetAddConnection2W(&netResource, bstrPassWord, bstrFullUserName, CONNECT_INTERACTIVE);
if (dwResult == ERROR_SESSION_CREDENTIAL_CONFLICT)
{
dwResult = WNetCancelConnection2W(bstrServerNameWithSlash, CONNECT_UPDATE_PROFILE, TRUE);
if (dwResult == NO_ERROR)
{
dwResult = WNetAddConnection2W(&netResource, bstrPassWord, bstrFullUserName, CONNECT_INTERACTIVE);
}
else
{
//MyMessageBox_Error(_T("IPCConnect Error."), _T("Error"));
return FALSE;
}
}
if (dwResult == NO_ERROR)
{
return TRUE;
}
else
{
//MyMessageBox_Error(_T("IPCConnect Error."), _T("Error"));
return FALSE;
}
}
FYI: After typing "net use" in cmd, I got this, I feel there's something with error:
Status Local Remote Network
-------------------------------------------------------------------------------
OK \\klbnt\NRDC1001 Microsoft Windows Network
The command completed successfully.
I was just having this problem now, and basically it seemed that it was due to another process still having file open, even though I specified "true" as the last parameter of WNetCancelConnection2() to force close the connection. Once I shut-down that other process, I was able to use successfully switch between credentials connecting and re-connecting to the same share. This is on Windows 2012 (64-bit), and the share was local (referenced by the machinename).
BUT...it's still a problem if you want to connect to different shares on the same machine. If I try to connect to \\mymachine\share1 as user1 then to \\mymachine\share2 as user2, I get the 1219 error (even if it's in a completely different process). I have to explicitly call WNetCancelConnnection on \\mymachine\share1 before I can connect to share2, which means at the point you connect to a share on a particular machine, you may have to first enumerate existing connections and close each one.
Rather frustrating, and I can't understand the design principle here. It seems the flags to create temporary connections etc. have no effect on this behaviour either. Really what I want to be able to do is say "for this thread, connect to this share on this machine and as this user, such that all attempts to access files on the share are done with that user's credentials". That way what other processes/threads are doing can't cause issues with the current one.

How Do you set MOTW on an Executable

How do you set MOTW (Mark of the Web) on an executable that is downloaded from the internet?
This data is stored in an NTFS alternative file stream alongside an executable. The stream is called Zone.Identifier:
Windows® Internet Explorer® uses the stream name Zone.Identifier for storage of URL security zones.
The fully qualified form is sample.txt:Zone.Identifier:$DATA
The stream is a simple text stream of the form:
[ZoneTransfer]
ZoneId=3
MSDN-SECZONES gives an explanation of security zones.
(N.B. The original has a space between the colon and "Zone" but I think this is erroneous.)
You can find the ZoneIds in UrlMon.h in the SDK; there's an enum which equates to
enum URLZONE {
URLZONE_LOCAL_MACHINE = 0,
URLZONE_INTRANET = 1,
URLZONE_TRUSTED = 2,
URLZONE_INTERNET = 3,
URLZONE_RESTRICTED = 4
};
(The original uses previous value + 1 rather than absolute values.)
As Hans says in the comments, these can be written with the standard Win32 file APIs CreateFile and WriteFile.
Firefox always writes Internet Zone, zone 3 - Firefox code here (MPL/LGPL/GPL tri-license):
bool SetInternetZoneIdentifier(const FilePath& full_path) {
const DWORD kShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
std::wstring path = full_path.value() + L":Zone.Identifier";
HANDLE file = CreateFile(path.c_str(), GENERIC_WRITE, kShare, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == file)
return false;
const char kIdentifier[] = "[ZoneTransfer]\nZoneId=3";
DWORD written = 0;
BOOL result = WriteFile(file, kIdentifier, arraysize(kIdentifier), &written,
NULL);
CloseHandle(file);
if (!result || written != arraysize(kIdentifier)) {
DCHECK(FALSE);
return false;
}
return true;
}
Alternatively there's an IE COM API CLSID_PersistentZoneIdentifier you can use to abstract this all for you.
It is not explicitly stated in RFC 3514, but today, due to increased security requirements, implementations should really retain the information of the presence or absence of the RFC3514 bit in a network transmission, when they write files out to disk, and vice-versa for reading from disk.