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