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
WatchNameHandler(VolumeWatcher * volumeWatcher)27 WatchNameHandler::WatchNameHandler(VolumeWatcher* volumeWatcher)
28 :
29 fVolumeWatcher(volumeWatcher)
30 {
31
32 }
33
34
35 void
EntryCreated(const char * name,ino_t directory,dev_t device,ino_t node)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
EntryRemoved(const char * name,ino_t directory,dev_t device,ino_t node)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
EntryMoved(const char * name,const char * fromName,ino_t from_directory,ino_t to_directory,dev_t device,ino_t node,dev_t nodeDevice)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
StatChanged(ino_t node,dev_t device,int32 statFields)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 void
MessageReceived(BMessage * msg)78 WatchNameHandler::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
AnalyserDispatcher(const char * name)115 AnalyserDispatcher::AnalyserDispatcher(const char* name)
116 :
117 BLooper(name, B_LOW_PRIORITY),
118
119 fStopped(0)
120 {
121
122 }
123
124
~AnalyserDispatcher()125 AnalyserDispatcher::~AnalyserDispatcher()
126 {
127 for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
128 delete fFileAnalyserList.ItemAt(i);
129 }
130
131
132 void
Stop()133 AnalyserDispatcher::Stop()
134 {
135 atomic_set(&fStopped, 1);
136 }
137
138
139 bool
Stopped()140 AnalyserDispatcher::Stopped()
141 {
142 return (atomic_get(&fStopped) != 0);
143 }
144
145
146 void
AnalyseEntry(const entry_ref & ref)147 AnalyserDispatcher::AnalyseEntry(const entry_ref& ref)
148 {
149 for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
150 fFileAnalyserList.ItemAt(i)->AnalyseEntry(ref);
151 }
152
153
154 void
DeleteEntry(const entry_ref & ref)155 AnalyserDispatcher::DeleteEntry(const entry_ref& ref)
156 {
157 for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
158 fFileAnalyserList.ItemAt(i)->DeleteEntry(ref);
159 }
160
161
162 void
MoveEntry(const entry_ref & oldRef,const entry_ref & newRef)163 AnalyserDispatcher::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
170 void
LastEntry()171 AnalyserDispatcher::LastEntry()
172 {
173 for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
174 fFileAnalyserList.ItemAt(i)->LastEntry();
175 }
176
177
178 bool
AddAnalyser(FileAnalyser * analyser)179 AnalyserDispatcher::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
194 bool
RemoveAnalyser(const BString & name)195 AnalyserDispatcher::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
208 FileAnalyser*
_FindAnalyser(const BString & name)209 AnalyserDispatcher::_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
220 void
WriteAnalyserSettings()221 AnalyserDispatcher::WriteAnalyserSettings()
222 {
223 for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
224 fFileAnalyserList.ItemAt(i)->Settings()->WriteSettings();
225 }
226
227
228 void
SetSyncPosition(bigtime_t time)229 AnalyserDispatcher::SetSyncPosition(bigtime_t time)
230 {
231 for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
232 fFileAnalyserList.ItemAt(i)->Settings()->SetSyncPosition(time);
233 }
234
235
236 void
SetWatchingStart(bigtime_t time)237 AnalyserDispatcher::SetWatchingStart(bigtime_t time)
238 {
239 for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
240 fFileAnalyserList.ItemAt(i)->Settings()->SetWatchingStart(time);
241 }
242
243
244 void
SetWatchingPosition(bigtime_t time)245 AnalyserDispatcher::SetWatchingPosition(bigtime_t time)
246 {
247 for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
248 fFileAnalyserList.ItemAt(i)->Settings()->SetWatchingPosition(time);
249 }
250
251
VolumeWorker(VolumeWatcher * watcher)252 VolumeWorker::VolumeWorker(VolumeWatcher* watcher)
253 :
254 AnalyserDispatcher("VolumeWorker"),
255
256 fVolumeWatcher(watcher),
257 fBusy(0)
258 {
259
260 }
261
262
263 void
MessageReceived(BMessage * message)264 VolumeWorker::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
277 bool
IsBusy()278 VolumeWorker::IsBusy()
279 {
280 return (atomic_get(&fBusy) != 0);
281 }
282
283
284 void
_Work()285 VolumeWorker::_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
325 void
_SetBusy(bool busy)326 VolumeWorker::_SetBusy(bool busy)
327 {
328 if (busy)
329 atomic_set(&fBusy, 1);
330 else
331 atomic_set(&fBusy, 0);
332 }
333
334
VolumeWatcherBase(const BVolume & volume)335 VolumeWatcherBase::VolumeWatcherBase(const BVolume& volume)
336 :
337 fVolume(volume),
338 fEnabled(true),
339 fLastUpdated(0)
340 {
341 ReadSettings();
342 }
343
344
345 const char* kEnabledAttr = "Enabled";
346
347
348 bool
ReadSettings()349 VolumeWatcherBase::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
377 bool
WriteSettings()378 VolumeWatcherBase::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
SwapEntryRefVector()399 SwapEntryRefVector::SwapEntryRefVector()
400 {
401 fCurrentList = &fFirstList;
402 fNextList = &fSecondList;
403 }
404
405
406 EntryRefVector*
SwapList()407 SwapEntryRefVector::SwapList()
408 {
409 EntryRefVector* temp = fCurrentList;
410 fCurrentList = fNextList;
411 fNextList = temp;
412 return temp;
413 }
414
415
416 EntryRefVector*
CurrentList()417 SwapEntryRefVector::CurrentList()
418 {
419 return fCurrentList;
420 }
421
422
VolumeWatcher(const BVolume & volume)423 VolumeWatcher::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
~VolumeWatcher()439 VolumeWatcher::~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
449 bool
StartWatching()450 VolumeWatcher::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
470 void
Stop()471 VolumeWatcher::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
491 bool
AddAnalyser(FileAnalyser * analyser)492 VolumeWatcher::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
508 bool
RemoveAnalyser(const BString & name)509 VolumeWatcher::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
520 void
GetSecureEntries(list_collection & collection)521 VolumeWatcher::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
532 bool
FindEntryRef(ino_t node,dev_t device,entry_ref & entry)533 VolumeWatcher::FindEntryRef(ino_t node, dev_t device, entry_ref& entry)
534 {
535 return false;
536 }
537
538
539 void
_NewEntriesArrived()540 VolumeWatcher::_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