diff --git a/headers/private/storage/AddOnMonitorHandler.h b/headers/private/storage/AddOnMonitorHandler.h index 52b2860e16..b7efc675c1 100644 --- a/headers/private/storage/AddOnMonitorHandler.h +++ b/headers/private/storage/AddOnMonitorHandler.h @@ -1,10 +1,11 @@ /* - * Copyright 2004-2008, Haiku, Inc. All rights reserved. + * Copyright 2004-2010, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _ADD_ON_MONITOR_HANDLER_H #define _ADD_ON_MONITOR_HANDLER_H + #include #include "NodeMonitorHandler.h" @@ -13,55 +14,112 @@ namespace BPrivate { namespace Storage { + struct add_on_entry_info { char name[B_FILE_NAME_LENGTH]; node_ref nref; node_ref dir_nref; + node_ref addon_nref; }; -struct add_on_directory_info { - node_ref nref; - std::list entries; -}; class AddOnMonitorHandler : public NodeMonitorHandler { public: - AddOnMonitorHandler(const char* name = NULL); - virtual ~AddOnMonitorHandler(); + AddOnMonitorHandler(const char* name = NULL); + virtual ~AddOnMonitorHandler(); - virtual void MessageReceived(BMessage* msg); + virtual void MessageReceived(BMessage* message); - // supply the add-on directories here, in the order you want them checked - virtual status_t AddDirectory(const node_ref* nref); + // Supply the add-on directories here, in the order you want them checked. + // Add-ons in directories added earlier will shadow add-ons in directories + // added later, if they share the same file name. If an add-on is removed + // from or renamed in a directory and it has previously shadowed another + // add-on, the previously shadowed add-on shall become enabled + // (AddOnEnabled()). If an add-on appears in a directory, or is renamed, + // it can cause another add-on to become disabled, if it has the same name. + // Note that directories are not watched recursively, and all entries + // are reported as add-ons regardless of their node type (files, + // directories, symlinks). + virtual status_t AddDirectory(const node_ref* nref); protected: - // hooks for subclass - virtual void AddOnCreated(const add_on_entry_info* entryInfo); - virtual void AddOnEnabled(const add_on_entry_info* entryInfo); - virtual void AddOnDisabled(const add_on_entry_info* entryInfo); - virtual void AddOnRemoved(const add_on_entry_info* entryInfo); + // hooks for sub-class + virtual void AddOnCreated( + const add_on_entry_info* entryInfo); + virtual void AddOnEnabled( + const add_on_entry_info* entryInfo); + virtual void AddOnDisabled( + const add_on_entry_info* entryInfo); + // name field will be invalid! + virtual void AddOnRemoved( + const add_on_entry_info* entryInfo); + // name field will be invalid! protected: - virtual void EntryCreated(const char* name, ino_t directory, - dev_t device, ino_t node); - virtual void EntryRemoved(ino_t directory, dev_t device, ino_t node); - virtual void EntryMoved(const char* name, ino_t fromDirectory, - ino_t toDirectory, dev_t device, ino_t node); + virtual void EntryCreated(const char* name, ino_t directory, + dev_t device, ino_t node); + virtual void EntryRemoved(ino_t directory, dev_t device, + ino_t node); + virtual void EntryMoved(const char* name, + ino_t fromDirectory, ino_t toDirectory, + dev_t device, ino_t node); + virtual void StatChanged(ino_t node, dev_t device); private: - typedef NodeMonitorHandler inherited; + void _HandlePulse(); + void _EntryCreated(add_on_entry_info& info); - void _HandlePulse(); + typedef NodeMonitorHandler inherited; + typedef std::list EntryList; - std::list fDirectories; - std::list fPendingEntries; - std::list fFormerEntries; + struct add_on_directory_info { + node_ref nref; + EntryList entries; + }; + + typedef std::list DirectoryList; + + bool _FindEntry(const node_ref& entry, + const EntryList& list, + EntryList::iterator& it) const; + bool _FindEntry(const char* name, + const EntryList& list, + EntryList::iterator& it) const; + + bool _HasEntry(const node_ref& entry, + EntryList& list) const; + bool _HasEntry(const char* name, + EntryList& list) const; + + bool _FindDirectory(ino_t directory, dev_t device, + DirectoryList::iterator& it) const; + bool _FindDirectory( + const node_ref& directoryNodeRef, + DirectoryList::iterator& it) const; + bool _FindDirectory(ino_t directory, dev_t device, + DirectoryList::iterator& it, + const DirectoryList::const_iterator& end) + const; + bool _FindDirectory( + const node_ref& directoryNodeRef, + DirectoryList::iterator& it, + const DirectoryList::const_iterator& end) + const; + + void _AddNewEntry(EntryList& list, + add_on_entry_info& info); + +private: + DirectoryList fDirectories; + EntryList fPendingEntries; }; }; // namespace Storage }; // namespace BPrivate + using namespace BPrivate::Storage; + #endif // _ADD_ON_MONITOR_HANDLER_H diff --git a/src/kits/storage/AddOnMonitorHandler.cpp b/src/kits/storage/AddOnMonitorHandler.cpp index c6094d47b2..54d89eaa2c 100644 --- a/src/kits/storage/AddOnMonitorHandler.cpp +++ b/src/kits/storage/AddOnMonitorHandler.cpp @@ -4,6 +4,7 @@ * * Authors: * Andrew Bachmann + * Stephan Aßmus */ @@ -29,6 +30,16 @@ AddOnMonitorHandler::AddOnMonitorHandler(const char* name) AddOnMonitorHandler::~AddOnMonitorHandler() { + // TODO: Actually calling watch_node() here should be too late, since we + // are likely not attached to a looper anymore, and thus consitute no valid + // BMessenger at this time. + DirectoryList::iterator it = fDirectories.begin(); + for (; it != fDirectories.end(); it++) { + EntryList::iterator eiter = it->entries.begin(); + for (; eiter != it->entries.end(); eiter++) + watch_node(&eiter->addon_nref, B_STOP_WATCHING, this); + watch_node(&it->nref, B_STOP_WATCHING, this); + } } @@ -47,16 +58,17 @@ AddOnMonitorHandler::AddDirectory(const node_ref* nref) { // Keep the looper thread locked, since this method is likely to be called // in a thread other than the looper thread. Otherwise we may access the - // lists concurrently with the looper thread, when node monitor notifications - // arrive while we are still adding initial entries from the directory, or - // (much more likely) if the looper thread handles a pulse message and wants - // to process pending entries while we are still adding them. + // lists concurrently with the looper thread, when node monitor + // notifications arrive while we are still adding initial entries from the + // directory, or (much more likely) if the looper thread handles a pulse + // message and wants to process pending entries while we are still adding + // them. BAutolock _(Looper()); // ignore directories added twice - std::list::iterator iterator = fDirectories.begin(); - for (; iterator != fDirectories.end() ; iterator++) { - if (iterator->nref == *nref) + DirectoryList::iterator it = fDirectories.begin(); + for (; it != fDirectories.end(); it++) { + if (it->nref == *nref) return B_OK; } @@ -65,25 +77,24 @@ AddOnMonitorHandler::AddDirectory(const node_ref* nref) if (status != B_OK) return status; + status = watch_node(nref, B_WATCH_DIRECTORY, this); + if (status != B_OK) + return status; + add_on_directory_info dirInfo; dirInfo.nref = *nref; fDirectories.push_back(dirInfo); - status = watch_node(nref, B_WATCH_DIRECTORY, this); - if (status != B_OK) { - fDirectories.pop_back(); - return status; - } + add_on_entry_info entryInfo; + entryInfo.dir_nref = *nref; BEntry entry; - while (directory.GetNextEntry(&entry, true) == B_OK) { - add_on_entry_info entryInfo; + while (directory.GetNextEntry(&entry) == B_OK) { if (entry.GetName(entryInfo.name) != B_OK || entry.GetNodeRef(&entryInfo.nref) != B_OK) { continue; } - entryInfo.dir_nref = *nref; fPendingEntries.push_back(entryInfo); } @@ -143,70 +154,55 @@ AddOnMonitorHandler::EntryRemoved(ino_t directory, dev_t device, ino_t node) // Search pending entries first, which can simply be discarded // We might have this entry in the pending list multiple times, // so we search entire list through, even after finding one. - std::list::iterator eiter = fPendingEntries.begin(); + EntryList::iterator eiter = fPendingEntries.begin(); while (eiter != fPendingEntries.end()) { - if (eiter->nref == entryNodeRef) { + if (eiter->nref == entryNodeRef) eiter = fPendingEntries.erase(eiter); - } else { + else eiter++; - } } - add_on_entry_info info; - node_ref dirNodeRef; - make_node_ref(device, directory, &dirNodeRef); - - // find the entry's info, and the entry's directory info - std::list::iterator diter = fDirectories.begin(); - for (; diter != fDirectories.end() ; diter++) { - if (diter->nref == dirNodeRef) { - std::list::iterator eiter - = diter->entries.begin(); - for (; eiter != diter->entries.end() ; eiter++) { - info = *eiter; - if (eiter->nref == entryNodeRef) { - info = *eiter; - diter->entries.erase(eiter); - break; - } - } - break; - } - } - - // if it was not found, we're done - if (diter == fDirectories.end()) + // Find the directory of the entry. + DirectoryList::iterator diter = fDirectories.begin(); + if (!_FindDirectory(directory, device, diter)) { + // If it was not found, we're done return; + } + + eiter = diter->entries.begin(); + if (!_FindEntry(entryNodeRef, diter->entries, eiter)) { + // This must be the directory, but we didn't find the entry. + return; + } + + add_on_entry_info info = *eiter; + watch_node(&entryNodeRef, B_STOP_WATCHING, this); + diter->entries.erase(eiter); // Start at the top again, and search until the directory we found // the old add-on in. If we find an add-on with the same name then - // the old add-on was not enabled. So we deallocate the old - // add-on and return. - std::list::iterator diter2 = fDirectories.begin(); - for (; diter2 != diter ; diter2++) { - std::list::iterator eiter = diter2->entries.begin(); - for (; eiter != diter2->entries.end() ; eiter++) { - if (strcmp(eiter->name, info.name) == 0) { - AddOnRemoved(&info); - return; - } + // the old add-on was not enabled. So we deallocate the old add-on and + // return. + DirectoryList::iterator diter2 = fDirectories.begin(); + for (; diter2 != diter; diter2++) { + if (_HasEntry(info.name, diter2->entries)) { + AddOnRemoved(&info); + return; } } - // The active add-on was removed. We need to disable and - // then subsequently deallocate it. + // An active add-on was removed. We need to disable and then subsequently + // deallocate it. AddOnDisabled(&info); AddOnRemoved(&info); // Continue searching for an add-on below us. If we find an add-on // with the same name, we must enable it. - for (diter++ ; diter != fDirectories.end() ; diter++) { - std::list::iterator eiter = diter->entries.begin(); - for (; eiter != diter->entries.end() ; eiter++) { - if (strcmp(eiter->name, info.name) == 0) { - AddOnEnabled(&*eiter); - return; - } + for (diter++; diter != fDirectories.end(); diter++) { + eiter = diter->entries.begin(); + if (_FindEntry(info.name, diter->entries, eiter)) { + AddOnEnabled(&*eiter); + break; } } } @@ -216,69 +212,49 @@ void AddOnMonitorHandler::EntryMoved(const char* name, ino_t fromDirectory, ino_t toDirectory, dev_t device, ino_t node) { - // Search the "from" directory in the known entries - node_ref fromNodeRef; - make_node_ref(device, fromDirectory, &fromNodeRef); - std::list::iterator from_iter = fDirectories.begin(); - for (; from_iter != fDirectories.end(); from_iter++) { - if (from_iter->nref == fromNodeRef) - break; - } - - // Search the "to" directory in the known entries node_ref toNodeRef; make_node_ref(device, toDirectory, &toNodeRef); - std::list::iterator to_iter = fDirectories.begin(); - for ( ; to_iter != fDirectories.end() ; to_iter++) { - if (to_iter->nref == toNodeRef) - break; - } - if (from_iter == fDirectories.end() && to_iter == fDirectories.end()) { + // Search the "from" and "to" directory in the known directories + DirectoryList::iterator from_iter = fDirectories.begin(); + bool watchingFromDirectory = _FindDirectory(fromDirectory, device, + from_iter); + + DirectoryList::iterator to_iter = fDirectories.begin(); + bool watchingToDirectory = _FindDirectory(toNodeRef, to_iter); + + if (!watchingFromDirectory && !watchingToDirectory) { // It seems the notification was for a directory we are not - // actually watching by intention. + // actually watching (at least not by intention). return; } add_on_entry_info info; + node_ref entryNodeRef; make_node_ref(device, node, &entryNodeRef); - if (to_iter == fDirectories.end()) { + if (!watchingToDirectory) { // moved out of our view - std::list::iterator eiter - = from_iter->entries.begin(); - for (; eiter != from_iter->entries.end() ; eiter++) { - if (entryNodeRef == eiter->nref) { - // save the info and remove the entry - info = *eiter; - from_iter->entries.erase(eiter); - break; - } - } - - if (eiter == from_iter->entries.end()) { + EntryList::iterator eiter = from_iter->entries.begin(); + if (!_FindEntry(entryNodeRef, from_iter->entries, eiter)) { // we don't know anything about this entry yet.. ignore it return; } - // if the name is the same, save the information about this - // add-on into fFormerEntries for later use - if (strcmp(info.name, name) == 0) - fFormerEntries.push_back(info); + // save the info and remove the entry + info = *eiter; + watch_node(&entryNodeRef, B_STOP_WATCHING, this); + from_iter->entries.erase(eiter); // Start at the top again, and search until the from directory. // If we find a add-on with the same name then the moved add-on // was not enabled. So we are done. - std::list::iterator diter = fDirectories.begin(); - for (; diter != from_iter ; diter++) { - std::list::iterator eiter2 - = diter->entries.begin(); - for (; eiter2 != diter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - return; - } - } + DirectoryList::iterator diter = fDirectories.begin(); + for (; diter != from_iter; diter++) { + eiter = diter->entries.begin(); + if (_FindEntry(info.name, diter->entries, eiter)) + return; } // finally disable the add-on @@ -286,141 +262,110 @@ AddOnMonitorHandler::EntryMoved(const char* name, ino_t fromDirectory, // Continue searching for a add-on below us. If we find a add-on // with the same name, we must enable it. - for (from_iter++ ; from_iter != fDirectories.end() ; from_iter++) { - std::list::iterator eiter2 - = from_iter->entries.begin(); - for (; eiter2 != from_iter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - AddOnEnabled(&*eiter2); - return; - } + for (from_iter++; from_iter != fDirectories.end(); from_iter++) { + eiter = from_iter->entries.begin(); + if (_FindEntry(info.name, from_iter->entries, eiter)) { + AddOnEnabled(&*eiter); + return; } } - // finally, if the new name is different, destroy the addon - if (strcmp(info.name, name) != 0) - AddOnRemoved(&info); + // finally destroy the addon + AddOnRemoved(&info); // done return; } - if (from_iter == fDirectories.end()) { + if (!watchingFromDirectory) { // moved into our view - std::list::iterator eiter = fFormerEntries.begin(); - for (; eiter != fFormerEntries.end() ; eiter++) { - if (entryNodeRef == eiter->nref) { - // save the info and remove the entry - info = *eiter; - fFormerEntries.erase(eiter); - break; - } - } - - if (eiter != fFormerEntries.end()) { - if (strcmp(info.name, name) != 0) { - // name changed on the way in, remove the old one - AddOnRemoved(&info); - } - } // update the info strncpy(info.name, name, sizeof(info.name)); info.nref = entryNodeRef; info.dir_nref = toNodeRef; - if (eiter == fFormerEntries.end()) { - // this add-on was not seen before - AddOnCreated(&info); - } + AddOnCreated(&info); // Start at the top again, and search until the to directory. // If we find an add-on with the same name then the moved add-on // is not to be enabled. So we are done. - std::list::iterator diter = fDirectories.begin(); - for (; diter != to_iter ; diter++) { - std::list::iterator eiter2 = diter->entries.begin(); - for (; eiter2 != diter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) - return; + DirectoryList::iterator diter = fDirectories.begin(); + for (; diter != to_iter; diter++) { + if (_HasEntry(info.name, diter->entries)) { + // The new add-on is being shadowed. + return; } } // The new add-on should be enabled, but first we check to see // if there is an add-on below us. If we find one, we disable it. - bool shadowing = false; - for (diter++ ; diter != fDirectories.end() ; diter++) { - std::list::iterator eiter2 = diter->entries.begin(); - for (; eiter2 != diter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - AddOnDisabled(&*eiter2); - shadowing = true; - break; - } - } - if (shadowing) + for (diter++ ; diter != fDirectories.end(); diter++) { + EntryList::iterator eiter = diter->entries.begin(); + if (_FindEntry(info.name, diter->entries, eiter)) { + AddOnDisabled(&*eiter); break; + } } // enable the new add-on AddOnEnabled(&info); // put the new entry into the target directory - to_iter->entries.push_back(info); + _AddNewEntry(to_iter->entries, info); // done return; } - std::list::iterator eiter = from_iter->entries.begin(); - for (; eiter != from_iter->entries.end() ; eiter++) { - if (entryNodeRef == eiter->nref) { - // save the old info and remove the entry - info = *eiter; - from_iter->entries.erase(eiter); - break; - } + // The add-on was renamed, or moved within our hierarchy. + + EntryList::iterator eiter = from_iter->entries.begin(); + if (_FindEntry(entryNodeRef, from_iter->entries, eiter)) { + // save the old info and remove the entry + info = *eiter; + } else { + // If an entry moved from one watched directory into another watched + // directory, there will be two notifications, and this may be the + // second. We have handled everything in the first. In that case the + // entry was already removed from the fromDirectory and added in the + // toDirectory list. + return; } if (strcmp(info.name, name) == 0) { - // same name moved in heirarchy - debugger("add-on moved inside the heirarchy"); + // It should be impossible for the name to stay the same, unless the + // node moved in the watched hierarchy. Handle this case by removing + // the entry and readding it. TODO: This can temporarily enable add-ons + // which should in fact stay hidden (moving add-on from home to common + // folder or vice versa, the system add-on should remain hidden). + EntryRemoved(fromDirectory, device, node); + info.dir_nref = toNodeRef; + _EntryCreated(info); } else { + // Erase the entry + from_iter->entries.erase(eiter); + // check to see if it was formerly enabled - bool was_enabled = true; - std::list::iterator old_iter - = fDirectories.begin(); - for (; old_iter != from_iter ; old_iter++) { - std::list::iterator eiter2 - = old_iter->entries.begin(); - for (; eiter2 != old_iter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - was_enabled = false; - break; - } - } - if (!was_enabled) { + bool wasEnabled = true; + DirectoryList::iterator old_iter = fDirectories.begin(); + for (; old_iter != from_iter; old_iter++) { + if (_HasEntry(info.name, old_iter->entries)) { + wasEnabled = false; break; } } - // if it was enabled, disable it and enable the one under us, if it - // exists - if (was_enabled) { + // If it was enabled, disable it and enable the one under us, if it + // exists. + if (wasEnabled) { AddOnDisabled(&info); - bool done = false; - for (; old_iter != fDirectories.end() ; old_iter++) { - std::list::iterator eiter2 - = old_iter->entries.begin(); - for (; eiter2 != old_iter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - AddOnEnabled(&*eiter2); - done = true; - break; - } - } - if (done) + for (; old_iter != fDirectories.end(); old_iter++) { + eiter = old_iter->entries.begin(); + if (_FindEntry(info.name, old_iter->entries, eiter)) { + AddOnEnabled(&*eiter); break; + } } } @@ -436,56 +381,75 @@ AddOnMonitorHandler::EntryMoved(const char* name, ino_t fromDirectory, // check to see if we are newly enabled bool isEnabled = true; - std::list::iterator new_iter - = fDirectories.begin(); - for (; new_iter != to_iter ; new_iter++) { - std::list::iterator eiter2 - = new_iter->entries.begin(); - for (; eiter2 != new_iter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - isEnabled = false; - break; - } - } - if (!isEnabled) + DirectoryList::iterator new_iter = fDirectories.begin(); + for (; new_iter != to_iter; new_iter++) { + if (_HasEntry(info.name, new_iter->entries)) { + isEnabled = false; break; + } } // if it is newly enabled, check under us for an enabled one, and // disable that first if (isEnabled) { - bool done = false; - for (; new_iter != fDirectories.end() ; new_iter++) { - std::list::iterator eiter2 - = new_iter->entries.begin(); - for (; eiter2 != new_iter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - AddOnDisabled(&*eiter2); - done = true; - break; - } - } - if (done) + for (; new_iter != fDirectories.end(); new_iter++) { + eiter = new_iter->entries.begin(); + if (_FindEntry(info.name, new_iter->entries, eiter)) { + AddOnDisabled(&*eiter); break; + } } AddOnEnabled(&info); } + // put the new entry into the target directory + to_iter->entries.push_back(info); } - - // put the new entry into the target directory - to_iter->entries.push_back(info); } +void +AddOnMonitorHandler::StatChanged(ino_t node, dev_t device) +{ + // This notification is received for the add-ons themselves. + + // TODO: Add the entry to the pending list, disable/enable it + // when the modification time remains stable. + + node_ref entryNodeRef; + make_node_ref(device, node, &entryNodeRef); + + DirectoryList::iterator diter = fDirectories.begin(); + for (; diter != fDirectories.end(); diter++) { + EntryList::iterator eiter = diter->entries.begin(); + for (; eiter != diter->entries.end(); eiter++) { + if (eiter->addon_nref == entryNodeRef) { + // Trigger reloading of the add-on + const add_on_entry_info* info = &*eiter; + AddOnDisabled(info); + AddOnRemoved(info); + AddOnCreated(info); + AddOnEnabled(info); + return; + } + } + } +} + + +// #pragma mark - private + + //! Process pending entries. void AddOnMonitorHandler::_HandlePulse() { BDirectory directory; - std::list::iterator iter = fPendingEntries.begin(); + EntryList::iterator iter = fPendingEntries.begin(); while (iter != fPendingEntries.end()) { add_on_entry_info info = *iter; + // Initialize directory, or re-use from previous iteration, if + // directory node_ref remained the same from the last pending entry. node_ref dirNodeRef; if (directory.GetNodeRef(&dirNodeRef) != B_OK || dirNodeRef != info.dir_nref) { @@ -514,58 +478,150 @@ AddOnMonitorHandler::_HandlePulse() // we are going to deal with the stable entry, so remove it iter = fPendingEntries.erase(iter); - // put the new entry into the directory info - std::list::iterator diter = fDirectories.begin(); - for (; diter != fDirectories.end() ; diter++) { - if (diter->nref == dirNodeRef) { - diter->entries.push_back(info); - break; - } - } - - // report it - AddOnCreated(&info); - - // Start at the top again, and search until the directory we put - // the new add-on in. If we find an add-on with the same name then - // the new add-on should not be enabled. - bool enabled = true; - std::list::iterator diter2 - = fDirectories.begin(); - for (; diter2 != diter ; diter2++) { - std::list::iterator eiter - = diter2->entries.begin(); - for (; eiter != diter2->entries.end() ; eiter++) { - if (strcmp(eiter->name, info.name) == 0) { - enabled = false; - break; - } - } - if (!enabled) - break; - } - if (!enabled) { - // if we are not enabled, go on to the next pending entry - continue; - } - - // The new add-on should be enabled, but first we check to see - // if there is an add-on below us. If we find one, we disable it. - bool shadowing = false; - for (diter++ ; diter != fDirectories.end() ; diter++) { - std::list::iterator eiter = diter->entries.begin(); - for (; eiter != diter->entries.end() ; eiter++) { - if (strcmp(eiter->name, info.name) == 0) { - AddOnDisabled(&*eiter); - shadowing = true; - break; - } - } - if (shadowing) - break; - } - - // finally, enable the new entry - AddOnEnabled(&info); + _EntryCreated(info); } } + + +void +AddOnMonitorHandler::_EntryCreated(add_on_entry_info& info) +{ + // put the new entry into the directory info + DirectoryList::iterator diter = fDirectories.begin(); + for (; diter != fDirectories.end(); diter++) { + if (diter->nref == info.dir_nref) { + _AddNewEntry(diter->entries, info); + break; + } + } + + // report it + AddOnCreated(&info); + + // Start at the top again, and search until the directory we put + // the new add-on in. If we find an add-on with the same name then + // the new add-on should not be enabled. + bool enabled = true; + DirectoryList::iterator diter2 = fDirectories.begin(); + for (; diter2 != diter; diter2++) { + if (_HasEntry(info.name, diter2->entries)) { + enabled = false; + break; + } + } + if (!enabled) + return; + + // The new add-on should be enabled, but first we check to see + // if there is an add-on shadowed by the new one. If we find one, + // we disable it. + for (diter++ ; diter != fDirectories.end(); diter++) { + EntryList::iterator eiter = diter->entries.begin(); + if (_FindEntry(info.name, diter->entries, eiter)) { + AddOnDisabled(&*eiter); + break; + } + } + + // enable the new entry + AddOnEnabled(&info); +} + + +bool +AddOnMonitorHandler::_FindEntry(const node_ref& entry, const EntryList& list, + EntryList::iterator& it) const +{ + for (; it != list.end(); it++) { + if (it->nref == entry) + return true; + } + return false; +} + + +bool +AddOnMonitorHandler::_FindEntry(const char* name, const EntryList& list, + EntryList::iterator& it) const +{ + for (; it != list.end(); it++) { + if (strcmp(it->name, name) == 0) + return true; + } + return false; +} + + +bool +AddOnMonitorHandler::_HasEntry(const node_ref& entry, EntryList& list) const +{ + EntryList::iterator it = list.begin(); + return _FindEntry(entry, list, it); +} + + +bool +AddOnMonitorHandler::_HasEntry(const char* name, EntryList& list) const +{ + EntryList::iterator it = list.begin(); + return _FindEntry(name, list, it); +} + + +bool +AddOnMonitorHandler::_FindDirectory(ino_t directory, dev_t device, + DirectoryList::iterator& it) const +{ + node_ref nodeRef; + make_node_ref(device, directory, &nodeRef); + return _FindDirectory(nodeRef, it, fDirectories.end()); +} + + +bool +AddOnMonitorHandler::_FindDirectory(const node_ref& directoryNodeRef, + DirectoryList::iterator& it) const +{ + return _FindDirectory(directoryNodeRef, it, fDirectories.end()); +} + + +bool +AddOnMonitorHandler::_FindDirectory(ino_t directory, dev_t device, + DirectoryList::iterator& it, + const DirectoryList::const_iterator& end) const +{ + node_ref nodeRef; + make_node_ref(device, directory, &nodeRef); + return _FindDirectory(nodeRef, it, end); +} + + +bool +AddOnMonitorHandler::_FindDirectory(const node_ref& directoryNodeRef, + DirectoryList::iterator& it, + const DirectoryList::const_iterator& end) const +{ + for (; it != end; it++) { + if (it->nref == directoryNodeRef) + return true; + } + return false; +} + + +void +AddOnMonitorHandler::_AddNewEntry(EntryList& list, add_on_entry_info& info) +{ + BDirectory directory(&info.dir_nref); + BEntry entry(&directory, info.name, true); + + node_ref addOnRef; + if (entry.GetNodeRef(&addOnRef) == B_OK) { + watch_node(&addOnRef, B_WATCH_STAT, this); + info.addon_nref = addOnRef; + } else + info.addon_nref = info.nref; + + list.push_back(info); +}