#include "VolumeManager.h"
#include <new>
#include <sys/stat.h>
#include <AutoDeleter.h>
#include <Entry.h>
#include <fs_info.h>
#include <fs_query.h>
#include <HashMap.h>
#include <NodeMonitor.h>
#include <Volume.h>
#include "ClientVolume.h"
#include "DebugSupport.h"
#include "Directory.h"
#include "Entry.h"
#include "FDManager.h"
#include "NodeHandle.h"
#include "Path.h"
#include "QueryDomain.h"
#include "Volume.h"
const bigtime_t kRecentEventLifeTime = 100000;
class VolumeManager::QueryHandler : public BHandler, public QueryListener,
public BReferenceable {
public:
QueryHandler(NodeMonitorListener* listener, QueryDomain* queryDomain,
QueryHandle* handle)
:
BHandler(),
QueryListener(),
BReferenceable(),
fListener(listener),
fQueryDomain(queryDomain),
fHandle(handle)
{
}
QueryDomain* GetQueryDomain() const
{
return fQueryDomain;
}
QueryHandle* GetQueryHandle() const
{
return fHandle;
}
virtual void MessageReceived(BMessage* message)
{
switch (message->what) {
case B_QUERY_UPDATE:
{
NodeMonitoringEvent* event = NULL;
int32 opcode;
if (message->FindInt32("opcode", &opcode) == B_OK) {
switch (opcode) {
case B_ENTRY_CREATED:
event = new(std::nothrow) EntryCreatedEvent;
break;
case B_ENTRY_REMOVED:
event = new(std::nothrow) EntryRemovedEvent;
break;
case B_ENTRY_MOVED:
event = new(std::nothrow) EntryMovedEvent;
break;
}
}
if (event) {
event->queryHandler = this;
AcquireReference();
if (event->Init(message) == B_OK)
fListener->ProcessNodeMonitoringEvent(event);
else
delete event;
}
break;
}
default:
BHandler::MessageReceived(message);
}
}
virtual void QueryHandleClosed(QueryHandle* handle)
{
BLooper* looper = Looper();
if (looper && looper->Lock()) {
looper->RemoveHandler(this);
looper->Unlock();
}
handle->SetQueryListener(NULL);
ReleaseReference();
}
private:
NodeMonitorListener* fListener;
QueryDomain* fQueryDomain;
QueryHandle* fHandle;
};
struct VolumeManager::VolumeMap : HashMap<HashKey32<dev_t>, Volume*> {
};
struct VolumeManager::ClientVolumeMap
: HashMap<HashKey32<int32>, ClientVolume*> {
};
extern "C" int _kset_fd_limit_(int num);
extern "C" int _kset_mon_limit_(int num);
struct VolumeManager::EntryCreatedEventMap
: HashMap<EntryRef, EntryCreatedEvent*> {
};
struct VolumeManager::EntryRemovedEventMap
: HashMap<EntryRef, EntryRemovedEvent*> {
};
struct EntryMovedEventKey : public EntryRef {
EntryMovedEventKey()
{
}
EntryMovedEventKey(dev_t volumeID, ino_t fromDirectory,
const char* fromName, ino_t toDirectory, const char* toName)
: EntryRef(volumeID, fromDirectory, fromName),
toDirectory(toDirectory),
toName(toName)
{
}
uint32 GetHashCode() const
{
uint32 hash = EntryRef::GetHashCode();
hash = 17 * hash + (uint32)(toDirectory >> 32);
hash = 17 * hash + (uint32)toDirectory;
hash = 17 * hash + string_hash(toName.GetString());
return hash;
}
bool operator==(const EntryMovedEventKey& other) const
{
return (*(const EntryRef*)this) == other
&& toDirectory == other.toDirectory
&& toName == other.toName;
}
bool operator!=(const EntryMovedEventKey& other) const
{
return !(*this == other);
}
ino_t toDirectory;
HashString toName;
};
struct VolumeManager::EntryMovedEventMap : HashMap<EntryRef, EntryMovedEvent*> {
};
struct VolumeManager::NodeStatChangedEventMap
: HashMap<NodeRef, StatChangedEvent*> {
};
typedef EntryRef AttributeRef;
struct VolumeManager::NodeAttributeChangedEventMap
: HashMap<AttributeRef, AttributeChangedEvent*> {
};
VolumeManager::VolumeManager()
: fLock("volume manager"),
fVolumes(NULL),
fRootVolume(NULL),
fClientVolumes(NULL),
fNodeMonitor(NULL),
fNodeMonitoringProcessor(-1),
fNodeMonitoringEvents(),
fRecentNodeMonitoringEvents(),
fEntryCreatedEvents(NULL),
fEntryRemovedEvents(NULL),
fEntryMovedEvents(NULL),
fNodeStatChangedEvents(NULL),
fNodeAttributeChangedEvents(NULL),
fRevision(0),
fTerminating(false)
{
}
VolumeManager::~VolumeManager()
{
fTerminating = true;
if (fNodeMonitor && fNodeMonitor->Lock())
fNodeMonitor->Quit();
fNodeMonitoringEvents.Close(true);
if (fNodeMonitoringProcessor >= 0) {
int32 result;
wait_for_thread(fNodeMonitoringProcessor, &result);
}
for (EntryCreatedEventMap::Iterator it = fEntryCreatedEvents->GetIterator();
it.HasNext();) {
it.Next().value->ReleaseReference();
}
delete fEntryCreatedEvents;
for (EntryRemovedEventMap::Iterator it = fEntryRemovedEvents->GetIterator();
it.HasNext();) {
it.Next().value->ReleaseReference();
}
delete fEntryRemovedEvents;
for (EntryMovedEventMap::Iterator it = fEntryMovedEvents->GetIterator();
it.HasNext();) {
it.Next().value->ReleaseReference();
}
delete fEntryMovedEvents;
for (NodeStatChangedEventMap::Iterator it
= fNodeStatChangedEvents->GetIterator();
it.HasNext();) {
it.Next().value->ReleaseReference();
}
delete fNodeStatChangedEvents;
for (NodeAttributeChangedEventMap::Iterator it
= fNodeAttributeChangedEvents->GetIterator();
it.HasNext();) {
it.Next().value->ReleaseReference();
}
delete fNodeAttributeChangedEvents;
delete fClientVolumes;
for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) {
Volume* volume = it.Next().value;
delete volume;
}
delete fVolumes;
}
status_t
VolumeManager::Init()
{
status_t error = fNodeMonitoringEvents.InitCheck();
if (error != B_OK)
return error;
fEntryCreatedEvents = new(std::nothrow) EntryCreatedEventMap;
if (!fEntryCreatedEvents)
return B_NO_MEMORY;
fEntryRemovedEvents = new(std::nothrow) EntryRemovedEventMap;
if (!fEntryRemovedEvents)
return B_NO_MEMORY;
fEntryMovedEvents = new(std::nothrow) EntryMovedEventMap;
if (!fEntryMovedEvents)
return B_NO_MEMORY;
fNodeStatChangedEvents = new(std::nothrow) NodeStatChangedEventMap;
if (!fNodeStatChangedEvents)
return B_NO_MEMORY;
fNodeAttributeChangedEvents = new(std::nothrow) NodeAttributeChangedEventMap;
if (!fNodeAttributeChangedEvents)
return B_NO_MEMORY;
fNodeMonitor = new(std::nothrow) NodeMonitor(this);
if (!fNodeMonitor)
return B_NO_MEMORY;
fVolumes = new(std::nothrow) VolumeMap;
if (!fVolumes)
return B_NO_MEMORY;
if (fVolumes->InitCheck() != B_OK)
return fVolumes->InitCheck();
fClientVolumes = new(std::nothrow) ClientVolumeMap;
if (!fClientVolumes)
return B_NO_MEMORY;
if (fClientVolumes->InitCheck() != B_OK)
return fClientVolumes->InitCheck();
thread_id monitorThread = fNodeMonitor->Run();
if (monitorThread < 0) {
delete fNodeMonitor;
fNodeMonitor = NULL;
return monitorThread;
}
int32 cookie = 0;
dev_t volumeID;
while ((volumeID = next_dev(&cookie)) >= 0)
_AddVolume(volumeID);
volumeID = dev_for_path("/");
if (volumeID < 0)
return volumeID;
fRootVolume = GetVolume(volumeID, true);
if (!fRootVolume)
return B_ERROR;
fNodeMonitoringProcessor = spawn_thread(&_NodeMonitoringProcessorEntry,
"node monitoring processor", B_NORMAL_PRIORITY, this);
if (fNodeMonitoringProcessor < 0)
return fNodeMonitoringProcessor;
resume_thread(fNodeMonitoringProcessor);
return B_OK;
}
Volume*
VolumeManager::GetRootVolume() const
{
return fRootVolume;
}
status_t
VolumeManager::AddClientVolume(ClientVolume* clientVolume)
{
if (!clientVolume)
return B_BAD_VALUE;
return fClientVolumes->Put(clientVolume->GetID(), clientVolume);
}
void
VolumeManager::RemoveClientVolume(ClientVolume* clientVolume)
{
if (!clientVolume)
return;
fClientVolumes->Remove(clientVolume->GetID());
}
status_t
VolumeManager::CreateDefault()
{
if (sManager)
return B_OK;
VolumeManager* manager = new(std::nothrow) VolumeManager;
if (!manager)
return B_NO_MEMORY;
status_t error = manager->Init();
if (error != B_OK) {
delete manager;
return error;
}
sManager = manager;
return B_OK;
}
void
VolumeManager::DeleteDefault()
{
if (sManager) {
delete sManager;
sManager = NULL;
}
}
VolumeManager*
VolumeManager::GetDefault()
{
return sManager;
}
bool
VolumeManager::Lock()
{
bool alreadyLocked = fLock.IsLocked();
bool success = fLock.Lock();
if (success && !alreadyLocked)
fRevision++;
return success;
}
void
VolumeManager::Unlock()
{
return fLock.Unlock();
}
int64
VolumeManager::GetRevision() const
{
return fRevision;
}
Volume*
VolumeManager::GetVolume(dev_t volumeID, bool add)
{
Volume* volume = fVolumes->Get(volumeID);
if (!volume && add)
_AddVolume(volumeID, &volume);
return volume;
}
status_t
VolumeManager::AddNode(Node* node)
{
if (!node || !node->GetVolume())
return B_BAD_VALUE;
status_t error = node->GetVolume()->AddNode(node);
if (error == B_OK)
fNodeMonitor->StartWatching(node->GetNodeRef());
return error;
}
void
VolumeManager::RemoveNode(Node* node)
{
if (!node)
return;
if (Directory* directory = dynamic_cast<Directory*>(node)) {
while (Entry* entry = directory->GetFirstEntry()) {
RemoveEntry(entry);
delete entry;
}
}
while (Entry* entry = node->GetFirstReferringEntry())
RemoveEntry(entry);
if (node->GetVolume())
node->GetVolume()->RemoveNode(node);
fNodeMonitor->StopWatching(node->GetNodeRef());
}
Node*
VolumeManager::GetNode(dev_t volumeID, ino_t nodeID)
{
if (Volume* volume = GetVolume(volumeID))
return volume->GetNode(nodeID);
return NULL;
}
status_t
VolumeManager::LoadNode(const struct stat& st, Node** _node)
{
Node* node = GetNode(st.st_dev, st.st_ino);
if (!node) {
Volume* volume = GetVolume(st.st_dev, true);
if (!volume)
return B_BAD_VALUE;
if (S_ISDIR(st.st_mode))
node = new(std::nothrow) Directory(volume, st);
else
node = new(std::nothrow) Node(volume, st);
if (!node)
return B_NO_MEMORY;
status_t error = AddNode(node);
if (error != B_OK) {
delete node;
return error;
}
}
if (_node)
*_node = node;
return B_OK;
}
Directory*
VolumeManager::GetDirectory(dev_t volumeID, ino_t nodeID)
{
return dynamic_cast<Directory*>(GetNode(volumeID, nodeID));
}
Directory*
VolumeManager::GetRootDirectory() const
{
return (fRootVolume ? fRootVolume->GetRootDirectory() : NULL);
}
Directory*
VolumeManager::GetParentDirectory(Directory* directory)
{
if (!directory)
return NULL;
Entry* parentEntry;
if (LoadEntry(directory->GetVolumeID(), directory->GetID(), "..", true,
&parentEntry) != B_OK) {
return NULL;
}
return dynamic_cast<Directory*>(parentEntry->GetNode());
}
status_t
VolumeManager::LoadDirectory(dev_t volumeID, ino_t directoryID,
Directory** _directory)
{
Node* node = GetNode(volumeID, directoryID);
bool newNode = false;
if (!node) {
NoAllocEntryRef entryRef(volumeID, directoryID, ".");
struct stat st;
BEntry bEntry;
status_t error = FDManager::SetEntry(&bEntry, &entryRef);
if (error == B_OK)
error = bEntry.GetStat(&st);
if (error != B_OK)
return error;
error = LoadNode(st, &node);
if (error != B_OK)
return error;
newNode = true;
}
Directory* directory = dynamic_cast<Directory*>(node);
if (!directory)
return B_NOT_A_DIRECTORY;
if (newNode)
CompletePathToRoot(directory);
if (_directory)
*_directory = directory;
return B_OK;
}
status_t
VolumeManager::AddEntry(Entry* entry)
{
if (!entry || !entry->GetVolume() || !entry->GetDirectory()
|| ! entry->GetNode()) {
return B_BAD_VALUE;
}
status_t error = entry->GetVolume()->AddEntry(entry);
if (error != B_OK)
return error;
entry->GetDirectory()->AddEntry(entry);
entry->GetNode()->AddReferringEntry(entry);
return B_OK;
}
void
VolumeManager::RemoveEntry(Entry* entry)
{
if (entry) {
if (entry->GetVolume())
entry->GetVolume()->RemoveEntry(entry);
entry->GetDirectory()->RemoveEntry(entry);
entry->GetNode()->RemoveReferringEntry(entry);
}
}
void
VolumeManager::DeleteEntry(Entry* entry, bool keepNode)
{
if (!entry)
return;
Node* node = entry->GetNode();
RemoveEntry(entry);
delete entry;
if (!keepNode && !node->GetActualReferringEntry()) {
RemoveNode(node);
if (node != node->GetVolume()->GetRootDirectory())
delete node;
}
}
Entry*
VolumeManager::GetEntry(dev_t volumeID, ino_t directoryID, const char* name)
{
if (Volume* volume = GetVolume(volumeID))
return volume->GetEntry(directoryID, name);
return NULL;
}
Entry*
VolumeManager::GetEntry(const entry_ref& ref)
{
return GetEntry(ref.device, ref.directory, ref.name);
}
status_t
VolumeManager::LoadEntry(dev_t volumeID, ino_t directoryID, const char* name,
bool loadDir, Entry** _entry)
{
Entry* entry = GetEntry(volumeID, directoryID, name);
if (!entry) {
PRINT("VolumeManager::LoadEntry(%" B_PRIdDEV ", "
"%" B_PRIdINO ", `%s')\n", volumeID, directoryID, name);
Volume* volume = GetVolume(volumeID, true);
if (!volume)
return B_BAD_VALUE;
status_t error = B_OK;
Directory* directory = GetDirectory(volumeID, directoryID);
if (!directory) {
if (!loadDir)
return B_ENTRY_NOT_FOUND;
error = LoadDirectory(volumeID, directoryID, &directory);
if (error != B_OK)
return error;
}
NoAllocEntryRef entryRef(volumeID, directoryID, name);
struct stat st;
BNode bNode;
error = bNode.SetTo(&entryRef);
if (error == B_OK)
error = bNode.GetStat(&st);
if (error != B_OK)
return error;
Node* node;
error = LoadNode(st, &node);
if (error != B_OK)
return error;
entry = new(std::nothrow) Entry(volume, directory, name, node);
if (!entry)
return B_NO_MEMORY;
error = AddEntry(entry);
if (error != B_OK) {
delete entry;
return error;
}
}
if (_entry)
*_entry = entry;
return B_OK;
}
status_t
VolumeManager::OpenQuery(QueryDomain* queryDomain, const char* queryString,
uint32 flags, port_id remotePort, int32 remoteToken, QueryHandle** handle)
{
if (!queryDomain || !queryString || !handle)
return B_BAD_VALUE;
bool liveQuery = (flags & B_LIVE_QUERY);
PRINT("VolumeManager::OpenQuery(%p, \"%s\", 0x%" B_PRIx32 ", "
"%" B_PRId32 ", %" B_PRId32 ")\n",
queryDomain, queryString, flags, remotePort, remoteToken);
QueryHandle* queryHandle = new(std::nothrow) QueryHandle(remotePort,
remoteToken);
if (!queryHandle)
return B_NO_MEMORY;
ObjectDeleter<QueryHandle> handleDeleter(queryHandle);
QueryHandler* queryHandler = NULL;
if (liveQuery) {
queryHandler = new(std::nothrow) QueryHandler(this, queryDomain,
queryHandle);
if (!queryHandler)
return B_NO_MEMORY;
fNodeMonitor->Lock();
fNodeMonitor->AddHandler(queryHandler);
fNodeMonitor->Unlock();
queryHandle->SetQueryListener(queryHandler);
}
for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) {
Volume* volume = it.Next().value;
if (!volume->KnowsQuery())
continue;
if (!queryDomain->QueryDomainIntersectsWith(volume))
continue;
PRINT("VolumeManager::OpenQuery(): adding Query for volume "
"%" B_PRIdDEV "\n", volume->GetID());
BVolume bVolume(volume->GetID());
Query* query = new(std::nothrow) Query;
if (!query)
return B_NO_MEMORY;
ObjectDeleter<Query> queryDeleter(query);
status_t error = query->SetVolume(&bVolume);
if (error != B_OK)
return error;
error = query->SetPredicate(queryString);
if (error != B_OK)
return error;
if (liveQuery) {
error = query->SetTarget(queryHandler);
if (error != B_OK)
return error;
}
error = query->Fetch();
if (error != B_OK)
return error;
queryHandle->AddQuery(query);
queryDeleter.Detach();
}
*handle = queryHandle;
handleDeleter.Detach();
return B_OK;
}
status_t
VolumeManager::CompletePathToRoot(Directory* directory)
{
if (!directory)
return B_BAD_VALUE;
while (directory != GetRootDirectory()) {
if (directory->GetActualReferringEntry())
return B_OK;
BEntry bEntry;
entry_ref entryRef(directory->GetVolumeID(), directory->GetID(), ".");
status_t error = FDManager::SetEntry(&bEntry, &entryRef);
if (error == B_OK)
error = bEntry.GetRef(&entryRef);
if (error != B_OK)
return error;
if (GetEntry(entryRef))
return B_OK;
Entry* entry;
error = LoadEntry(entryRef.device, entryRef.directory, entryRef.name,
true, &entry);
if (error != B_OK)
return error;
directory = entry->GetDirectory();
}
return B_OK;
}
status_t
VolumeManager::GetPath(Entry* entry, Path* path)
{
status_t error = GetPath(entry->GetDirectory(), path);
if (error != B_OK)
return error;
return path->Append(entry->GetName());
}
status_t
VolumeManager::GetPath(Node* node, Path* path)
{
if (node == GetRootDirectory())
return path->SetTo("/");
Entry* entry = node->GetActualReferringEntry();
if (!entry) {
if (Directory* directory = dynamic_cast<Directory*>(node)) {
CompletePathToRoot(directory);
entry = node->GetActualReferringEntry();
}
if (!entry)
return B_ERROR;
}
return GetPath(entry, path);
}
bool
VolumeManager::DirectoryContains(Directory* directory, Entry* entry)
{
if (!directory || !entry)
return false;
return DirectoryContains(directory, entry->GetDirectory(), true);
}
bool
VolumeManager::DirectoryContains(Directory* directory, Directory* descendant,
bool reflexive)
{
if (!directory || !descendant)
return false;
if (directory == descendant)
return reflexive;
Directory* rootDir = GetRootDirectory();
if (directory == rootDir)
return true;
while (descendant != rootDir) {
descendant = GetParentDirectory(descendant);
if (!descendant)
return false;
if (descendant == directory)
return true;
}
return false;
}
bool
VolumeManager::DirectoryContains(Directory* directory, Node* descendant,
bool reflexive)
{
if (!directory || !descendant)
return false;
if (Directory* dir = dynamic_cast<Directory*>(descendant))
return DirectoryContains(directory, dir, reflexive);
for (Entry* entry = descendant->GetFirstReferringEntry();
entry;
entry = descendant->GetNextReferringEntry(entry)) {
if (DirectoryContains(directory, entry))
return true;
}
return false;
}
void
VolumeManager::ProcessNodeMonitoringEvent(NodeMonitoringEvent* event)
{
if (fNodeMonitoringEvents.Push(event) != B_OK)
delete event;
}
status_t
VolumeManager::_AddVolume(dev_t volumeID, Volume** _volume)
{
if (GetVolume(volumeID))
return B_OK;
Volume* volume = new(std::nothrow) Volume(volumeID);
if (!volume)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<Volume> volumeDeleter(volume);
status_t error = volume->Init();
if (error != B_OK)
RETURN_ERROR(error);
error = fVolumes->Put(volumeID, volume);
if (error != B_OK)
RETURN_ERROR(error);
error = AddNode(volume->GetRootDirectory());
if (error != B_OK) {
fVolumes->Remove(volumeID);
RETURN_ERROR(error);
}
CompletePathToRoot(volume->GetRootDirectory());
volumeDeleter.Detach();
if (_volume)
*_volume = volume;
return B_OK;
}
void
VolumeManager::_EntryCreated(EntryCreatedEvent* event)
{
Directory* directory = GetDirectory(event->volumeID, event->directoryID);
if (!directory)
return;
bool notify = true;
NoAllocEntryRef ref(event->volumeID, event->directoryID,
event->name.GetString());
EntryCreatedEvent* oldEvent = fEntryCreatedEvents->Get(ref);
if (oldEvent) {
fEntryCreatedEvents->Remove(ref);
fRecentNodeMonitoringEvents.Remove(oldEvent);
notify = !_IsRecentEvent(oldEvent);
oldEvent->ReleaseReference();
}
if (fEntryCreatedEvents->Put(ref, event) == B_OK) {
fRecentNodeMonitoringEvents.Insert(event);
event->AcquireReference();
}
if (directory->IsComplete() || directory->HasDirIterators()) {
Entry* entry;
LoadEntry(ref.device, ref.directory, ref.name, false, &entry);
}
if (notify) {
for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
it.HasNext();) {
ClientVolume* clientVolume = it.Next().value;
if (DirectoryContains(clientVolume->GetRootDirectory(), directory,
true)) {
clientVolume->ProcessNodeMonitoringEvent(event);
}
}
}
}
void
VolumeManager::_EntryRemoved(EntryRemovedEvent* event, bool keepNode)
{
Node* node = GetNode(event->nodeVolumeID, event->nodeID);
Directory* directory = GetDirectory(event->volumeID, event->directoryID);
if (!directory)
return;
Entry* entry = NULL;
if (node) {
if (event->name.GetLength() == 0) {
for (entry = node->GetFirstReferringEntry();
entry;
entry = node->GetNextReferringEntry(entry)) {
if (!entry->Exists()) {
event->name.SetTo(entry->GetName());
break;
}
}
} else {
entry = GetEntry(directory->GetVolumeID(), directory->GetID(),
event->name.GetString());
}
}
bool notify = true;
NoAllocEntryRef ref(event->volumeID, event->directoryID,
event->name.GetString());
EntryRemovedEvent* oldEvent = fEntryRemovedEvents->Get(ref);
if (oldEvent) {
fEntryRemovedEvents->Remove(ref);
fRecentNodeMonitoringEvents.Remove(oldEvent);
notify = !_IsRecentEvent(oldEvent);
oldEvent->ReleaseReference();
}
if (fEntryRemovedEvents->Put(ref, event) == B_OK) {
fRecentNodeMonitoringEvents.Insert(event);
event->AcquireReference();
}
if (entry) {
RemoveEntry(entry);
delete entry;
}
if (node && !keepNode && !node->GetActualReferringEntry()) {
RemoveNode(node);
if (node != node->GetVolume()->GetRootDirectory())
delete node;
}
if (notify) {
NodeRef nodeRef(event->nodeVolumeID, event->nodeID);
for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
it.HasNext();) {
ClientVolume* clientVolume = it.Next().value;
Directory* rootDir = clientVolume->GetRootDirectory();
if (DirectoryContains(rootDir, directory, true)
|| clientVolume->GetRootNodeRef() == nodeRef) {
clientVolume->ProcessNodeMonitoringEvent(event);
}
}
}
}
void
VolumeManager::_EntryMoved(EntryMovedEvent* event)
{
_CheckVolumeRootMoved(event);
Directory* fromDirectory
= GetDirectory(event->volumeID, event->fromDirectoryID);
Directory* toDirectory
= GetDirectory(event->volumeID, event->toDirectoryID);
Node* node = GetNode(event->nodeVolumeID, event->nodeID);
if (!fromDirectory && !toDirectory)
return;
Entry* oldEntry = NULL;
if (node) {
if (event->fromName.GetLength() == 0) {
for (oldEntry = node->GetFirstReferringEntry();
oldEntry;
oldEntry = node->GetNextReferringEntry(oldEntry)) {
if (!oldEntry->Exists()) {
event->fromName.SetTo(oldEntry->GetName());
break;
}
}
} else {
oldEntry = GetEntry(event->volumeID, event->fromDirectoryID,
event->fromName.GetString());
}
}
bool notify = true;
if (event->fromName.GetLength() > 0) {
EntryMovedEventKey key(event->volumeID, event->fromDirectoryID,
event->fromName.GetString(), event->toDirectoryID,
event->toName.GetString());
EntryMovedEvent* oldEvent = fEntryMovedEvents->Get(key);
if (oldEvent) {
fEntryMovedEvents->Remove(key);
fRecentNodeMonitoringEvents.Remove(oldEvent);
notify = !_IsRecentEvent(oldEvent);
oldEvent->ReleaseReference();
}
if (fEntryMovedEvents->Put(key, event) == B_OK) {
fRecentNodeMonitoringEvents.Insert(event);
event->AcquireReference();
}
}
if (oldEntry) {
RemoveEntry(oldEntry);
delete oldEntry;
}
if (toDirectory
&& (toDirectory->IsComplete() || toDirectory->HasDirIterators()
|| (node && node == node->GetVolume()->GetRootDirectory()))) {
Entry* newEntry;
LoadEntry(event->volumeID, event->toDirectoryID,
event->toName.GetString(), false, &newEntry);
}
if (node && !node->GetActualReferringEntry()) {
RemoveNode(node);
if (node != node->GetVolume()->GetRootDirectory())
delete node;
}
if (notify) {
for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
it.HasNext();) {
ClientVolume* clientVolume = it.Next().value;
Directory* rootDir = clientVolume->GetRootDirectory();
bool containsFrom = DirectoryContains(rootDir, fromDirectory, true);
bool containsTo = DirectoryContains(rootDir, toDirectory, true);
if (containsFrom) {
if (containsTo) {
clientVolume->ProcessNodeMonitoringEvent(event);
} else {
EntryRemovedEvent *removedEvent
= new(std::nothrow) EntryRemovedEvent;
if (!removedEvent)
continue;
removedEvent->opcode = B_ENTRY_REMOVED;
removedEvent->time = event->time;
removedEvent->volumeID = event->volumeID;
removedEvent->directoryID = event->fromDirectoryID;
removedEvent->nodeVolumeID = event->nodeVolumeID;
removedEvent->nodeID = event->nodeID;
if (event->fromName.GetLength() > 0)
removedEvent->name = event->fromName;
clientVolume->ProcessNodeMonitoringEvent(removedEvent);
removedEvent->ReleaseReference();
}
} else if (containsTo) {
EntryCreatedEvent *createdEvent
= new(std::nothrow) EntryCreatedEvent;
if (!createdEvent)
continue;
createdEvent->opcode = B_ENTRY_CREATED;
createdEvent->time = event->time;
createdEvent->volumeID = event->volumeID;
createdEvent->directoryID = event->toDirectoryID;
createdEvent->name = event->toName;
clientVolume->ProcessNodeMonitoringEvent(createdEvent);
createdEvent->ReleaseReference();
}
}
}
}
void
VolumeManager::_NodeStatChanged(StatChangedEvent* event)
{
Node* node = GetNode(event->volumeID, event->nodeID);
if (!node)
return;
bool notify = true;
NodeRef ref(event->volumeID, event->nodeID);
StatChangedEvent* oldEvent = fNodeStatChangedEvents->Get(ref);
if (oldEvent) {
fNodeStatChangedEvents->Remove(ref);
fRecentNodeMonitoringEvents.Remove(oldEvent);
notify = !_IsRecentEvent(oldEvent);
oldEvent->ReleaseReference();
}
if (fNodeStatChangedEvents->Put(ref, event) == B_OK) {
fRecentNodeMonitoringEvents.Insert(event);
event->AcquireReference();
}
if (notify) {
node->UpdateStat();
for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
it.HasNext();) {
ClientVolume* clientVolume = it.Next().value;
if (DirectoryContains(clientVolume->GetRootDirectory(), node, true))
clientVolume->ProcessNodeMonitoringEvent(event);
}
}
}
void
VolumeManager::_NodeAttributeChanged(AttributeChangedEvent* event)
{
Node* node = GetNode(event->volumeID, event->nodeID);
if (!node)
return;
bool notify = true;
AttributeRef ref(event->volumeID, event->nodeID,
event->attribute.GetString());
AttributeChangedEvent* oldEvent = fNodeAttributeChangedEvents->Get(ref);
if (oldEvent) {
fNodeAttributeChangedEvents->Remove(ref);
fRecentNodeMonitoringEvents.Remove(oldEvent);
notify = !_IsRecentEvent(oldEvent);
oldEvent->ReleaseReference();
}
if (fNodeAttributeChangedEvents->Put(ref, event) == B_OK) {
fRecentNodeMonitoringEvents.Insert(event);
event->AcquireReference();
}
if (notify) {
for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
it.HasNext();) {
ClientVolume* clientVolume = it.Next().value;
if (DirectoryContains(clientVolume->GetRootDirectory(), node, true))
clientVolume->ProcessNodeMonitoringEvent(event);
}
}
}
void
VolumeManager::_VolumeMounted(VolumeMountedEvent* event)
{
entry_ref rootRef;
bool rootRefInitialized = false;
Directory* coveredDirectory = GetDirectory(event->volumeID,
event->directoryID);
if (coveredDirectory) {
if (Entry* entry = coveredDirectory->GetActualReferringEntry()) {
rootRef = entry->GetEntryRef();
rootRefInitialized = true;
EntryRemovedEvent* event;
if (_GenerateEntryRemovedEvent(entry, system_time(),
&event) == B_OK) {
_EntryRemoved(event, true);
event->ReleaseReference();
} else {
RemoveEntry(entry);
delete entry;
}
}
}
_AddVolume(event->newVolumeID);
if (rootRefInitialized)
_GenerateEntryCreatedEvent(rootRef, event->time);
}
void
VolumeManager::_VolumeUnmounted(VolumeUnmountedEvent* event)
{
Volume* volume = GetVolume(event->volumeID);
if (!volume)
return;
entry_ref rootRef;
bool rootRefInitialized = false;
if (Directory* rootDir = volume->GetRootDirectory()) {
if (Entry* entry = rootDir->GetActualReferringEntry()) {
rootRef = entry->GetEntryRef();
rootRefInitialized = true;
}
Entry* entry = rootDir->GetFirstReferringEntry();
while (entry) {
Entry* nextEntry = rootDir->GetNextReferringEntry(entry);
if (entry->IsActualEntry()) {
EntryRemovedEvent* removedEvent;
if (_GenerateEntryRemovedEvent(entry, event->time,
&removedEvent) == B_OK) {
_EntryRemoved(removedEvent, true);
removedEvent->ReleaseReference();
} else {
RemoveEntry(entry);
delete entry;
}
}
entry = nextEntry;
}
}
while (Entry* entry = volume->GetFirstEntry()) {
bool remove = true;
if (entry->IsActualEntry()) {
if (_GenerateEntryRemovedEvent(entry, event->time) != B_OK)
remove = false;
}
if (remove) {
RemoveEntry(entry);
delete entry;
}
}
while (Node* node = volume->GetFirstNode()) {
RemoveNode(node);
if (node != volume->GetRootDirectory())
delete node;
}
fVolumes->Remove(volume->GetID());
delete volume;
if (rootRefInitialized)
_GenerateEntryCreatedEvent(rootRef, event->time);
}
void
VolumeManager::_QueryEntryCreated(EntryCreatedEvent* event)
{
QueryHandler* queryHandler
= dynamic_cast<QueryHandler*>(event->queryHandler);
if (!queryHandler)
return;
Entry* entry = NULL;
status_t error = LoadEntry(event->volumeID, event->directoryID,
event->name.GetString(), true, &entry);
if (error != B_OK)
return;
if (!queryHandler->LockLooper())
return;
QueryHandle* queryHandle = queryHandler->GetQueryHandle();
event->remotePort = queryHandle->GetRemotePort();
event->remoteToken = queryHandle->GetRemoteToken();
queryHandler->UnlockLooper();
queryHandler->GetQueryDomain()->ProcessQueryEvent(event);
}
void
VolumeManager::_QueryEntryRemoved(EntryRemovedEvent* event)
{
QueryHandler* queryHandler
= dynamic_cast<QueryHandler*>(event->queryHandler);
if (!queryHandler)
return;
Directory* directory = NULL;
status_t error = LoadDirectory(event->volumeID, event->directoryID,
&directory);
if (error != B_OK)
return;
if (!queryHandler->LockLooper())
return;
QueryHandle* queryHandle = queryHandler->GetQueryHandle();
event->remotePort = queryHandle->GetRemotePort();
event->remoteToken = queryHandle->GetRemoteToken();
queryHandler->UnlockLooper();
queryHandler->GetQueryDomain()->ProcessQueryEvent(event);
}
void
VolumeManager::_QueryEntryMoved(EntryMovedEvent* event)
{
EntryRemovedEvent* removedEvent = new(std::nothrow) EntryRemovedEvent;
EntryCreatedEvent* createdEvent = new(std::nothrow) EntryCreatedEvent;
if (!removedEvent || !createdEvent) {
delete removedEvent;
delete createdEvent;
return;
}
removedEvent->opcode = B_ENTRY_REMOVED;
removedEvent->time = event->time;
removedEvent->queryHandler = event->queryHandler;
removedEvent->queryHandler->AcquireReference();
removedEvent->volumeID = event->volumeID;
removedEvent->directoryID = event->fromDirectoryID;
removedEvent->nodeVolumeID = event->volumeID;
removedEvent->nodeID = event->nodeID;
removedEvent->name = event->fromName;
createdEvent->opcode = B_ENTRY_CREATED;
createdEvent->time = event->time;
createdEvent->queryHandler = event->queryHandler;
createdEvent->queryHandler->AcquireReference();
createdEvent->volumeID = event->volumeID;
createdEvent->directoryID = event->toDirectoryID;
createdEvent->nodeID = event->nodeID;
createdEvent->name = event->toName;
_QueryEntryRemoved(removedEvent);
removedEvent->ReleaseReference();
_QueryEntryCreated(createdEvent);
createdEvent->ReleaseReference();
}
bool
VolumeManager::_IsRecentEvent(NodeMonitoringEvent* event) const
{
return (event && system_time() < event->time + kRecentEventLifeTime);
}
status_t
VolumeManager::_GenerateEntryCreatedEvent(const entry_ref& ref, bigtime_t time,
EntryCreatedEvent** _event)
{
Entry* entry;
status_t error = LoadEntry(ref.device, ref.directory, ref.name, true,
&entry);
if (error != B_OK)
return error;
EntryCreatedEvent* event = new(std::nothrow) EntryCreatedEvent;
if (!event)
return B_NO_MEMORY;
event->opcode = B_ENTRY_CREATED;
event->time = time;
event->volumeID = entry->GetVolumeID();
event->directoryID = entry->GetDirectoryID();
event->nodeID = entry->GetNode()->GetID();
event->name.SetTo(entry->GetName());
if (_event) {
*_event = event;
} else {
_EntryCreated(event);
event->ReleaseReference();
}
return B_OK;
}
status_t
VolumeManager::_GenerateEntryRemovedEvent(Entry* entry, bigtime_t time,
EntryRemovedEvent** _event)
{
if (!entry)
return B_BAD_VALUE;
EntryRemovedEvent* event = new(std::nothrow) EntryRemovedEvent;
if (!event)
return B_NO_MEMORY;
event->opcode = B_ENTRY_REMOVED;
event->time = time;
event->volumeID = entry->GetVolumeID();
event->directoryID = entry->GetDirectoryID();
event->nodeVolumeID = entry->GetNode()->GetVolumeID();
event->nodeID = entry->GetNode()->GetID();
event->name.SetTo(entry->GetName());
if (_event) {
*_event = event;
} else {
_EntryRemoved(event, false);
event->ReleaseReference();
}
return B_OK;
}
void
VolumeManager::_CheckVolumeRootMoved(EntryMovedEvent* event)
{
NoAllocEntryRef ref(event->volumeID, event->toDirectoryID,
event->toName.GetString());
BEntry entry;
struct stat st;
if (FDManager::SetEntry(&entry, &ref) == B_OK
&& entry.GetStat(&st) == B_OK) {
event->nodeVolumeID = st.st_dev;
event->nodeID = st.st_ino;
if (Volume* volume = GetVolume(st.st_dev)) {
if (volume->GetRootID() == st.st_ino) {
PRINT("Mount point for volume %" B_PRIdDEV " renamed\n",
volume->GetID());
}
}
}
}
int32
VolumeManager::_NodeMonitoringProcessorEntry(void* data)
{
return ((VolumeManager*)data)->_NodeMonitoringProcessor();
}
int32
VolumeManager::_NodeMonitoringProcessor()
{
do {
NodeMonitoringEvent* event = NULL;
status_t error = fNodeMonitoringEvents.Pop(&event);
VolumeManagerLocker managerLocker;
while (error == B_OK) {
if (event->queryHandler) {
switch (event->opcode) {
case B_ENTRY_CREATED:
_QueryEntryCreated(
dynamic_cast<EntryCreatedEvent*>(event));
break;
case B_ENTRY_REMOVED:
_QueryEntryRemoved(
dynamic_cast<EntryRemovedEvent*>(event));
break;
case B_ENTRY_MOVED:
_QueryEntryMoved(dynamic_cast<EntryMovedEvent*>(event));
break;
}
} else {
switch (event->opcode) {
case B_ENTRY_CREATED:
_EntryCreated(dynamic_cast<EntryCreatedEvent*>(event));
break;
case B_ENTRY_REMOVED:
_EntryRemoved(dynamic_cast<EntryRemovedEvent*>(event),
false);
break;
case B_ENTRY_MOVED:
_EntryMoved(dynamic_cast<EntryMovedEvent*>(event));
break;
case B_STAT_CHANGED:
_NodeStatChanged(
dynamic_cast<StatChangedEvent*>(event));
break;
case B_ATTR_CHANGED:
_NodeAttributeChanged(
dynamic_cast<AttributeChangedEvent*>(event));
break;
case B_DEVICE_MOUNTED:
_VolumeMounted(dynamic_cast<VolumeMountedEvent*>(event));
break;
case B_DEVICE_UNMOUNTED:
_VolumeUnmounted(
dynamic_cast<VolumeUnmountedEvent*>(event));
break;
}
}
event->ReleaseReference();
error = fNodeMonitoringEvents.Pop(&event, 0);
}
} while (!fTerminating);
return 0;
}
VolumeManager* VolumeManager::sManager = NULL;