xref: /haiku/src/servers/index/VolumeWatcher.cpp (revision ca8ed5ea660fb6275799a3b7f138b201c41a667b)
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