Sandboxie/SandboxiePlus/SandMan/Helpers/ReadDirectoryChangesPrivate...

184 lines
5.8 KiB
C++

//
// The MIT License
//
// Copyright (c) 2010 James E Beveridge
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW"
// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
// See ReadMe.txt for overview information.
#include "stdafx.h"
#include "ReadDirectoryChanges.h"
#include "ReadDirectoryChangesPrivate.h"
#include <shlwapi.h>
// The namespace is a convenience to emphasize that these are internals
// interfaces. The namespace can be safely removed if you need to.
namespace ReadDirectoryChangesPrivate
{
///////////////////////////////////////////////////////////////////////////
// CReadChangesRequest
CReadChangesRequest::CReadChangesRequest(CReadChangesServer* pServer, LPCTSTR sz, BOOL b, DWORD dw, DWORD size)
{
m_pServer = pServer;
m_dwFilterFlags = dw;
m_bIncludeChildren = b;
m_wstrDirectory = sz;
m_hDirectory = 0;
::ZeroMemory(&m_Overlapped, sizeof(OVERLAPPED));
// The hEvent member is not used when there is a completion
// function, so it's ok to use it to point to the object.
m_Overlapped.hEvent = this;
m_Buffer.resize(size);
m_BackupBuffer.resize(size);
}
CReadChangesRequest::~CReadChangesRequest()
{
// RequestTermination() must have been called successfully.
_ASSERTE(m_hDirectory == NULL || m_hDirectory == INVALID_HANDLE_VALUE);
}
bool CReadChangesRequest::OpenDirectory()
{
// Allow this routine to be called redundantly.
if (m_hDirectory)
return true;
m_hDirectory = ::CreateFileW(
m_wstrDirectory.c_str(), // pointer to the file name
FILE_LIST_DIRECTORY, // access (read/write) mode
FILE_SHARE_READ // share mode
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE,
NULL, // security descriptor
OPEN_EXISTING, // how to create
FILE_FLAG_BACKUP_SEMANTICS // file attributes
| FILE_FLAG_OVERLAPPED,
NULL); // file with attributes to copy
if (m_hDirectory == INVALID_HANDLE_VALUE)
{
return false;
}
return true;
}
void CReadChangesRequest::BeginRead()
{
DWORD dwBytes=0;
// This call needs to be reissued after every APC.
BOOL success = ::ReadDirectoryChangesW(
m_hDirectory, // handle to directory
&m_Buffer[0], // read results buffer
m_Buffer.size(), // length of buffer
m_bIncludeChildren, // monitoring option
m_dwFilterFlags, // filter conditions
&dwBytes, // bytes returned
&m_Overlapped, // overlapped buffer
&NotificationCompletion); // completion routine
}
//static
VOID CALLBACK CReadChangesRequest::NotificationCompletion(
DWORD dwErrorCode, // completion code
DWORD dwNumberOfBytesTransfered, // number of bytes transferred
LPOVERLAPPED lpOverlapped) // I/O information buffer
{
CReadChangesRequest* pBlock = (CReadChangesRequest*)lpOverlapped->hEvent;
if (dwErrorCode == ERROR_OPERATION_ABORTED)
{
::InterlockedDecrement(&pBlock->m_pServer->m_nOutstandingRequests);
delete pBlock;
return;
}
// This might mean overflow? Not sure.
if(!dwNumberOfBytesTransfered)
return;
// Can't use sizeof(FILE_NOTIFY_INFORMATION) because
// the structure is padded to 16 bytes.
_ASSERTE(dwNumberOfBytesTransfered >= offsetof(FILE_NOTIFY_INFORMATION, FileName) + sizeof(WCHAR));
pBlock->BackupBuffer(dwNumberOfBytesTransfered);
// Get the new read issued as fast as possible. The documentation
// says that the original OVERLAPPED structure will not be used
// again once the completion routine is called.
pBlock->BeginRead();
//pBlock->ProcessNotification();
pBlock->m_pServer->m_pBase->Notify(pBlock->GetDirectory());
}
void CReadChangesRequest::ProcessNotification()
{
BYTE* pBase = m_BackupBuffer.data();
for (;;)
{
FILE_NOTIFY_INFORMATION& fni = (FILE_NOTIFY_INFORMATION&)*pBase;
std::wstring wstrFilename(fni.FileName, fni.FileNameLength/sizeof(wchar_t));
// Handle a trailing backslash, such as for a root directory.
if (m_wstrDirectory.back() != L'\\')
wstrFilename = m_wstrDirectory + L"\\" + wstrFilename;
else
wstrFilename = m_wstrDirectory + wstrFilename;
// If it could be a short filename, expand it.
LPCWSTR wszFilename = PathFindFileNameW(wstrFilename.c_str());
int len = lstrlenW(wszFilename);
// The maximum length of an 8.3 filename is twelve, including the dot.
if (len <= 12 && wcschr(wszFilename, L'~'))
{
// Convert to the long filename form. Unfortunately, this
// does not work for deletions, so it's an imperfect fix.
wchar_t wbuf[MAX_PATH];
if (::GetLongPathNameW(wstrFilename.c_str(), wbuf, _countof(wbuf)) > 0)
wstrFilename = wbuf;
}
//m_pServer->m_pBase->Push(fni.Action, wstrFilename);
if (!fni.NextEntryOffset)
break;
pBase += fni.NextEntryOffset;
};
}
}