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