1 /* 2 * Copyright 2010, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Clemens Zeidler <haiku@clemens-zeidler.de> 7 */ 8 9 #include "VolumeWatcher.h" 10 11 #include <sys/stat.h> 12 13 #include <Autolock.h> 14 #include <Directory.h> 15 #include <NodeMonitor.h> 16 #include <Path.h> 17 #include <VolumeRoster.h> 18 #include <Query.h> 19 20 21 #include "IndexServerPrivate.h" 22 23 24 const bigtime_t kSecond = 1000000; 25 26 27 WatchNameHandler::WatchNameHandler(VolumeWatcher* volumeWatcher) 28 : 29 fVolumeWatcher(volumeWatcher) 30 { 31 32 } 33 34 35 void 36 WatchNameHandler::EntryCreated(const char *name, ino_t directory, dev_t device, 37 ino_t node) 38 { 39 entry_ref ref(device, directory, name); 40 fVolumeWatcher->fCreatedList.CurrentList()->push_back(ref); 41 fVolumeWatcher->_NewEntriesArrived(); 42 } 43 44 45 void 46 WatchNameHandler::EntryRemoved(const char *name, ino_t directory, dev_t device, 47 ino_t node) 48 { 49 entry_ref ref(device, directory, name); 50 fVolumeWatcher->fDeleteList.CurrentList()->push_back(ref); 51 fVolumeWatcher->_NewEntriesArrived(); 52 } 53 54 55 void 56 WatchNameHandler::EntryMoved(const char *name, const char *fromName, 57 ino_t from_directory, ino_t to_directory, dev_t device, ino_t node, 58 dev_t nodeDevice) 59 { 60 entry_ref ref(device, to_directory, name); 61 entry_ref refFrom(device, from_directory, fromName); 62 63 fVolumeWatcher->fMovedList.CurrentList()->push_back(ref); 64 fVolumeWatcher->fMovedFromList.CurrentList()->push_back(refFrom); 65 fVolumeWatcher->_NewEntriesArrived(); 66 } 67 68 69 void 70 WatchNameHandler::StatChanged(ino_t node, dev_t device, int32 statFields) 71 { 72 if ((statFields & B_STAT_MODIFICATION_TIME) == 0) 73 return; 74 } 75 76 77 AnalyserDispatcher::AnalyserDispatcher(const char* name) 78 : 79 BLooper(name, B_LOW_PRIORITY), 80 81 fStopped(0) 82 { 83 84 } 85 86 87 AnalyserDispatcher::~AnalyserDispatcher() 88 { 89 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 90 delete fFileAnalyserList.ItemAt(i); 91 } 92 93 94 void 95 AnalyserDispatcher::Stop() 96 { 97 atomic_set(&fStopped, 1); 98 } 99 100 101 bool 102 AnalyserDispatcher::Stopped() 103 { 104 return (atomic_get(&fStopped) != 0); 105 } 106 107 108 void 109 AnalyserDispatcher::AnalyseEntry(const entry_ref& ref) 110 { 111 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 112 fFileAnalyserList.ItemAt(i)->AnalyseEntry(ref); 113 } 114 115 116 void 117 AnalyserDispatcher::DeleteEntry(const entry_ref& ref) 118 { 119 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 120 fFileAnalyserList.ItemAt(i)->DeleteEntry(ref); 121 } 122 123 124 void 125 AnalyserDispatcher::MoveEntry(const entry_ref& oldRef, const entry_ref& newRef) 126 { 127 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 128 fFileAnalyserList.ItemAt(i)->MoveEntry(oldRef, newRef); 129 } 130 131 132 void 133 AnalyserDispatcher::LastEntry() 134 { 135 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 136 fFileAnalyserList.ItemAt(i)->LastEntry(); 137 } 138 139 140 bool 141 AnalyserDispatcher::AddAnalyser(FileAnalyser* analyser) 142 { 143 if (analyser == NULL) 144 return false; 145 146 bool result; 147 BAutolock _(this); 148 if (_FindAnalyser(analyser->Name())) 149 return false; 150 151 result = fFileAnalyserList.AddItem(analyser); 152 return result; 153 } 154 155 156 bool 157 AnalyserDispatcher::RemoveAnalyser(const BString& name) 158 { 159 BAutolock _(this); 160 FileAnalyser* analyser = _FindAnalyser(name); 161 if (analyser) { 162 fFileAnalyserList.RemoveItem(analyser); 163 delete analyser; 164 return true; 165 } 166 return false; 167 } 168 169 170 FileAnalyser* 171 AnalyserDispatcher::_FindAnalyser(const BString& name) 172 { 173 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) { 174 FileAnalyser* analyser = fFileAnalyserList.ItemAt(i); 175 if (analyser->Name() == name) 176 return analyser; 177 } 178 return NULL; 179 } 180 181 182 void 183 AnalyserDispatcher::WriteAnalyserSettings() 184 { 185 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 186 fFileAnalyserList.ItemAt(i)->Settings()->WriteSettings(); 187 } 188 189 190 void 191 AnalyserDispatcher::SetSyncPosition(bigtime_t time) 192 { 193 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 194 fFileAnalyserList.ItemAt(i)->Settings()->SetSyncPosition(time); 195 } 196 197 198 void 199 AnalyserDispatcher::SetWatchingStart(bigtime_t time) 200 { 201 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 202 fFileAnalyserList.ItemAt(i)->Settings()->SetWatchingStart(time); 203 } 204 205 206 void 207 AnalyserDispatcher::SetWatchingPosition(bigtime_t time) 208 { 209 for (int i = 0; i < fFileAnalyserList.CountItems(); i++) 210 fFileAnalyserList.ItemAt(i)->Settings()->SetWatchingPosition(time); 211 } 212 213 214 VolumeWorker::VolumeWorker(VolumeWatcher* watcher) 215 : 216 AnalyserDispatcher("VolumeWorker"), 217 218 fVolumeWatcher(watcher), 219 fBusy(0) 220 { 221 222 } 223 224 225 void 226 VolumeWorker::MessageReceived(BMessage *message) 227 { 228 switch (message->what) { 229 case kTriggerWork: 230 _Work(); 231 break; 232 233 default: 234 BLooper::MessageReceived(message); 235 } 236 } 237 238 239 bool 240 VolumeWorker::IsBusy() 241 { 242 return (atomic_get(&fBusy) != 0); 243 } 244 245 246 void 247 VolumeWorker::_Work() 248 { 249 list_collection collection; 250 fVolumeWatcher->GetSecureEntries(collection); 251 252 if (collection.createdList->size() == 0 253 && collection.deletedList->size() == 0 254 && collection.modifiedList->size() == 0 255 && collection.movedList->size() == 0) 256 return; 257 258 _SetBusy(true); 259 for (unsigned int i = 0; i < collection.createdList->size() || Stopped(); 260 i++) 261 AnalyseEntry((*collection.createdList)[i]); 262 collection.createdList->clear(); 263 264 for (unsigned int i = 0; i < collection.deletedList->size() || Stopped(); 265 i++) 266 DeleteEntry((*collection.deletedList)[i]); 267 collection.deletedList->clear(); 268 269 for (unsigned int i = 0; i < collection.modifiedList->size() || Stopped(); 270 i++) 271 AnalyseEntry((*collection.modifiedList)[i]); 272 collection.modifiedList->clear(); 273 274 for (unsigned int i = 0; i < collection.movedList->size() || Stopped(); 275 i++) 276 MoveEntry((*collection.movedFromList)[i], (*collection.movedList)[i]); 277 collection.movedList->clear(); 278 collection.movedFromList->clear(); 279 280 LastEntry(); 281 PostMessage(kTriggerWork); 282 283 _SetBusy(false); 284 } 285 286 287 void 288 VolumeWorker::_SetBusy(bool busy) 289 { 290 if (busy) 291 atomic_set(&fBusy, 1); 292 else 293 atomic_set(&fBusy, 0); 294 } 295 296 297 VolumeWatcherBase::VolumeWatcherBase(const BVolume& volume) 298 : 299 fVolume(volume), 300 fEnabled(true), 301 fLastUpdated(0) 302 { 303 ReadSettings(); 304 } 305 306 307 const char* kEnabledAttr = "Enabled"; 308 309 310 bool 311 VolumeWatcherBase::ReadSettings() 312 { 313 // TODO remove this 314 BVolume bootVolume; 315 BVolumeRoster roster; 316 roster.GetBootVolume(&bootVolume); 317 if (bootVolume == fVolume) { 318 fEnabled = true; 319 WriteSettings(); 320 } 321 322 BDirectory rootDir; 323 fVolume.GetRootDirectory(&rootDir); 324 BPath path(&rootDir); 325 path.Append(kIndexServerDirectory); 326 path.Append(kVolumeStatusFileName); 327 BFile file(path.Path(), B_READ_ONLY); 328 if (file.InitCheck() != B_OK) 329 return false; 330 331 uint32 enabled; 332 file.WriteAttr(kEnabledAttr, B_UINT32_TYPE, 0, &enabled, sizeof(uint32)); 333 fEnabled = enabled == 0 ? false : true; 334 335 return true; 336 } 337 338 339 bool 340 VolumeWatcherBase::WriteSettings() 341 { 342 BDirectory rootDir; 343 fVolume.GetRootDirectory(&rootDir); 344 BPath path(&rootDir); 345 path.Append(kIndexServerDirectory); 346 if (create_directory(path.Path(), 777) != B_OK) 347 return false; 348 349 path.Append(kVolumeStatusFileName); 350 BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 351 if (file.InitCheck() != B_OK) 352 return false; 353 354 uint32 enabled = fEnabled ? 1 : 0; 355 file.WriteAttr(kEnabledAttr, B_UINT32_TYPE, 0, &enabled, sizeof(uint32)); 356 357 return true; 358 } 359 360 361 SwapEntryRefVector::SwapEntryRefVector() 362 { 363 fCurrentList = &fFirstList; 364 fNextList = &fSecondList; 365 } 366 367 368 EntryRefVector* 369 SwapEntryRefVector::SwapList() 370 { 371 EntryRefVector* temp = fCurrentList; 372 fCurrentList = fNextList; 373 fNextList = temp; 374 return temp; 375 } 376 377 378 EntryRefVector* 379 SwapEntryRefVector::CurrentList() 380 { 381 return fCurrentList; 382 } 383 384 385 VolumeWatcher::VolumeWatcher(const BVolume& volume) 386 : 387 VolumeWatcherBase(volume), 388 BLooper("VolumeWatcher"), 389 390 fWatching(false), 391 fWatchNameHandler(this), 392 fCatchUpManager(volume) 393 { 394 AddHandler(&fWatchNameHandler); 395 396 fVolumeWorker = new VolumeWorker(this); 397 fVolumeWorker->Run(); 398 } 399 400 401 VolumeWatcher::~VolumeWatcher() 402 { 403 Stop(); 404 thread_id threadId = fVolumeWorker->Thread(); 405 fVolumeWorker->PostMessage(B_QUIT_REQUESTED); 406 status_t error; 407 wait_for_thread(threadId, &error); 408 } 409 410 411 void 412 VolumeWatcher::MessageReceived(BMessage *message) 413 { 414 int32 opcode; 415 switch (message->what) { 416 case B_QUERY_UPDATE: 417 message->FindInt32("opcode", &opcode); 418 if (opcode == B_ATTR_CHANGED || opcode == B_ENTRY_CREATED) { 419 const char *name; 420 ino_t directory; 421 dev_t device; 422 if ((message->FindString("name", &name) != B_OK) || 423 (message->FindInt64("directory", &directory) != B_OK) || 424 (message->FindInt32("device", &device) != B_OK)) 425 break; 426 entry_ref ref(device, directory, name); 427 fModifiedList.CurrentList()->push_back(ref); 428 _NewEntriesArrived(); 429 } 430 break; 431 432 default: 433 BLooper::MessageReceived(message); 434 } 435 } 436 437 438 bool 439 VolumeWatcher::StartWatching() 440 { 441 Run(); 442 443 watch_volume(fVolume.Device(), B_WATCH_NAME | B_WATCH_STAT, 444 &fWatchNameHandler); 445 if (fModfiedNotifications.StartWatching(fVolume.Device(), real_time_clock(), 446 this) != B_OK) 447 return false; 448 // set the time after start watching to not miss anything 449 fVolumeWorker->SetWatchingStart(real_time_clock_usecs()); 450 451 char name[255]; 452 fVolume.GetName(name); 453 454 fCatchUpManager.CatchUp(); 455 456 fWatching = true; 457 return true; 458 } 459 460 461 void 462 VolumeWatcher::Stop() 463 { 464 465 char name[255]; 466 fVolume.GetName(name); 467 468 // set the time before stop watching to not miss anything 469 fVolumeWorker->SetWatchingPosition(real_time_clock_usecs()); 470 471 stop_watching(&fWatchNameHandler); 472 473 fVolumeWorker->WriteAnalyserSettings(); 474 475 // don't stop the work because we have to handle all entries after writing 476 // the watching position 477 //fVolumeWorker->Stop(); 478 fCatchUpManager.Stop(); 479 } 480 481 482 bool 483 VolumeWatcher::AddAnalyser(FileAnalyser* analyser) 484 { 485 if (!fVolumeWorker->AddAnalyser(analyser)) 486 return false; 487 488 BAutolock _(this); 489 if (!fCatchUpManager.AddAnalyser(analyser)) 490 return false; 491 492 if (fWatching) 493 fCatchUpManager.CatchUp(); 494 495 return true; 496 } 497 498 499 bool 500 VolumeWatcher::RemoveAnalyser(const BString& name) 501 { 502 if (!fVolumeWorker->RemoveAnalyser(name)) 503 return false; 504 505 BAutolock _(this); 506 fCatchUpManager.RemoveAnalyser(name); 507 return true; 508 } 509 510 511 void 512 VolumeWatcher::GetSecureEntries(list_collection& collection) 513 { 514 BAutolock _(this); 515 collection.createdList = fCreatedList.SwapList(); 516 collection.deletedList = fDeleteList.SwapList(); 517 collection.modifiedList = fModifiedList.SwapList(); 518 collection.movedList = fMovedList.SwapList(); 519 collection.movedFromList = fMovedFromList.SwapList(); 520 } 521 522 523 bool 524 VolumeWatcher::FindEntryRef(ino_t node, dev_t device, entry_ref& entry) 525 { 526 return false; 527 } 528 529 530 void 531 VolumeWatcher::_NewEntriesArrived() 532 { 533 // The fVolumeWorker has to exist as long as we live so directly post to 534 // the queue. 535 if (fVolumeWorker->IsBusy()) 536 return; 537 fVolumeWorker->PostMessage(kTriggerWork); 538 } 539