1f09fa651SNicolas Bonnefon #include "winwatchtowerdriver.h" 2f09fa651SNicolas Bonnefon 3f09fa651SNicolas Bonnefon #define WIN32_LEAN_AND_MEAN 4f09fa651SNicolas Bonnefon #include <windows.h> 5f09fa651SNicolas Bonnefon #include <winbase.h> 6f09fa651SNicolas Bonnefon 7f09fa651SNicolas Bonnefon #include <map> 8f09fa651SNicolas Bonnefon 9f09fa651SNicolas Bonnefon #include "watchtowerlist.h" 10f09fa651SNicolas Bonnefon #include "utils.h" 11f09fa651SNicolas Bonnefon #include "log.h" 12f09fa651SNicolas Bonnefon 13f09fa651SNicolas Bonnefon namespace { 14f09fa651SNicolas Bonnefon std::string shortstringize( const std::wstring& long_string ); 15f09fa651SNicolas Bonnefon std::wstring longstringize( const std::string& short_string ); 16f09fa651SNicolas Bonnefon }; 17f09fa651SNicolas Bonnefon 18f09fa651SNicolas Bonnefon // Utility classes 19f09fa651SNicolas Bonnefon 20f09fa651SNicolas Bonnefon WinNotificationInfoList::WinNotificationInfoList( const char* buffer, size_t buffer_size ) 21f09fa651SNicolas Bonnefon { 22f09fa651SNicolas Bonnefon pointer_ = buffer; 23f09fa651SNicolas Bonnefon next_ = updateCurrentNotification( pointer_ ); 24f09fa651SNicolas Bonnefon } 25f09fa651SNicolas Bonnefon 26f09fa651SNicolas Bonnefon const char* WinNotificationInfoList::updateCurrentNotification( 27f09fa651SNicolas Bonnefon const char* new_position ) 28f09fa651SNicolas Bonnefon { 29f09fa651SNicolas Bonnefon using Action = WinNotificationInfo::Action; 30f09fa651SNicolas Bonnefon 31f09fa651SNicolas Bonnefon static const std::map<uint16_t, Action> int_to_action = { 32f09fa651SNicolas Bonnefon { FILE_ACTION_ADDED, Action::ADDED }, 33f09fa651SNicolas Bonnefon { FILE_ACTION_REMOVED, Action::REMOVED }, 34f09fa651SNicolas Bonnefon { FILE_ACTION_MODIFIED, Action::MODIFIED }, 35f09fa651SNicolas Bonnefon { FILE_ACTION_RENAMED_OLD_NAME, Action::RENAMED_OLD_NAME }, 36f09fa651SNicolas Bonnefon { FILE_ACTION_RENAMED_NEW_NAME, Action::RENAMED_NEW_NAME }, 37f09fa651SNicolas Bonnefon }; 38f09fa651SNicolas Bonnefon 39f09fa651SNicolas Bonnefon uint32_t next_offset = *( reinterpret_cast<const uint32_t*>( new_position ) ); 40f09fa651SNicolas Bonnefon uint32_t action = *( reinterpret_cast<const uint32_t*>( new_position ) + 1 ); 41f09fa651SNicolas Bonnefon uint32_t length = *( reinterpret_cast<const uint32_t*>( new_position ) + 2 ); 42f09fa651SNicolas Bonnefon 43f09fa651SNicolas Bonnefon const std::wstring file_name = { reinterpret_cast<const wchar_t*>( new_position + 12 ), length / 2 }; 44f09fa651SNicolas Bonnefon 45f09fa651SNicolas Bonnefon LOG(logDEBUG) << "Next: " << next_offset; 46f09fa651SNicolas Bonnefon LOG(logDEBUG) << "Action: " << action; 47f09fa651SNicolas Bonnefon LOG(logDEBUG) << "Length: " << length; 48f09fa651SNicolas Bonnefon 49f09fa651SNicolas Bonnefon current_notification_ = WinNotificationInfo( int_to_action.at( action ), file_name ); 50f09fa651SNicolas Bonnefon 51f09fa651SNicolas Bonnefon return ( next_offset == 0 ) ? nullptr : new_position + next_offset; 52f09fa651SNicolas Bonnefon } 53f09fa651SNicolas Bonnefon 54f09fa651SNicolas Bonnefon const char* WinNotificationInfoList::advanceToNext() 55f09fa651SNicolas Bonnefon { 56f09fa651SNicolas Bonnefon pointer_ = next_; 57f09fa651SNicolas Bonnefon if ( pointer_ ) 58f09fa651SNicolas Bonnefon next_ = updateCurrentNotification( pointer_ ); 59f09fa651SNicolas Bonnefon 60f09fa651SNicolas Bonnefon return pointer_; 61f09fa651SNicolas Bonnefon } 62f09fa651SNicolas Bonnefon 63f09fa651SNicolas Bonnefon // WinWatchTowerDriver 64f09fa651SNicolas Bonnefon 65f09fa651SNicolas Bonnefon WinWatchTowerDriver::WinWatchTowerDriver() 66f09fa651SNicolas Bonnefon { 67f09fa651SNicolas Bonnefon hCompPort_ = CreateIoCompletionPort( INVALID_HANDLE_VALUE, 68f09fa651SNicolas Bonnefon NULL, 69f09fa651SNicolas Bonnefon 0x0, 70f09fa651SNicolas Bonnefon 0); 71f09fa651SNicolas Bonnefon } 72f09fa651SNicolas Bonnefon 73f09fa651SNicolas Bonnefon WinWatchTowerDriver::~WinWatchTowerDriver() 74f09fa651SNicolas Bonnefon { 75f09fa651SNicolas Bonnefon } 76f09fa651SNicolas Bonnefon 77f09fa651SNicolas Bonnefon WinWatchTowerDriver::FileId WinWatchTowerDriver::addFile( 78f09fa651SNicolas Bonnefon const std::string& file_name ) 79f09fa651SNicolas Bonnefon { 80f09fa651SNicolas Bonnefon // Nothing for Windows 81f09fa651SNicolas Bonnefon return { }; 82f09fa651SNicolas Bonnefon } 83f09fa651SNicolas Bonnefon 84f09fa651SNicolas Bonnefon WinWatchTowerDriver::SymlinkId WinWatchTowerDriver::addSymlink( 85f09fa651SNicolas Bonnefon const std::string& file_name ) 86f09fa651SNicolas Bonnefon { 87f09fa651SNicolas Bonnefon // Nothing for Windows 88f09fa651SNicolas Bonnefon return { }; 89f09fa651SNicolas Bonnefon } 90f09fa651SNicolas Bonnefon 91f09fa651SNicolas Bonnefon // This implementation is blocking, i.e. it will wait until the file 92f09fa651SNicolas Bonnefon // is effectively loaded in the watchtower thread. 93f09fa651SNicolas Bonnefon WinWatchTowerDriver::DirId WinWatchTowerDriver::addDir( 94f09fa651SNicolas Bonnefon const std::string& file_name ) 95f09fa651SNicolas Bonnefon { 96f09fa651SNicolas Bonnefon DirId dir_id { }; 97f09fa651SNicolas Bonnefon 98f09fa651SNicolas Bonnefon // Add will be done in the watchtower thread 99f09fa651SNicolas Bonnefon { 1003104b268SNicolas Bonnefon /* 101f09fa651SNicolas Bonnefon std::lock_guard<std::mutex> lk( action_mutex_ ); 102f09fa651SNicolas Bonnefon scheduled_action_ = std::make_unique<Action>( [this, file_name, &dir_id] { 103f09fa651SNicolas Bonnefon serialisedAddDir( file_name, dir_id ); 104f09fa651SNicolas Bonnefon } ); 1053104b268SNicolas Bonnefon */ 1063104b268SNicolas Bonnefon serialisedAddDir( file_name, dir_id ); 107f09fa651SNicolas Bonnefon } 108f09fa651SNicolas Bonnefon 109f09fa651SNicolas Bonnefon // Poke the thread 110f09fa651SNicolas Bonnefon PostQueuedCompletionStatus( hCompPort_, 0, 0, NULL ); 111f09fa651SNicolas Bonnefon 112f09fa651SNicolas Bonnefon // Wait for the add task to be completed 113f09fa651SNicolas Bonnefon { 1143104b268SNicolas Bonnefon /* 115f09fa651SNicolas Bonnefon std::unique_lock<std::mutex> lk( action_mutex_ ); 116f09fa651SNicolas Bonnefon action_done_cv_.wait( lk, 117f09fa651SNicolas Bonnefon [this]{ return ( scheduled_action_ == nullptr ); } ); 1183104b268SNicolas Bonnefon */ 119f09fa651SNicolas Bonnefon } 120f09fa651SNicolas Bonnefon 121*dc1c9514SNicolas Bonnefon LOG(logDEBUG) << "addDir returned " << dir_id.dir_record_; 122f09fa651SNicolas Bonnefon 123f09fa651SNicolas Bonnefon return dir_id; 124f09fa651SNicolas Bonnefon } 125f09fa651SNicolas Bonnefon 126f09fa651SNicolas Bonnefon 127f09fa651SNicolas Bonnefon void WinWatchTowerDriver::removeFile( 1288b11848fSNicolas Bonnefon const WinWatchTowerDriver::FileId& ) 129f09fa651SNicolas Bonnefon { 130f09fa651SNicolas Bonnefon } 131f09fa651SNicolas Bonnefon 1328b11848fSNicolas Bonnefon void WinWatchTowerDriver::removeSymlink( const SymlinkId& ) 133f09fa651SNicolas Bonnefon { 134f09fa651SNicolas Bonnefon } 135f09fa651SNicolas Bonnefon 1363104b268SNicolas Bonnefon void WinWatchTowerDriver::removeDir( const DirId& dir_id ) 1373104b268SNicolas Bonnefon { 138*dc1c9514SNicolas Bonnefon LOG(logDEBUG) << "Entering driver::removeDir"; 1398b11848fSNicolas Bonnefon if ( dir_id.dir_record_ ) { 1403104b268SNicolas Bonnefon void* handle = dir_id.dir_record_->handle_; 1413104b268SNicolas Bonnefon 1423104b268SNicolas Bonnefon LOG(logDEBUG) << "WinWatchTowerDriver::removeDir handle=" << std::hex << handle; 1433104b268SNicolas Bonnefon 1443104b268SNicolas Bonnefon CloseHandle( handle ); 1453104b268SNicolas Bonnefon } 1468b11848fSNicolas Bonnefon else { 1478b11848fSNicolas Bonnefon /* Happens when an error occured when creating the dir_record_ */ 1488b11848fSNicolas Bonnefon } 1498b11848fSNicolas Bonnefon } 1503104b268SNicolas Bonnefon 151f09fa651SNicolas Bonnefon // 152f09fa651SNicolas Bonnefon // Private functions 153f09fa651SNicolas Bonnefon // 154f09fa651SNicolas Bonnefon 155f09fa651SNicolas Bonnefon // Add a file (run in the context of the WatchTower thread) 156f09fa651SNicolas Bonnefon void WinWatchTowerDriver::serialisedAddDir( 157f09fa651SNicolas Bonnefon const std::string& dir_name, 158f09fa651SNicolas Bonnefon DirId& dir_id ) 159f09fa651SNicolas Bonnefon { 160*dc1c9514SNicolas Bonnefon bool inserted = false; 161f09fa651SNicolas Bonnefon auto dir_record = std::make_shared<WinWatchedDirRecord>( dir_name ); 162*dc1c9514SNicolas Bonnefon // The index we will be inserting this record (if success), plus 1 (to avoid 163*dc1c9514SNicolas Bonnefon // 0 which is used as a magic value) 164*dc1c9514SNicolas Bonnefon unsigned int index_record = dir_records_.size() + 1; 165f09fa651SNicolas Bonnefon 166f09fa651SNicolas Bonnefon LOG(logDEBUG) << "Adding dir for: " << dir_name; 167f09fa651SNicolas Bonnefon 168f09fa651SNicolas Bonnefon // Open the directory 169f09fa651SNicolas Bonnefon HANDLE hDir = CreateFile( 170f09fa651SNicolas Bonnefon #ifdef UNICODE 171f09fa651SNicolas Bonnefon longstringize( dir_name ).c_str(), 172f09fa651SNicolas Bonnefon #else 173f09fa651SNicolas Bonnefon ( dir_name ).c_str(), 174f09fa651SNicolas Bonnefon #endif 175f09fa651SNicolas Bonnefon FILE_LIST_DIRECTORY, 176f09fa651SNicolas Bonnefon FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 177f09fa651SNicolas Bonnefon NULL, 178f09fa651SNicolas Bonnefon OPEN_EXISTING, 179f09fa651SNicolas Bonnefon FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, 180f09fa651SNicolas Bonnefon NULL ); 181f09fa651SNicolas Bonnefon 1823104b268SNicolas Bonnefon if ( hDir == INVALID_HANDLE_VALUE ) { 1833104b268SNicolas Bonnefon LOG(logERROR) << "CreateFile failed for dir " << dir_name; 1843104b268SNicolas Bonnefon } 185*dc1c9514SNicolas Bonnefon else { 186f09fa651SNicolas Bonnefon dir_record->handle_ = hDir; 187f09fa651SNicolas Bonnefon 188f09fa651SNicolas Bonnefon //create a IO completion port/or associate this key with 189f09fa651SNicolas Bonnefon //the existing IO completion port 190f09fa651SNicolas Bonnefon hCompPort_ = CreateIoCompletionPort( hDir, 191f09fa651SNicolas Bonnefon hCompPort_, //if m_hCompPort is NULL, hDir is associated with a NEW completion port, 192f09fa651SNicolas Bonnefon //if m_hCompPort is NON-NULL, hDir is associated with the existing completion port that the handle m_hCompPort references 193f09fa651SNicolas Bonnefon // We use the index (plus 1) of the weak_ptr as a key 194f09fa651SNicolas Bonnefon index_record, 195f09fa651SNicolas Bonnefon 0 ); 196f09fa651SNicolas Bonnefon 197f09fa651SNicolas Bonnefon LOG(logDEBUG) << "Weak ptr address stored: " << index_record; 198f09fa651SNicolas Bonnefon 199f09fa651SNicolas Bonnefon memset( &overlapped_, 0, sizeof overlapped_ ); 200f09fa651SNicolas Bonnefon 201*dc1c9514SNicolas Bonnefon inserted = ReadDirectoryChangesW( hDir, 202f09fa651SNicolas Bonnefon dir_record->buffer_, 203f09fa651SNicolas Bonnefon dir_record->buffer_length_, 204f09fa651SNicolas Bonnefon false, 205f09fa651SNicolas Bonnefon FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, 206f09fa651SNicolas Bonnefon &buffer_length_, // not set when using asynchronous mechanisms... 207f09fa651SNicolas Bonnefon &overlapped_, 208f09fa651SNicolas Bonnefon NULL ); // no completion routine 209f09fa651SNicolas Bonnefon 210*dc1c9514SNicolas Bonnefon if ( ! inserted ) { 2113104b268SNicolas Bonnefon LOG(logERROR) << "ReadDirectoryChangesW failed (" << GetLastError() << ")"; 212*dc1c9514SNicolas Bonnefon CloseHandle( hDir ); 2133104b268SNicolas Bonnefon } 2148b11848fSNicolas Bonnefon else { 215f09fa651SNicolas Bonnefon dir_id.dir_record_ = dir_record; 216f09fa651SNicolas Bonnefon } 2178b11848fSNicolas Bonnefon } 218f09fa651SNicolas Bonnefon 219*dc1c9514SNicolas Bonnefon if ( inserted ) { 220*dc1c9514SNicolas Bonnefon dir_records_.push_back( std::weak_ptr<WinWatchedDirRecord>( dir_record ) ); 221*dc1c9514SNicolas Bonnefon } 222*dc1c9514SNicolas Bonnefon } 223*dc1c9514SNicolas Bonnefon 224f09fa651SNicolas Bonnefon std::vector<ObservedFile<WinWatchTowerDriver>*> WinWatchTowerDriver::waitAndProcessEvents( 225f09fa651SNicolas Bonnefon ObservedFileList<WinWatchTowerDriver>* list, 22691f7c705SNicolas Bonnefon std::unique_lock<std::mutex>* lock, 22791f7c705SNicolas Bonnefon std::vector<ObservedFile<WinWatchTowerDriver>*>* /* not needed in WinWatchTowerDriver */ ) 228f09fa651SNicolas Bonnefon { 229f09fa651SNicolas Bonnefon std::vector<ObservedFile<WinWatchTowerDriver>*> files_to_notify { }; 230f09fa651SNicolas Bonnefon 231f09fa651SNicolas Bonnefon unsigned long long key = 0; 232f09fa651SNicolas Bonnefon DWORD num_bytes = 0; 233f09fa651SNicolas Bonnefon LPOVERLAPPED lpOverlapped = 0; 234f09fa651SNicolas Bonnefon 2353104b268SNicolas Bonnefon lock->unlock(); 236*dc1c9514SNicolas Bonnefon LOG(logDEBUG) << "waitAndProcessEvents now blocking..."; 237f09fa651SNicolas Bonnefon BOOL status = GetQueuedCompletionStatus( hCompPort_, 238f09fa651SNicolas Bonnefon &num_bytes, 239f09fa651SNicolas Bonnefon &key, 240f09fa651SNicolas Bonnefon &lpOverlapped, 241f09fa651SNicolas Bonnefon INFINITE ); 2423104b268SNicolas Bonnefon lock->lock(); 243f09fa651SNicolas Bonnefon 2443104b268SNicolas Bonnefon LOG(logDEBUG) << "Event (" << status << ") key: " << std::hex << key; 245f09fa651SNicolas Bonnefon 246f09fa651SNicolas Bonnefon if ( key ) { 247f09fa651SNicolas Bonnefon // Extract the dir from the completion key 248f09fa651SNicolas Bonnefon auto dir_record_ptr = dir_records_[key - 1]; 249f09fa651SNicolas Bonnefon LOG(logDEBUG) << "use_count = " << dir_record_ptr.use_count(); 250f09fa651SNicolas Bonnefon 251f09fa651SNicolas Bonnefon if ( std::shared_ptr<WinWatchedDirRecord> dir_record = dir_record_ptr.lock() ) 252f09fa651SNicolas Bonnefon { 253f09fa651SNicolas Bonnefon LOG(logDEBUG) << "Got event for dir " << dir_record.get(); 254f09fa651SNicolas Bonnefon 255f09fa651SNicolas Bonnefon WinNotificationInfoList notification_info( 256f09fa651SNicolas Bonnefon dir_record->buffer_, 257f09fa651SNicolas Bonnefon dir_record->buffer_length_ ); 258f09fa651SNicolas Bonnefon 259f09fa651SNicolas Bonnefon for ( auto notification : notification_info ) { 260f09fa651SNicolas Bonnefon std::string file_path = dir_record->path_ + shortstringize( notification.fileName() ); 261f09fa651SNicolas Bonnefon LOG(logDEBUG) << "File is " << file_path; 262f09fa651SNicolas Bonnefon auto file = list->searchByName( file_path ); 263f09fa651SNicolas Bonnefon 264f09fa651SNicolas Bonnefon if ( file ) 265f09fa651SNicolas Bonnefon { 266f09fa651SNicolas Bonnefon files_to_notify.push_back( file ); 267f09fa651SNicolas Bonnefon } 268f09fa651SNicolas Bonnefon } 269f09fa651SNicolas Bonnefon 270f09fa651SNicolas Bonnefon // Re-listen for changes 271f09fa651SNicolas Bonnefon status = ReadDirectoryChangesW( 272f09fa651SNicolas Bonnefon dir_record->handle_, 273f09fa651SNicolas Bonnefon dir_record->buffer_, 274f09fa651SNicolas Bonnefon dir_record->buffer_length_, 275f09fa651SNicolas Bonnefon false, 276f09fa651SNicolas Bonnefon FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, 277f09fa651SNicolas Bonnefon &buffer_length_,// not set when using asynchronous mechanisms... 278f09fa651SNicolas Bonnefon &overlapped_, 279f09fa651SNicolas Bonnefon NULL ); // no completion routine 280f09fa651SNicolas Bonnefon } 281f09fa651SNicolas Bonnefon else { 282f09fa651SNicolas Bonnefon LOG(logWARNING) << "Looks like our dir_record disappeared!"; 283f09fa651SNicolas Bonnefon } 284f09fa651SNicolas Bonnefon } 285f09fa651SNicolas Bonnefon else { 286f09fa651SNicolas Bonnefon LOG(logDEBUG) << "Signaled"; 287f09fa651SNicolas Bonnefon } 288f09fa651SNicolas Bonnefon 289f09fa651SNicolas Bonnefon { 290f09fa651SNicolas Bonnefon std::lock_guard<std::mutex> lk( action_mutex_ ); 291f09fa651SNicolas Bonnefon if ( scheduled_action_ ) { 292f09fa651SNicolas Bonnefon (*scheduled_action_)(); 293f09fa651SNicolas Bonnefon scheduled_action_ = nullptr; 294f09fa651SNicolas Bonnefon action_done_cv_.notify_all(); 295f09fa651SNicolas Bonnefon } 296f09fa651SNicolas Bonnefon } 297f09fa651SNicolas Bonnefon 298f09fa651SNicolas Bonnefon /* 299f09fa651SNicolas Bonnefon // Just in case someone is waiting for an action to complete 300f09fa651SNicolas Bonnefon std::lock_guard<std::mutex> lk( action_mutex_ ); 301f09fa651SNicolas Bonnefon scheduled_action_ = nullptr; 302f09fa651SNicolas Bonnefon action_done_cv_.notify_all(); 303f09fa651SNicolas Bonnefon */ 304f09fa651SNicolas Bonnefon return files_to_notify; 305f09fa651SNicolas Bonnefon } 306f09fa651SNicolas Bonnefon 307f09fa651SNicolas Bonnefon void WinWatchTowerDriver::interruptWait() 308f09fa651SNicolas Bonnefon { 309*dc1c9514SNicolas Bonnefon LOG(logDEBUG) << "Driver::interruptWait()"; 310f09fa651SNicolas Bonnefon PostQueuedCompletionStatus( hCompPort_, 0, 0, NULL ); 311f09fa651SNicolas Bonnefon } 312f09fa651SNicolas Bonnefon 313f09fa651SNicolas Bonnefon namespace { 314f09fa651SNicolas Bonnefon std::string shortstringize( const std::wstring& long_string ) 315f09fa651SNicolas Bonnefon { 316f09fa651SNicolas Bonnefon std::string short_result {}; 317f09fa651SNicolas Bonnefon 318f09fa651SNicolas Bonnefon for ( wchar_t c : long_string ) { 319f09fa651SNicolas Bonnefon // FIXME: that does not work for non ASCII char!! 320f09fa651SNicolas Bonnefon char short_c = static_cast<char>( c & 0x00FF ); 321f09fa651SNicolas Bonnefon short_result += short_c; 322f09fa651SNicolas Bonnefon } 323f09fa651SNicolas Bonnefon 324f09fa651SNicolas Bonnefon return short_result; 325f09fa651SNicolas Bonnefon } 326f09fa651SNicolas Bonnefon 327f09fa651SNicolas Bonnefon std::wstring longstringize( const std::string& short_string ) 328f09fa651SNicolas Bonnefon { 329f09fa651SNicolas Bonnefon std::wstring long_result {}; 330f09fa651SNicolas Bonnefon 331f09fa651SNicolas Bonnefon for ( char c : short_string ) { 332f09fa651SNicolas Bonnefon wchar_t long_c = static_cast<wchar_t>( c ); 333f09fa651SNicolas Bonnefon long_result += long_c; 334f09fa651SNicolas Bonnefon } 335f09fa651SNicolas Bonnefon 336f09fa651SNicolas Bonnefon return long_result; 337f09fa651SNicolas Bonnefon } 338f09fa651SNicolas Bonnefon }; 339