[haiku-commits] BRANCH HaikuPM-github.package-management [c143884] src/kits/storage src/tests/kits/storage/testapps src/kits headers/private/storage

  • From: HaikuPM-github.package-management <community@xxxxxxxxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Thu, 27 Jun 2013 22:01:14 +0200 (CEST)

added 21 changesets to branch 'refs/remotes/HaikuPM-github/package-management'
old head: ece582547a43df2c2009900ac638fecb97c71d9c
new head: c143884fdf03ec3a8b7a89c88baa477f8629395c
overview: https://github.com/haiku/HaikuPM/compare/ece5825...c143884

----------------------------------------------------------------------------

1055112: Move PUuid from shared to support

535207a: BPoseView::AddPosesTask(): fix count checks
  
  In case GetNextDirents() returned an error, the wrong blocks were
  executed.

2752612: BMergedDirectory::GetNextDirents(): fix end-of-list return value

5fa3519: BString::Private: Add IsShareable()

62b0b64: BStringList::Add(): fix ref counting problem
  
  Handle the case that the private data of the given string is not
  shareable.

6ca95bd: PathMonitor.cpp: some style cleanup

ad1875f: BPathMonitor: use pthread_once for initialization

77ca66c: BPathMonitor: make the node watching mechanism configurable
  
  Add inner class BWatchingInterface and method SetWatchingInterface().
  This abstracts the calls to watch_node() and stop_watching(), thus
  making it possible to use the path monitor in Tracker.

ddd775f: BPoseView::FSNotification(): fix issue in debug build

3209bc4: BPathMonitor: PathHandler::_NotifyTarget(): simplify
  
  * Add optional entry_ref return parameter to _HasFile().
  * Simplify _NotifyTarget() by using _HasDirectory() and _HasFile().

8d572c9: Add class NotOwningEntryRef
  
  A entry_ref subclass that avoids cloning the entry name.

7b198d8: B_WATCH_FOLDERS_ONLY -> B_WATCH_DIRECTORIES_ONLY
  
  Stick to the nomenclature generally used in the public API.

38afe23: BPathMonitor: pass BMessenger by reference

cb4a05c: Missed B_WATCH_FOLDERS_ONLY occurrence

3e8daeb: Add BMessenger::HashValue()

1eda851: BOpenHashTable: Add IsEmpty()

0d603ac: Fix node monitoring slot accounting for stop_watching()
  
  NodeMonitorService::RemoveUserListeners() didn't decrement
  io_context::num_monitors when removing a listener, so limit checks
  would be off afterwards.

8c974aa: node monitor: add TODO regarding the syscalls

cc4d194: Add test suite for BPathMonitor
  
  749 / 1504 tests fail

04382d4: BPathMonitor: rewrite
  
  This resolves all issues the test suite uncovered. It should also deal
  with hard links correctly, though that hasn't been tested. Still
  unsupported are:
  * changes due to mounting/unmounting a volume,
  * tracking of symlinks in the path components.

c143884: Use incorrect use of BPathMonitor in input/midi/net server
  
  The B_ENTRY_* constants aren't valid watch flags.

                                    [ Ingo Weinhold <ingo_weinhold@xxxxxx> ]

----------------------------------------------------------------------------

21 files changed, 3454 insertions(+), 787 deletions(-)
headers/os/app/Messenger.h                       |    2 +
headers/private/kernel/util/OpenHashTable.h      |    5 +
headers/private/storage/NotOwningEntryRef.h      |   90 +
headers/private/storage/PathMonitor.h            |   66 +-
headers/private/support/StringPrivate.h          |    5 +
headers/private/{shared => support}/Uuid.h       |    0
src/kits/app/Messenger.cpp                       |    7 +
src/kits/shared/Jamfile                          |    1 -
src/kits/storage/Jamfile                         |    2 +-
src/kits/storage/MergedDirectory.cpp             |    2 +-
src/kits/storage/PathMonitor.cpp                 | 2568 +++++++++++++-----
src/kits/support/Jamfile                         |    1 +
src/kits/support/StringList.cpp                  |   16 +-
src/kits/{shared => support}/Uuid.cpp            |    0
src/kits/tracker/PoseView.cpp                    |   15 +-
src/servers/input/AddOnManager.cpp               |    5 +-
src/servers/midi/DeviceWatcher.cpp               |    5 +-
src/servers/net/NetServer.cpp                    |    4 +-
src/system/kernel/fs/node_monitor.cpp            |    7 +
src/tests/kits/storage/testapps/Jamfile          |    2 +
.../kits/storage/testapps/PathMonitorTest2.cpp   | 1438 ++++++++++

############################################################################

Commit:      105511275e00602f982f593afea9f65c6bd075e9
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 20 01:41:36 2013 UTC

Move PUuid from shared to support

----------------------------------------------------------------------------

diff --git a/headers/private/shared/Uuid.h b/headers/private/support/Uuid.h
similarity index 100%
rename from headers/private/shared/Uuid.h
rename to headers/private/support/Uuid.h
diff --git a/src/kits/shared/Jamfile b/src/kits/shared/Jamfile
index d4aca53..2459568 100644
--- a/src/kits/shared/Jamfile
+++ b/src/kits/shared/Jamfile
@@ -33,7 +33,6 @@ StaticLibrary libshared.a :
        ShakeTrackingFilter.cpp
        StringForRate.cpp
        StringForSize.cpp
-       Uuid.cpp
        Variant.cpp
 ;
 
diff --git a/src/kits/support/Jamfile b/src/kits/support/Jamfile
index 40ecc11..80b74f3 100644
--- a/src/kits/support/Jamfile
+++ b/src/kits/support/Jamfile
@@ -23,6 +23,7 @@ MergeObject <libbe>support_kit.o :
        String.cpp
        StringList.cpp
        Url.cpp
+       Uuid.cpp
 ;
 
 StaticLibrary libreferenceable.a : : [ FGristFiles Referenceable.o ] ;
diff --git a/src/kits/shared/Uuid.cpp b/src/kits/support/Uuid.cpp
similarity index 100%
rename from src/kits/shared/Uuid.cpp
rename to src/kits/support/Uuid.cpp

############################################################################

Commit:      535207ae9a3e759ac960379905a5e1661174eb7c
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 20 03:52:07 2013 UTC

BPoseView::AddPosesTask(): fix count checks

In case GetNextDirents() returned an error, the wrong blocks were
executed.

----------------------------------------------------------------------------

diff --git a/src/kits/tracker/PoseView.cpp b/src/kits/tracker/PoseView.cpp
index 88583bd..fe69b14 100644
--- a/src/kits/tracker/PoseView.cpp
+++ b/src/kits/tracker/PoseView.cpp
@@ -1349,7 +1349,7 @@ BPoseView::AddPosesTask(void* castToParams)
                        if (count <= 0 && modelChunkIndex == -1)
                                break;
 
-                       if (count) {
+                       if (count > 0) {
                                ASSERT(count == 1);
 
                                if ((!hideDotFiles && (!strcmp(eptr->d_name, 
".")
@@ -1393,7 +1393,7 @@ BPoseView::AddPosesTask(void* castToParams)
                                throw failToLock();
                        }
 
-                       if (count) {
+                       if (count > 0) {
                                // try to watch the model, no matter what
 
                                if (result != B_OK) {
@@ -1421,7 +1421,7 @@ BPoseView::AddPosesTask(void* castToParams)
 
                        bigtime_t now = system_time();
 
-                       if (!count || modelChunkIndex >= kMaxAddPosesChunk - 1
+                       if (count <= 0 || modelChunkIndex >= kMaxAddPosesChunk 
- 1
                                || now > nextChunkTime) {
                                // keep getting models until we get 
<kMaxAddPosesChunk> of them
                                // or until 300000 runs out
@@ -1446,7 +1446,7 @@ BPoseView::AddPosesTask(void* castToParams)
                                snooze(500);    // be nice
                        }
 
-                       if (!count)
+                       if (count <= 0)
                                break;
                }
 

############################################################################

Commit:      2752612bc60b08174c98181b33b7be3a7b93c3e3
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 20 03:52:45 2013 UTC

BMergedDirectory::GetNextDirents(): fix end-of-list return value

----------------------------------------------------------------------------

diff --git a/src/kits/storage/MergedDirectory.cpp 
b/src/kits/storage/MergedDirectory.cpp
index 2ec338f..fb35094 100644
--- a/src/kits/storage/MergedDirectory.cpp
+++ b/src/kits/storage/MergedDirectory.cpp
@@ -170,7 +170,7 @@ BMergedDirectory::GetNextDirents(struct dirent* 
direntBuffer, size_t bufferSize,
                }
        }
 
-       return B_ENTRY_NOT_FOUND;
+       return 0;
 }
 
 

############################################################################

Commit:      5fa3519dece47e6f9b519e56df643e52642b2fa6
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 20 08:28:12 2013 UTC

BString::Private: Add IsShareable()

----------------------------------------------------------------------------

diff --git a/headers/private/support/StringPrivate.h 
b/headers/private/support/StringPrivate.h
index 68a098d..799151b 100644
--- a/headers/private/support/StringPrivate.h
+++ b/headers/private/support/StringPrivate.h
@@ -27,6 +27,11 @@ public:
                return fString.fPrivateData;
        }
 
+       bool IsShareable() const
+       {
+               return fString._IsShareable();
+       }
+
        static vint32& DataRefCount(char* data)
        {
                return *(((int32 *)data) - 2);

############################################################################

Commit:      62b0b64124aa1eb9007ecbeae69938e99aeae4d6
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 20 08:30:08 2013 UTC

BStringList::Add(): fix ref counting problem

Handle the case that the private data of the given string is not
shareable.

----------------------------------------------------------------------------

diff --git a/src/kits/support/StringList.cpp b/src/kits/support/StringList.cpp
index 8d51f3f..fe6e138 100644
--- a/src/kits/support/StringList.cpp
+++ b/src/kits/support/StringList.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011, Ingo Weinhold, ingo_weinhold@xxxxxx
+ * Copyright 2011-2013, Ingo Weinhold, ingo_weinhold@xxxxxx
  * Copyright 2011, Clemens Zeidler <haiku@xxxxxxxxxxxxxxxxxx>
  *
  * Distributed under the terms of the MIT License.
@@ -55,8 +55,13 @@ BStringList::~BStringList()
 
 
 bool
-BStringList::Add(const BString& string, int32 index)
+BStringList::Add(const BString& _string, int32 index)
 {
+       BString string(_string);
+               // makes sure the string is shareable
+       if (string.Length() != _string.Length())
+               return false;
+
        char* privateData = BString::Private(string).Data();
        if (!fStrings.AddItem(privateData, index))
                return false;
@@ -67,8 +72,13 @@ BStringList::Add(const BString& string, int32 index)
 
 
 bool
-BStringList::Add(const BString& string)
+BStringList::Add(const BString& _string)
 {
+       BString string(_string);
+               // makes sure the string is shareable
+       if (string.Length() != _string.Length())
+               return false;
+
        char* privateData = BString::Private(string).Data();
        if (!fStrings.AddItem(privateData))
                return false;

############################################################################

Commit:      6ca95bd81343ac9be3836d112cf5e079a6e6ff9f
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 20 11:46:56 2013 UTC

PathMonitor.cpp: some style cleanup

----------------------------------------------------------------------------

diff --git a/src/kits/storage/PathMonitor.cpp b/src/kits/storage/PathMonitor.cpp
index 27ee2f1..cb66b2e 100644
--- a/src/kits/storage/PathMonitor.cpp
+++ b/src/kits/storage/PathMonitor.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007-2008, Haiku Inc. All Rights Reserved.
+ * Copyright 2007-2013, Haiku Inc. All Rights Reserved.
  * Distributed under the terms of the MIT License.
  *
  * Authors:
@@ -10,7 +10,12 @@
 
 #include <PathMonitor.h>
 
-#include <ObjectList.h>
+#include <pthread.h>
+#include <stdio.h>
+
+#include <map>
+#include <new>
+#include <set>
 
 #include <Autolock.h>
 #include <Directory.h>
@@ -21,10 +26,8 @@
 #include <Path.h>
 #include <String.h>
 
-#include <map>
-#include <new>
-#include <set>
-#include <stdio.h>
+#include <ObjectList.h>
+
 
 #undef TRACE
 //#define TRACE_PATH_MONITOR
@@ -34,10 +37,12 @@
 #      define TRACE(x...) ;
 #endif
 
+
 using namespace BPrivate;
 using namespace std;
 using std::nothrow; // TODO: Remove this line if the above line is enough.
 
+
 // TODO: Use optimizations where stuff is already known to avoid iterating
 // the watched directory and files set too often.
 

############################################################################

Commit:      ad1875fd705fe89064bcc91788ab4a05d14bf59d
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 20 11:48:21 2013 UTC

BPathMonitor: use pthread_once for initialization

----------------------------------------------------------------------------

diff --git a/headers/private/storage/PathMonitor.h 
b/headers/private/storage/PathMonitor.h
index 68c1cb8..c32e94e 100644
--- a/headers/private/storage/PathMonitor.h
+++ b/headers/private/storage/PathMonitor.h
@@ -33,8 +33,8 @@ private:
                                                                BPathMonitor();
                                                                ~BPathMonitor();
 
-       static  status_t                        _InitLockerIfNeeded();
-       static  status_t                        _InitLooperIfNeeded();
+       static  status_t                        _InitIfNeeded();
+       static  void                            _Init();
 };
 
 }      // namespace BPrivate
diff --git a/src/kits/storage/PathMonitor.cpp b/src/kits/storage/PathMonitor.cpp
index cb66b2e..dfa2137 100644
--- a/src/kits/storage/PathMonitor.cpp
+++ b/src/kits/storage/PathMonitor.cpp
@@ -143,6 +143,7 @@ class PathHandler : public BHandler {
 };
 
 
+static pthread_once_t sInitOnce = PTHREAD_ONCE_INIT;
 static WatcherMap sWatchers;
 static BLocker* sLocker = NULL;
 static BLooper* sLooper = NULL;
@@ -975,50 +976,32 @@ BPathMonitor::~BPathMonitor()
 
 
 /*static*/ status_t
-BPathMonitor::_InitLockerIfNeeded()
+BPathMonitor::_InitIfNeeded()
 {
-       static vint32 lock = 0;
-
-       if (sLocker != NULL)
-               return B_OK;
-
-       while (sLocker == NULL) {
-               if (atomic_add(&lock, 1) == 0) {
-                       sLocker = new (nothrow) BLocker("path monitor");
-                       TRACE("Create PathMonitor locker\n");
-                       if (sLocker == NULL)
-                               return B_NO_MEMORY;
-               }
-               snooze(5000);
-       }
-
-       return B_OK;
+       pthread_once(&sInitOnce, &BPathMonitor::_Init);
+       return sLooper != NULL ? B_OK : B_NO_MEMORY;
 }
 
 
-/*static*/ status_t
-BPathMonitor::_InitLooperIfNeeded()
+/*static*/ void
+BPathMonitor::_Init()
 {
-       static vint32 lock = 0;
-
-       if (sLooper != NULL)
-               return B_OK;
+       sLocker = new (nothrow) BLocker("path monitor");
+       TRACE("Create PathMonitor locker\n");
+       if (sLocker == NULL)
+               return;
 
-       while (sLooper == NULL) {
-               if (atomic_add(&lock, 1) == 0) {
-                       // first thread initializes the global looper
-                       sLooper = new (nothrow) BLooper("PathMonitor looper");
-                       TRACE("Start PathMonitor looper\n");
-                       if (sLooper == NULL)
-                               return B_NO_MEMORY;
-                       thread_id thread = sLooper->Run();
-                       if (thread < B_OK)
-                               return (status_t)thread;
-               }
-               snooze(5000);
+       BLooper* looper = new (nothrow) BLooper("PathMonitor looper");
+       TRACE("Start PathMonitor looper\n");
+       if (looper == NULL)
+               return;
+       thread_id thread = looper->Run();
+       if (thread < 0) {
+               delete looper;
+               return;
        }
 
-       return sLooper->Thread() >= 0 ? B_OK : B_ERROR;
+       sLooper = looper;
 }
 
 
@@ -1027,15 +1010,10 @@ BPathMonitor::StartWatching(const char* path, uint32 
flags, BMessenger target)
 {
        TRACE("StartWatching(%s)\n", path);
 
-       status_t status = _InitLockerIfNeeded();
+       status_t status = _InitIfNeeded();
        if (status != B_OK)
                return status;
 
-       // use the global looper for receiving node monitor notifications
-       status = _InitLooperIfNeeded();
-       if (status < B_OK)
-               return status;
-
        BAutolock _(sLocker);
 
        WatcherMap::iterator iterator = sWatchers.find(target);

############################################################################

Commit:      77ca66cdb7b44669fc03e80e084234bee21bfd0e
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 20 11:59:22 2013 UTC

BPathMonitor: make the node watching mechanism configurable

Add inner class BWatchingInterface and method SetWatchingInterface().
This abstracts the calls to watch_node() and stop_watching(), thus
making it possible to use the path monitor in Tracker.

----------------------------------------------------------------------------

diff --git a/headers/private/storage/PathMonitor.h 
b/headers/private/storage/PathMonitor.h
index c32e94e..777ffd8 100644
--- a/headers/private/storage/PathMonitor.h
+++ b/headers/private/storage/PathMonitor.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007-2008, Haiku Inc. All Rights Reserved.
+ * Copyright 2007-2013, Haiku Inc. All Rights Reserved.
  * Distributed under the terms of the MIT License.
  */
 #ifndef _PATH_MONITOR_H
@@ -18,10 +18,15 @@
 
 #define B_PATH_MONITOR         '_PMN'
 
+
 namespace BPrivate {
 
+
 class BPathMonitor {
 public:
+                       class BWatchingInterface;
+
+public:
        static  status_t                        StartWatching(const char* path, 
uint32 flags,
                                                                        
BMessenger target);
 
@@ -29,6 +34,10 @@ public:
                                                                        
BMessenger target);
        static  status_t                        StopWatching(BMessenger target);
 
+       static  void                            SetWatchingInterface(
+                                                                       
BWatchingInterface* watchingInterface);
+                                                                       // pass 
NULL to reset to default
+
 private:
                                                                BPathMonitor();
                                                                ~BPathMonitor();
@@ -37,6 +46,27 @@ private:
        static  void                            _Init();
 };
 
+
+/*!    Base class just delegates to the respective C functions.
+ */
+class BPathMonitor::BWatchingInterface {
+public:
+                                                               
BWatchingInterface();
+       virtual                                         ~BWatchingInterface();
+
+       virtual status_t                        WatchNode(const node_ref* node, 
uint32 flags,
+                                                                       const 
BMessenger& target);
+       virtual status_t                        WatchNode(const node_ref* node, 
uint32 flags,
+                                               const BHandler* handler,
+                                                                       const 
BLooper* looper = NULL);
+
+       virtual status_t                        StopWatching(const BMessenger& 
target);
+       virtual status_t                        StopWatching(const BHandler* 
handler,
+                                                                       const 
BLooper* looper = NULL);
+};
+
+
 }      // namespace BPrivate
 
+
 #endif // _PATH_MONITOR_H
diff --git a/src/kits/storage/PathMonitor.cpp b/src/kits/storage/PathMonitor.cpp
index dfa2137..cab06a5 100644
--- a/src/kits/storage/PathMonitor.cpp
+++ b/src/kits/storage/PathMonitor.cpp
@@ -147,6 +147,8 @@ static pthread_once_t sInitOnce = PTHREAD_ONCE_INIT;
 static WatcherMap sWatchers;
 static BLocker* sLocker = NULL;
 static BLooper* sLooper = NULL;
+static BPathMonitor::BWatchingInterface* sDefaultWatchingInterface = NULL;
+static BPathMonitor::BWatchingInterface* sWatchingInterface = NULL;
 
 
 static status_t
@@ -248,7 +250,7 @@ void
 PathHandler::Quit()
 {
        if (sLooper->Lock()) {
-               stop_watching(this);
+               sWatchingInterface->StopWatching(this);
                sLooper->RemoveHandler(this);
                sLooper->Unlock();
        }
@@ -706,7 +708,7 @@ PathHandler::_AddDirectory(BEntry& entry, bool notify)
        else
                flags = B_WATCH_DIRECTORY;
 
-       status = watch_node(&directory.node, flags, this);
+       status = sWatchingInterface->WatchNode(&directory.node, flags, this);
        if (status != B_OK)
                return status;
 
@@ -765,7 +767,7 @@ PathHandler::_RemoveDirectory(const node_ref& nodeRef, 
ino_t directoryNode)
        if (iterator == fDirectories.end())
                return B_ENTRY_NOT_FOUND;
 
-       watch_node(&directory.node, B_STOP_WATCHING, this);
+       sWatchingInterface->WatchNode(&directory.node, B_STOP_WATCHING, this);
 
        node_ref directoryRef;
        directoryRef.device = nodeRef.device;
@@ -852,7 +854,8 @@ PathHandler::_AddFile(BEntry& entry, bool notify)
        if (_HasFile(nodeRef))
                return B_OK;
 
-       status = watch_node(&nodeRef, (fFlags & WATCH_NODE_FLAG_MASK), this);
+       status = sWatchingInterface->WatchNode(&nodeRef,
+               (fFlags & WATCH_NODE_FLAG_MASK), this);
        if (status != B_OK)
                return status;
 
@@ -893,7 +896,7 @@ PathHandler::_RemoveFile(const node_ref& nodeRef)
        if (iterator == fFiles.end())
                return B_ENTRY_NOT_FOUND;
 
-       watch_node(&nodeRef, B_STOP_WATCHING, this);
+       sWatchingInterface->WatchNode(&nodeRef, B_STOP_WATCHING, this);
        fFiles.erase(iterator);
        return B_OK;
 }
@@ -991,6 +994,13 @@ BPathMonitor::_Init()
        if (sLocker == NULL)
                return;
 
+       sDefaultWatchingInterface = new(std::nothrow) BWatchingInterface;
+       if (sDefaultWatchingInterface == NULL)
+               return;
+
+       if (sWatchingInterface == NULL)
+               SetWatchingInterface(sDefaultWatchingInterface);
+
        BLooper* looper = new (nothrow) BLooper("PathMonitor looper");
        TRACE("Start PathMonitor looper\n");
        if (looper == NULL)
@@ -1106,4 +1116,57 @@ BPathMonitor::StopWatching(BMessenger target)
        return B_OK;
 }
 
+
+/*static*/ void
+BPathMonitor::SetWatchingInterface(BWatchingInterface* watchingInterface)
+{
+       sWatchingInterface = watchingInterface != NULL
+               ? watchingInterface : sDefaultWatchingInterface;
+}
+
+
+// #pragma mark - BWatchingInterface
+
+
+BPathMonitor::BWatchingInterface::BWatchingInterface()
+{
+}
+
+
+BPathMonitor::BWatchingInterface::~BWatchingInterface()
+{
+}
+
+
+status_t
+BPathMonitor::BWatchingInterface::WatchNode(const node_ref* node, uint32 flags,
+       const BMessenger& target)
+{
+       return watch_node(node, flags, target);
+}
+
+
+status_t
+BPathMonitor::BWatchingInterface::WatchNode(const node_ref* node, uint32 flags,
+       const BHandler* handler, const BLooper* looper)
+{
+       return watch_node(node, flags, handler, looper);
+}
+
+
+status_t
+BPathMonitor::BWatchingInterface::StopWatching(const BMessenger& target)
+{
+       return stop_watching(target);
+}
+
+
+status_t
+BPathMonitor::BWatchingInterface::StopWatching(const BHandler* handler,
+       const BLooper* looper)
+{
+       return stop_watching(handler, looper);
+}
+
+
 }      // namespace BPrivate

############################################################################

Commit:      ddd775f5ac1bf7dee15dd492500b50b935b71935
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 20 12:58:34 2013 UTC

BPoseView::FSNotification(): fix issue in debug build

----------------------------------------------------------------------------

diff --git a/src/kits/tracker/PoseView.cpp b/src/kits/tracker/PoseView.cpp
index fe69b14..1b03aaf 100644
--- a/src/kits/tracker/PoseView.cpp
+++ b/src/kits/tracker/PoseView.cpp
@@ -5174,13 +5174,12 @@ BPoseView::FSNotification(const BMessage* message)
                                }
 
                                const char* name;
-                               if (message->FindString("name", &name) != B_OK)
-                                       break;
+                               if (message->FindString("name", &name) != B_OK) 
{
 #if DEBUG
-                               else
                                        SERIAL_PRINT(("no name in entry 
creation message\n"));
-                                       break;
 #endif
+                                       break;
+                               }
                                if (count) {
                                        // basically, let's say we have a 
broken link :
                                        // ./a_link -> 
./some_folder/another_folder/a_target

############################################################################

Commit:      3209bc40c523d63e98d635ae302df6bece0966d3
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Sat Jun 22 11:41:48 2013 UTC

BPathMonitor: PathHandler::_NotifyTarget(): simplify

* Add optional entry_ref return parameter to _HasFile().
* Simplify _NotifyTarget() by using _HasDirectory() and _HasFile().

----------------------------------------------------------------------------

diff --git a/src/kits/storage/PathMonitor.cpp b/src/kits/storage/PathMonitor.cpp
index cab06a5..9e43750 100644
--- a/src/kits/storage/PathMonitor.cpp
+++ b/src/kits/storage/PathMonitor.cpp
@@ -126,7 +126,8 @@ class PathHandler : public BHandler {
                status_t _RemoveDirectory(const node_ref& nodeRef, ino_t 
directoryNode);
                status_t _RemoveDirectory(BEntry& entry, ino_t directoryNode);
 
-               bool _HasFile(const node_ref& nodeRef) const;
+               bool _HasFile(const node_ref& nodeRef, const entry_ref** _ref = 
NULL)
+                       const;
                status_t _AddFile(BEntry& entry, bool notify = false);
                status_t _RemoveFile(const node_ref& nodeRef);
                status_t _RemoveFile(BEntry& entry);
@@ -635,11 +636,7 @@ PathHandler::_NotifyTarget(BMessage* message, const 
node_ref& nodeRef) const
 
        TRACE("_NotifyTarget(): node ref %ld.%Ld\n", nodeRef.device, 
nodeRef.node);
 
-       WatchedDirectory directory;
-       directory.node = nodeRef;
-
-       DirectorySet::const_iterator iterator = fDirectories.find(directory);
-       if (iterator != fDirectories.end()) {
+       if (_HasDirectory(nodeRef)) {
                if (_WatchFilesOnly()) {
                        // stat or attr notification for a directory
                        return;
@@ -655,13 +652,10 @@ PathHandler::_NotifyTarget(BMessage* message, const 
node_ref& nodeRef) const
                        // this is bound to be a notification for a file
                        return;
                }
-               FileEntry setEntry;
-               setEntry.ref.device = nodeRef.device;
-               setEntry.node = nodeRef.node;
-                       // name does not need to be set, since it's not used 
for comparing
-               FileSet::const_iterator i = fFiles.find(setEntry);
-               if (i != fFiles.end()) {
-                       BPath path(&(i->ref));
+
+               const entry_ref* entryRef;
+               if (_HasFile(nodeRef, &entryRef)) {
+                       BPath path(entryRef);
                        update.AddString("path", path.Path());
                }
        }
@@ -815,14 +809,20 @@ PathHandler::_RemoveDirectory(BEntry& entry, ino_t 
directoryNode)
 
 
 bool
-PathHandler::_HasFile(const node_ref& nodeRef) const
+PathHandler::_HasFile(const node_ref& nodeRef,
+       const entry_ref** _ref /*= NULL*/) const
 {
        FileEntry setEntry;
        setEntry.ref.device = nodeRef.device;
        setEntry.node = nodeRef.node;
                // name does not need to be set, since it's not used for 
comparing
        FileSet::const_iterator iterator = fFiles.find(setEntry);
-       return iterator != fFiles.end();
+       if (iterator == fFiles.end())
+               return false;
+
+       if (_ref != NULL)
+               *_ref = &iterator->ref;
+       return true;
 }
 
 

############################################################################

Commit:      8d572c926432f2e29b3ffe88576a64ba731772a4
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Sat Jun 22 14:40:32 2013 UTC

Add class NotOwningEntryRef

A entry_ref subclass that avoids cloning the entry name.

----------------------------------------------------------------------------

diff --git a/headers/private/storage/NotOwningEntryRef.h 
b/headers/private/storage/NotOwningEntryRef.h
new file mode 100644
index 0000000..9e3312a
--- /dev/null
+++ b/headers/private/storage/NotOwningEntryRef.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2013, Haiku, Inc. All Rights Reserved.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ *             Ingo Weinhold <ingo_weinhold@xxxxxx>
+ */
+#ifndef _NOT_OWNING_ENTRY_REF_H
+#define _NOT_OWNING_ENTRY_REF_H
+
+
+#include <Entry.h>
+#include <Node.h>
+
+
+namespace BPrivate {
+
+
+/*!    entry_ref subclass that avoids cloning the entry name.
+
+       Therefore initialization is cheaper and cannot fail. It derives from
+       entry_ref for convenience. However, care must be taken that:
+        - the name remains valid while the object is in use,
+        - the object isn't passed as an entry_ref to a function that modifies 
it.
+ */
+class NotOwningEntryRef : public entry_ref {
+public:
+       NotOwningEntryRef()
+       {
+       }
+
+       NotOwningEntryRef(dev_t device, ino_t directory, const char* name)
+       {
+               SetTo(device, directory, name);
+       }
+
+       NotOwningEntryRef(const node_ref& directoryRef, const char* name)
+       {
+               SetTo(directoryRef, name);
+       }
+
+       NotOwningEntryRef(const entry_ref& other)
+       {
+               *this = other;
+       }
+
+       ~NotOwningEntryRef()
+       {
+               name = NULL;
+       }
+
+       NotOwningEntryRef& SetTo(dev_t device, ino_t directory, const char* 
name)
+       {
+               this->device = device;
+               this->directory = directory;
+               this->name = const_cast<char*>(name);
+               return *this;
+       }
+
+       NotOwningEntryRef& SetTo(const node_ref& directoryRef, const char* name)
+       {
+               return SetTo(directoryRef.device, directoryRef.node, name);
+       }
+
+       node_ref DirectoryNodeRef() const
+       {
+               return node_ref(device, directory);
+       }
+
+       NotOwningEntryRef& SetDirectoryNodeRef(const node_ref& directoryRef)
+       {
+               device = directoryRef.device;
+               directory = directoryRef.node;
+               return *this;
+       }
+
+       NotOwningEntryRef& operator=(const entry_ref& other)
+       {
+               return SetTo(other.device, other.directory, other.name);
+       }
+};
+
+
+} // namespace BPrivate
+
+
+using BPrivate::NotOwningEntryRef;
+
+
+#endif // _NOT_OWNING_ENTRY_REF_H

############################################################################

Commit:      7b198d812e45dfdaf08d5660fec27b3b4acf2a6f
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Sun Jun 23 11:01:09 2013 UTC

B_WATCH_FOLDERS_ONLY -> B_WATCH_DIRECTORIES_ONLY

Stick to the nomenclature generally used in the public API.

----------------------------------------------------------------------------

diff --git a/headers/private/storage/PathMonitor.h 
b/headers/private/storage/PathMonitor.h
index 777ffd8..657162d 100644
--- a/headers/private/storage/PathMonitor.h
+++ b/headers/private/storage/PathMonitor.h
@@ -10,9 +10,9 @@
 
 
 // additional flags (combined with those in NodeMonitor.h)
-#define        B_WATCH_FILES_ONLY              0x0100
-#define B_WATCH_RECURSIVELY            0x0200
-#define        B_WATCH_FOLDERS_ONLY    0x0400
+#define        B_WATCH_FILES_ONLY                      0x0100
+#define B_WATCH_RECURSIVELY                    0x0200
+#define        B_WATCH_DIRECTORIES_ONLY        0x0400
 // NOTE: B_WATCH_RECURSIVELY usually implies to watch for file changes as well,
 // if that is not desired, add B_WATCH_FOLDERS_ONLY to the flags.
 
diff --git a/src/kits/storage/PathMonitor.cpp b/src/kits/storage/PathMonitor.cpp
index 9e43750..9763280 100644
--- a/src/kits/storage/PathMonitor.cpp
+++ b/src/kits/storage/PathMonitor.cpp
@@ -334,7 +334,7 @@ PathHandler::_WatchFilesOnly() const
 bool
 PathHandler::_WatchFoldersOnly() const
 {
-       return (fFlags & B_WATCH_FOLDERS_ONLY) != 0;
+       return (fFlags & B_WATCH_DIRECTORIES_ONLY) != 0;
 }
 
 

############################################################################

Commit:      38afe232de0aba455fb21897ac8a739dd5793e52
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Sun Jun 23 11:12:38 2013 UTC

BPathMonitor: pass BMessenger by reference

----------------------------------------------------------------------------

diff --git a/headers/private/storage/PathMonitor.h 
b/headers/private/storage/PathMonitor.h
index 657162d..f623e9c 100644
--- a/headers/private/storage/PathMonitor.h
+++ b/headers/private/storage/PathMonitor.h
@@ -28,11 +28,11 @@ public:
 
 public:
        static  status_t                        StartWatching(const char* path, 
uint32 flags,
-                                                                       
BMessenger target);
+                                                                       const 
BMessenger& target);
 
        static  status_t                        StopWatching(const char* path,
-                                                                       
BMessenger target);
-       static  status_t                        StopWatching(BMessenger target);
+                                                                       const 
BMessenger& target);
+       static  status_t                        StopWatching(const BMessenger& 
target);
 
        static  void                            SetWatchingInterface(
                                                                        
BWatchingInterface* watchingInterface);
diff --git a/src/kits/storage/PathMonitor.cpp b/src/kits/storage/PathMonitor.cpp
index 9763280..a782171 100644
--- a/src/kits/storage/PathMonitor.cpp
+++ b/src/kits/storage/PathMonitor.cpp
@@ -1016,7 +1016,8 @@ BPathMonitor::_Init()
 
 
 /*static*/ status_t
-BPathMonitor::StartWatching(const char* path, uint32 flags, BMessenger target)
+BPathMonitor::StartWatching(const char* path, uint32 flags,
+       const BMessenger& target)
 {
        TRACE("StartWatching(%s)\n", path);
 
@@ -1056,7 +1057,7 @@ BPathMonitor::StartWatching(const char* path, uint32 
flags, BMessenger target)
 
 
 /*static*/ status_t
-BPathMonitor::StopWatching(const char* path, BMessenger target)
+BPathMonitor::StopWatching(const char* path, const BMessenger& target)
 {
        if (sLocker == NULL)
                return B_NO_INIT;
@@ -1090,7 +1091,7 @@ BPathMonitor::StopWatching(const char* path, BMessenger 
target)
 
 
 /*static*/ status_t
-BPathMonitor::StopWatching(BMessenger target)
+BPathMonitor::StopWatching(const BMessenger& target)
 {
        if (sLocker == NULL)
                return B_NO_INIT;

############################################################################

Commit:      cb4a05cfdf59c3645e0e09e3cf154204e1a28c41
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Mon Jun 24 08:52:07 2013 UTC

Missed B_WATCH_FOLDERS_ONLY occurrence

----------------------------------------------------------------------------

diff --git a/headers/private/storage/PathMonitor.h 
b/headers/private/storage/PathMonitor.h
index f623e9c..3309eb2 100644
--- a/headers/private/storage/PathMonitor.h
+++ b/headers/private/storage/PathMonitor.h
@@ -14,7 +14,7 @@
 #define B_WATCH_RECURSIVELY                    0x0200
 #define        B_WATCH_DIRECTORIES_ONLY        0x0400
 // NOTE: B_WATCH_RECURSIVELY usually implies to watch for file changes as well,
-// if that is not desired, add B_WATCH_FOLDERS_ONLY to the flags.
+// if that is not desired, add B_WATCH_DIRECTORIES_ONLY to the flags.
 
 #define B_PATH_MONITOR         '_PMN'
 

############################################################################

Commit:      3e8daeb7bc9c99f4858c072638b11f8fc9c7c220
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 27 19:13:08 2013 UTC

Add BMessenger::HashValue()

----------------------------------------------------------------------------

diff --git a/headers/os/app/Messenger.h b/headers/os/app/Messenger.h
index 1285014..3adec7d 100644
--- a/headers/os/app/Messenger.h
+++ b/headers/os/app/Messenger.h
@@ -57,6 +57,8 @@ public:
        bool IsValid() const;
        team_id Team() const;
 
+       uint32 HashValue() const;
+
        //----- Private or reserved -----------------------------------------
 
        class Private;
diff --git a/src/kits/app/Messenger.cpp b/src/kits/app/Messenger.cpp
index 3e284a3..11975b8 100644
--- a/src/kits/app/Messenger.cpp
+++ b/src/kits/app/Messenger.cpp
@@ -504,6 +504,13 @@ BMessenger::Team() const
 }
 
 
+uint32
+BMessenger::HashValue() const
+{
+       return fPort * 19 + fHandlerToken;
+}
+
+
 //     #pragma mark - Private or reserved
 
 

############################################################################

Commit:      1eda8517f126fed1af44d199a9c06d6f4584ac8d
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 27 19:14:11 2013 UTC

BOpenHashTable: Add IsEmpty()

----------------------------------------------------------------------------

diff --git a/headers/private/kernel/util/OpenHashTable.h 
b/headers/private/kernel/util/OpenHashTable.h
index a2af3aa..b8804cb 100644
--- a/headers/private/kernel/util/OpenHashTable.h
+++ b/headers/private/kernel/util/OpenHashTable.h
@@ -126,6 +126,11 @@ public:
                return fTableSize;
        }
 
+       bool IsEmpty() const
+       {
+               return fItemCount == 0;
+       }
+
        size_t CountElements() const
        {
                return fItemCount;

############################################################################

Commit:      0d603ac65ca118bfc02ea8ec34ef74f93c5fc956
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 27 19:21:42 2013 UTC

Fix node monitoring slot accounting for stop_watching()

NodeMonitorService::RemoveUserListeners() didn't decrement
io_context::num_monitors when removing a listener, so limit checks
would be off afterwards.

----------------------------------------------------------------------------

diff --git a/src/system/kernel/fs/node_monitor.cpp 
b/src/system/kernel/fs/node_monitor.cpp
index 601fd6c..d665344 100644
--- a/src/system/kernel/fs/node_monitor.cpp
+++ b/src/system/kernel/fs/node_monitor.cpp
@@ -982,6 +982,7 @@ NodeMonitorService::RemoveUserListeners(struct io_context 
*context,
                        // to remove its successor (which is saved in 
"removeListener")
 
                _RemoveListener(removeListener);
+               context->num_monitors--;
                count++;
        }
 

############################################################################

Commit:      8c974aa8006f2f33f6b14563bda5a14f5bcc412d
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 27 19:22:02 2013 UTC

node monitor: add TODO regarding the syscalls

----------------------------------------------------------------------------

diff --git a/src/system/kernel/fs/node_monitor.cpp 
b/src/system/kernel/fs/node_monitor.cpp
index d665344..ae5e66e 100644
--- a/src/system/kernel/fs/node_monitor.cpp
+++ b/src/system/kernel/fs/node_monitor.cpp
@@ -1247,6 +1247,12 @@ notify_query_attr_changed(port_id port, int32 token, 
dev_t device,
 //     #pragma mark - User syscalls
 
 
+// TODO: We should verify that the port specified in the syscalls does actually
+// belong to the calling team. The situation is complicated by the fact that a
+// port can be transferred to another team. Consequently we should remove all
+// associated monitor listeners when a port is transferred/deleted.
+
+
 status_t
 _user_stop_notifying(port_id port, uint32 token)
 {

############################################################################

Commit:      cc4d194aeb41c48a4f7b7d00e3715f2f65a8af2c
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 27 19:37:08 2013 UTC

Add test suite for BPathMonitor

749 / 1504 tests fail

----------------------------------------------------------------------------

diff --git a/src/tests/kits/storage/testapps/Jamfile 
b/src/tests/kits/storage/testapps/Jamfile
index 2698a85..0b6ef3f 100644
--- a/src/tests/kits/storage/testapps/Jamfile
+++ b/src/tests/kits/storage/testapps/Jamfile
@@ -20,3 +20,5 @@ SimpleTest PathMonitorTest
        : PathMonitorTest.cpp PathMonitor.cpp
        : be $(TARGET_LIBSTDC++)
 ;
+
+SimpleTest PathMonitorTest2 : PathMonitorTest2.cpp : be $(TARGET_LIBSTDC++) ;
diff --git a/src/tests/kits/storage/testapps/PathMonitorTest2.cpp 
b/src/tests/kits/storage/testapps/PathMonitorTest2.cpp
new file mode 100644
index 0000000..0aa8400
--- /dev/null
+++ b/src/tests/kits/storage/testapps/PathMonitorTest2.cpp
@@ -0,0 +1,1438 @@
+/*
+ * Copyright 2013, Haiku, Inc.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ *             Ingo Weinhold, ingo_weinhold@xxxxxx
+ */
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <algorithm>
+#include <set>
+#include <vector>
+
+#include <Directory.h>
+#include <Entry.h>
+#include <File.h>
+#include <Looper.h>
+#include <ObjectList.h>
+#include <Path.h>
+#include <String.h>
+
+#include <AutoDeleter.h>
+#include <AutoLocker.h>
+#include <NotOwningEntryRef.h>
+#include <PathMonitor.h>
+
+
+using BPrivate::BPathMonitor;
+
+
+static const char* const kTestBasePath = "/tmp/path-monitor-test";
+static const bigtime_t kMaxNotificationDelay = 100000;
+
+
+#define FATAL(...)                                                             
                                        \
+       do {                                                                    
                                                \
+               throw FatalException(                                           
                                \
+                       BString().SetToFormat("%s:%d: ", __FILE__, __LINE__)    
\
+                               << BString().SetToFormat(__VA_ARGS__));         
                \
+       } while (false)
+
+#define FATAL_IF_ERROR(error, ...)                                             
                                \
+       do {                                                                    
                                                        \
+               status_t _fatalError = (error);                                 
                                \
+               if (_fatalError < 0) {                                          
                                        \
+                       throw FatalException(                                   
                                        \
+                               BString().SetToFormat("%s:%d: ", __FILE__, 
__LINE__)    \
+                                       << BString().SetToFormat(__VA_ARGS__)   
                        \
+                                       << BString().SetToFormat(               
                                        \
+                                               ": %s\n", 
strerror(_fatalError)));                              \
+               }                                                               
                                                                \
+       } while (false)
+
+#define FATAL_IF_POSIX_ERROR(error, ...)       \
+       if ((error) < 0)                                                \
+               FATAL_IF_ERROR(errno, __VA_ARGS__)
+
+#define FAIL(...)      \
+       throw TestException(BString().SetToFormat(__VA_ARGS__))
+
+
+struct TestException {
+       TestException(const BString& message)
+               :
+               fMessage(message)
+       {
+       }
+
+       const BString& Message() const
+       {
+               return fMessage;
+       }
+
+private:
+       BString fMessage;
+};
+
+
+struct FatalException {
+       FatalException(const BString& message)
+               :
+               fMessage(message)
+       {
+       }
+
+       const BString& Message() const
+       {
+               return fMessage;
+       }
+
+private:
+       BString fMessage;
+};
+
+
+static BString
+test_path(const BString& maybeRelativePath)
+{
+       if (maybeRelativePath.ByteAt(0) == '/')
+               return maybeRelativePath;
+
+       BString path;
+       path.SetToFormat("%s/%s", kTestBasePath, maybeRelativePath.String());
+       if (path.IsEmpty())
+               FATAL_IF_ERROR(B_NO_MEMORY, "Failed to make absolute path");
+       return path;
+}
+
+
+static BString
+node_ref_to_string(const node_ref& nodeRef)
+{
+       return BString().SetToFormat("%" B_PRIdDEV ":%" B_PRIdINO, 
nodeRef.device,
+               nodeRef.node);
+}
+
+
+static BString
+entry_ref_to_string(const entry_ref& entryRef)
+{
+       return BString().SetToFormat("%" B_PRIdDEV ":%" B_PRIdINO ":\"%s\"",
+               entryRef.device, entryRef.directory, entryRef.name);
+}
+
+
+static BString
+indented_string(const char* string, const char* indent,
+       const char* firstIndent = NULL)
+{
+       const char* end = string + strlen(string);
+       BString result;
+       const char* line = string;
+       while (line < end) {
+               const char* lineEnd = strchr(line, '\n');
+               lineEnd = lineEnd != NULL ? lineEnd + 1 : end;
+               result
+                       << (line == string && firstIndent != NULL ? firstIndent 
: indent);
+               result.Append(line, lineEnd - line);
+               line = lineEnd;
+       }
+
+       return result;
+}
+
+
+static BString
+message_to_string(const BMessage& message)
+{
+       BString result;
+
+       char* name;
+       type_code typeCode;
+       int32 count;
+       for (int32 i = 0;
+               message.GetInfo(B_ANY_TYPE, i, &name, &typeCode, &count) == 
B_OK;
+               i++) {
+               if (i > 0)
+                       result << '\n';
+
+               result << '"' << name << '"';
+               BString type;
+
+               switch (typeCode) {
+                       case B_UINT8_TYPE:
+                       case B_INT8_TYPE:
+                               type << "int8";
+                               break;
+
+                       case B_UINT16_TYPE:
+                               type = "u";
+                       case B_INT16_TYPE:
+                               type << "int16";
+                               break;
+
+                       case B_UINT32_TYPE:
+                               type = "u";
+                       case B_INT32_TYPE:
+                               type << "int32";
+                               break;
+
+                       case B_UINT64_TYPE:
+                               type = "u";
+                       case B_INT64_TYPE:
+                               type << "int64";
+                               break;
+
+                       case B_STRING_TYPE:
+                               type = "string";
+                               break;
+
+                       default:
+                       {
+                               int code = (int)typeCode;
+                               type.SetToFormat("'%02x%02x%02x%02x'", code >> 
24,
+                                       (code >> 16) & 0xff, (code >> 8) & 
0xff, code & 0xff);
+                               break;
+                       }
+               }
+
+               result << " (" << type << "):";
+
+               for (int32 k = 0; k < count; k++) {
+                       BString value;
+                       switch (typeCode) {
+                               case B_UINT8_TYPE:
+                                       value << message.GetUInt8(name, k, 0);
+                                       break;
+                               case B_INT8_TYPE:
+                                       value << message.GetInt8(name, k, 0);
+                                       break;
+                               case B_UINT16_TYPE:
+                                       value << message.GetUInt16(name, k, 0);
+                                       break;
+                               case B_INT16_TYPE:
+                                       value << message.GetInt16(name, k, 0);
+                                       break;
+                               case B_UINT32_TYPE:
+                                       value << message.GetUInt32(name, k, 0);
+                                       break;
+                               case B_INT32_TYPE:
+                                       value << message.GetInt32(name, k, 0);
+                                       break;
+                               case B_UINT64_TYPE:
+                                       value << message.GetUInt64(name, k, 0);
+                                       break;
+                               case B_INT64_TYPE:
+                                       value << message.GetInt64(name, k, 0);
+                                       break;
+                               case B_STRING_TYPE:
+                                       value.SetToFormat("\"%s\"", 
message.GetString(name, k, ""));
+                                       break;
+                               default:
+                               {
+                                       const void* data;
+                                       ssize_t size;
+                                       if (message.FindData(name, typeCode, k, 
&data, &size)
+                                                       != B_OK) {
+                                               value = "???";
+                                               break;
+                                       }
+
+                                       for (ssize_t l = 0; l < size; l++) {
+                                               uint8 v = ((const 
uint8*)data)[l];
+                                               value << 
BString().SetToFormat("%02x", v);
+                                       }
+                                       break;
+                               }
+                       }
+
+                       if (k == 0 && count == 1) {
+                               result << ' ' << value;
+                       } else {
+                               result << BString().SetToFormat("\n  [%2" 
B_PRId32 "] ", k)
+                                       << value;
+                       }
+               }
+       }
+
+       return result;
+}
+
+
+static BString
+watch_flags_to_string(uint32 flags)
+{
+       BString result;
+       if ((flags & B_WATCH_NAME) != 0)
+               result << "name ";
+       if ((flags & B_WATCH_STAT) != 0)
+               result << "stat ";
+       if ((flags & B_WATCH_ATTR) != 0)
+               result << "attr ";
+       if ((flags & B_WATCH_DIRECTORY) != 0)
+               result << "dir ";
+       if ((flags & B_WATCH_RECURSIVELY) != 0)
+               result << "recursive ";
+       if ((flags & B_WATCH_FILES_ONLY) != 0)
+               result << "files-only ";
+       if ((flags & B_WATCH_DIRECTORIES_ONLY) != 0)
+               result << "dirs-only ";
+
+       if (!result.IsEmpty())
+               result.Truncate(result.Length() - 1);
+       return result;
+}
+
+
+struct MonitoringInfo {
+       MonitoringInfo()
+       {
+       }
+
+       MonitoringInfo(int32 opcode, const char* path)
+               :
+               fOpcode(opcode)
+       {
+               _Init(opcode, path);
+       }
+
+       MonitoringInfo(int32 opcode, const char* fromPath, const char* toPath)
+       {
+               _Init(opcode, toPath);
+
+               // init fFromEntryRef
+               BEntry entry;
+               FATAL_IF_ERROR(entry.SetTo(fromPath),
+                       "Failed to init BEntry for \"%s\"", fromPath);
+               FATAL_IF_ERROR(entry.GetRef(&fFromEntryRef),
+                       "Failed to get entry_ref for \"%s\"", fromPath);
+       }
+
+       BString ToString() const
+       {
+               switch (fOpcode) {
+                       case B_ENTRY_CREATED:
+                       case B_ENTRY_REMOVED:
+                               return BString().SetToFormat("%s %s at %s",
+                                       fOpcode == B_ENTRY_CREATED ? "created" 
: "removed",
+                                       node_ref_to_string(fNodeRef).String(),
+                                       
entry_ref_to_string(fEntryRef).String());
+
+                       case B_ENTRY_MOVED:
+                               return BString().SetToFormat("moved %s from %s 
to %s",
+                                       node_ref_to_string(fNodeRef).String(),
+                                       
entry_ref_to_string(fFromEntryRef).String(),
+                                       
entry_ref_to_string(fEntryRef).String());
+
+                       case B_STAT_CHANGED:
+                               return BString().SetToFormat("stat changed for 
%s",
+                                       node_ref_to_string(fNodeRef).String());
+
+                       case B_ATTR_CHANGED:
+                               return BString().SetToFormat("attr changed for 
%s",
+                                       node_ref_to_string(fNodeRef).String());
+
+                       case B_DEVICE_MOUNTED:
+                               return BString().SetToFormat("volume mounted");
+
+                       case B_DEVICE_UNMOUNTED:
+                               return BString().SetToFormat("volume 
unmounted");
+               }
+
+               return BString();
+       }
+
+       bool Matches(const BMessage& message) const
+       {
+               if (fOpcode != message.GetInt32("opcode", -1))
+                       return false;
+
+               switch (fOpcode) {
+                       case B_ENTRY_CREATED:
+                       case B_ENTRY_REMOVED:
+                       {
+                               NotOwningEntryRef entryRef;
+                               node_ref nodeRef;
+
+                               if (message.FindInt32("device", 
&nodeRef.device) != B_OK
+                                       || message.FindInt64("node", 
&nodeRef.node) != B_OK
+                                       || message.FindInt64("directory", 
&entryRef.directory)
+                                               != B_OK
+                                       || message.FindString("name", (const 
char**)&entryRef.name)
+                                               != B_OK) {
+                                       return false;
+                               }
+                               entryRef.device = nodeRef.device;
+
+                               return nodeRef == fNodeRef && entryRef == 
fEntryRef;
+                       }
+
+                       case B_ENTRY_MOVED:
+                       {
+                               NotOwningEntryRef fromEntryRef;
+                               NotOwningEntryRef toEntryRef;
+                               node_ref nodeRef;
+
+                               if (message.FindInt32("node device", 
&nodeRef.device) != B_OK
+                                       || message.FindInt64("node", 
&nodeRef.node) != B_OK
+                                       || message.FindInt32("device", 
&fromEntryRef.device)
+                                               != B_OK
+                                       || message.FindInt64("from directory",
+                                               &fromEntryRef.directory) != B_OK
+                                       || message.FindInt64("to directory", 
&toEntryRef.directory)
+                                               != B_OK
+                                       || message.FindString("from name",
+                                               (const 
char**)&fromEntryRef.name) != B_OK
+                                       || message.FindString("name",
+                                               (const char**)&toEntryRef.name) 
!= B_OK) {
+                                       return false;
+                               }
+                               toEntryRef.device = fromEntryRef.device;
+
+                               return nodeRef == fNodeRef && toEntryRef == 
fEntryRef
+                                       && fromEntryRef == fFromEntryRef;
+                       }
+
+                       case B_STAT_CHANGED:
+                       case B_ATTR_CHANGED:
+                       {
+                               node_ref nodeRef;
+
+                               if (message.FindInt32("device", 
&nodeRef.device) != B_OK
+                                       || message.FindInt64("node", 
&nodeRef.node) != B_OK) {
+                                       return false;
+                               }
+
+                               return nodeRef == fNodeRef;
+                       }
+
+                       case B_DEVICE_MOUNTED:
+                       case B_DEVICE_UNMOUNTED:
+                               return true;
+               }
+
+               return false;
+       }
+
+private:
+       void _Init(int32 opcode, const char* path)
+       {
+               fOpcode = opcode;
+               BEntry entry;
+               FATAL_IF_ERROR(entry.SetTo(path), "Failed to init BEntry for 
\"%s\"",
+                       path);
+               FATAL_IF_ERROR(entry.GetRef(&fEntryRef),
+                       "Failed to get entry_ref for \"%s\"", path);
+               FATAL_IF_ERROR(entry.GetNodeRef(&fNodeRef),
+                       "Failed to get node_ref for \"%s\"", path);
+       }
+
+private:
+       int32           fOpcode;
+       node_ref        fNodeRef;
+       entry_ref       fEntryRef;
+       entry_ref       fFromEntryRef;
+};
+
+
+struct MonitoringInfoSet {
+       MonitoringInfoSet()
+       {
+       }
+
+       MonitoringInfoSet& Add(const MonitoringInfo& info, bool expected = true)
+       {
+               if (expected)
+                       fInfos.push_back(info);
+               return *this;
+       }
+
+       MonitoringInfoSet& Add(int32 opcode, const BString& path,
+               bool expected = true)
+       {
+               return Add(MonitoringInfo(opcode, test_path(path)), expected);
+       }
+
+       MonitoringInfoSet& Add(int32 opcode, const BString& fromPath,
+               const BString& toPath, bool expected = true)
+       {
+               return Add(MonitoringInfo(opcode, test_path(fromPath),
+                       test_path(toPath)), expected);
+       }
+
+       bool IsEmpty() const
+       {
+               return fInfos.empty();
+       }
+
+       int32 CountInfos() const
+       {
+               return fInfos.size();
+       }
+
+       const MonitoringInfo& InfoAt(int32 index) const
+       {
+               return fInfos[index];
+       }
+
+       void Remove(int32 index)
+       {
+               fInfos.erase(fInfos.begin() + index);
+       }
+
+       BString ToString() const
+       {
+               BString result;
+               for (int32 i = 0; i < CountInfos(); i++) {
+                       const MonitoringInfo& info = InfoAt(i);
+                       if (i > 0)
+                               result << '\n';
+                       result << info.ToString();
+               }
+               return result;
+       }
+
+private:
+       std::vector<MonitoringInfo>     fInfos;
+};
+
+
+struct Test : private BLooper {
+       Test(const char* name)
+               :
+               fName(name),
+               fFlags(0),
+               fLooperThread(-1),
+               fNotifications(10, true),
+               fProcessedMonitoringInfos(),
+               fIsWatching(false)
+       {
+       }
+
+       void Init(uint32 flags)
+       {
+               fFlags = flags;
+
+               // delete and re-create the test directory
+               BEntry entry;
+               FATAL_IF_ERROR(entry.SetTo(kTestBasePath),
+                       "Failed to init entry to \"%s\"", kTestBasePath);
+
+               if (entry.Exists())
+                       _RemoveRecursively(entry);
+
+               _CreateDirectory(kTestBasePath);
+
+               fLooperThread = BLooper::Run();
+               if (fLooperThread < 0)
+                       FATAL_IF_ERROR(fLooperThread, "Failed to init looper");
+       }
+
+       void Delete()
+       {
+               if (fIsWatching)
+                       BPathMonitor::StopWatching(this);
+
+               if (fLooperThread < 0) {
+                       delete this;
+               } else {
+                       PostMessage(B_QUIT_REQUESTED);
+                       wait_for_thread(fLooperThread, NULL);
+               }
+       }
+
+       void Do()
+       {
+               bool recursive = (fFlags & B_WATCH_RECURSIVELY) != 0;
+               DoInternal(recursive && (fFlags & B_WATCH_DIRECTORIES_ONLY) != 
0,
+                       recursive && (fFlags & B_WATCH_FILES_ONLY) != 0, 
recursive,
+                       !recursive && (fFlags & B_WATCH_DIRECTORY) == 0,
+                       (fFlags & B_WATCH_STAT) != 0);
+
+               // verify that there aren't any spurious notifications
+               snooze(kMaxNotificationDelay);
+
+               AutoLocker<BLooper> locker(this);
+               if (fNotifications.IsEmpty())
+                       return;
+
+               BString pendingNotifications
+                       = "unexpected notification(s) at end of test:";
+               for (int32 i = 0; BMessage* message = fNotifications.ItemAt(i); 
i++) {
+                       pendingNotifications << '\n'
+                               << indented_string(message_to_string(*message), 
"    ", "  * ");
+               }
+
+               FAIL("%s%s", pendingNotifications.String(),
+                       _ProcessedInfosString().String());
+       }
+
+       const BString& Name() const
+       {
+               return fName;
+       }
+
+protected:
+       ~Test()
+       {
+       }
+
+       void StartWatching(const char* path)
+       {
+               BString absolutePath(test_path(path));
+               FATAL_IF_ERROR(BPathMonitor::StartWatching(absolutePath, 
fFlags, this),
+                       "Failed to start watching \"%s\"", 
absolutePath.String());
+               fIsWatching = true;
+       }
+
+       MonitoringInfo CreateDirectory(const char* path)
+       {
+               BString absolutePath(test_path(path));
+               _CreateDirectory(absolutePath);
+               return MonitoringInfo(B_ENTRY_CREATED, absolutePath);
+       }
+
+       MonitoringInfo CreateFile(const char* path)
+       {
+               BString absolutePath(test_path(path));
+               FATAL_IF_ERROR(
+                       BFile().SetTo(absolutePath, B_CREATE_FILE | 
B_READ_WRITE),
+                       "Failed to create file \"%s\"", absolutePath.String());
+               return MonitoringInfo(B_ENTRY_CREATED, absolutePath);
+       }
+
+       MonitoringInfo MoveEntry(const char* fromPath, const char* toPath)
+       {
+               BString absoluteFromPath(test_path(fromPath));
+               BString absoluteToPath(test_path(toPath));
+               FATAL_IF_POSIX_ERROR(rename(absoluteFromPath, absoluteToPath),
+                       "Failed to move \"%s\" to \"%s\"", 
absoluteFromPath.String(),
+                       absoluteToPath.String());
+               return MonitoringInfo(B_ENTRY_MOVED, absoluteFromPath, 
absoluteToPath);
+       }
+
+       MonitoringInfo RemoveEntry(const char* path)
+       {
+               BString absolutePath(test_path(path));
+               MonitoringInfo info(B_ENTRY_REMOVED, absolutePath);
+               BEntry entry;
+               FATAL_IF_ERROR(entry.SetTo(absolutePath),
+                       "Failed to init BEntry for \"%s\"", 
absolutePath.String());
+               FATAL_IF_ERROR(entry.Remove(),
+                       "Failed to remove entry \"%s\"", absolutePath.String());
+               return info;
+       }
+
+       MonitoringInfo TouchEntry(const char* path)
+       {
+               BString absolutePath(test_path(path));
+               FATAL_IF_POSIX_ERROR(utimes(absolutePath, NULL),
+                       "Failed to touch \"%s\"", absolutePath.String());
+               MonitoringInfo info(B_STAT_CHANGED, absolutePath);
+               return info;
+       }
+
+       void ExpectNotification(const MonitoringInfo& info, bool expected = 
true)
+       {
+               if (!expected)
+                       return;
+
+               AutoLocker<BLooper> locker(this);
+               if (fNotifications.IsEmpty()) {
+                       locker.Unlock();
+                       snooze(kMaxNotificationDelay);
+                       locker.Lock();
+               }
+
+               if (fNotifications.IsEmpty()) {
+                       FAIL("missing notification, expected:\n  %s",
+                               info.ToString().String());
+               }
+
+               BMessage* message = fNotifications.RemoveItemAt(0);
+               ObjectDeleter<BMessage> messageDeleter(message);
+
+               if (!info.Matches(*message)) {
+                       BString processedInfosString(_ProcessedInfosString());
+                       FAIL("unexpected notification:\n  expected:\n  %s\n  
got:\n%s%s",
+                               info.ToString().String(),
+                               indented_string(message_to_string(*message), "  
  ").String(),
+                               processedInfosString.String());
+               }
+
+               fProcessedMonitoringInfos.Add(info);
+       }
+
+       void ExpectNotifications(MonitoringInfoSet infos)
+       {
+               bool waited = false;
+               AutoLocker<BLooper> locker(this);
+
+               while (!infos.IsEmpty()) {
+                       if (fNotifications.IsEmpty()) {
+                               locker.Unlock();
+                               if (!waited) {
+                                       snooze(kMaxNotificationDelay);
+                                       waited = true;
+                               }
+                               locker.Lock();
+                       }
+
+                       if (fNotifications.IsEmpty()) {
+                               FAIL("missing notification(s), expected:\n%s",
+                                       indented_string(infos.ToString(), "  
").String());
+                       }
+
+                       BMessage* message = fNotifications.RemoveItemAt(0);
+                       ObjectDeleter<BMessage> messageDeleter(message);
+
+                       bool foundMatch = false;
+                       for (int32 i = 0; i < infos.CountInfos(); i++) {
+                               const MonitoringInfo& info = infos.InfoAt(i);
+                               if (info.Matches(*message)) {
+                                       infos.Remove(i);
+                                       foundMatch = true;
+                                       break;
+                               }
+                       }
+
+                       if (foundMatch)
+                               continue;
+
+                       BString processedInfosString(_ProcessedInfosString());
+                       FAIL("unexpected notification:\n  expected:\n%s\n  
got:\n%s%s",
+                               indented_string(infos.ToString(), "    
").String(),
+                               indented_string(message_to_string(*message), "  
  ").String(),
+                               processedInfosString.String());
+               }
+       }
+
+       virtual void DoInternal(bool directoriesOnly, bool filesOnly,
+               bool recursive, bool pathOnly, bool watchStat) = 0;
+
+private:
+       typedef BObjectList<BMessage> MessageList;
+       typedef BObjectList<MonitoringInfo> MonitoringInfoList;
+
+private:
+       virtual void MessageReceived(BMessage* message)
+       {
+               switch (message->what) {
+                       case B_PATH_MONITOR:
+                               if (!fNotifications.AddItem(new 
BMessage(*message)))
+                                       FATAL_IF_ERROR(B_NO_MEMORY, "Failed to 
store notification");
+                               break;
+
+                       default:
+                               BLooper::MessageReceived(message);
+                               break;
+               }
+       }
+
+private:
+       void _CreateDirectory(const char* path)
+       {
+               FATAL_IF_ERROR(create_directory(path, 0755),
+                       "Failed to create directory \"%s\"", path);
+       }
+
+       void _RemoveRecursively(BEntry& entry)
+       {
+               // recurse, if the entry is a directory
+               if (entry.IsDirectory()) {
+                       BDirectory directory;
+                       FATAL_IF_ERROR(directory.SetTo(&entry),
+                               "Failed to init BDirectory for \"%s\"",
+                               BPath(&entry).Path());
+
+                       BEntry childEntry;
+                       while (directory.GetNextEntry(&childEntry) == B_OK)
+                               _RemoveRecursively(childEntry);
+               }
+
+               // remove the entry
+               FATAL_IF_ERROR(entry.Remove(), "Failed to remove entry \"%s\"",
+                       BPath(&entry).Path());
+       }
+
+       BString _ProcessedInfosString() const
+       {
+               BString processedInfosString;
+               if (!fProcessedMonitoringInfos.IsEmpty()) {
+                       processedInfosString << "\nprocessed so far:\n"
+                               << 
indented_string(fProcessedMonitoringInfos.ToString(), "  ");
+               }
+               return processedInfosString;
+       }
+
+protected:
+       BString                         fName;
+       uint32                          fFlags;
+       thread_id                       fLooperThread;
+       MessageList                     fNotifications;
+       MonitoringInfoSet       fProcessedMonitoringInfos;
+       bool                            fIsWatching;
+};
+
+
+struct TestBase : Test {
+protected:
+       TestBase(const char* name)
+               :
+               Test(name)
+       {
+       }
+
+       void StandardSetup()
+       {
+               CreateDirectory("base");
+               CreateDirectory("base/dir1");
+               CreateDirectory("base/dir1/dir0");
+               CreateFile("base/file0");
+               CreateFile("base/dir1/file0.0");
+       }
+};
+
+
+#define CREATE_TEST_WITH_CUSTOM_SETUP(name, code)                              
                \
+       struct Test##name : TestBase {                                          
                                \
+               Test##name() : TestBase(#name) {}                               
                                \
+               virtual void DoInternal(bool directoriesOnly, bool filesOnly,   
\
+                       bool recursive, bool pathOnly, bool watchStat)          
                \
+               {                                                               
                                                                \
+                       code                                                    
                                                        \
+               }                                                               
                                                                \
+       };                                                                      
                                                                \
+       tests.push_back(new Test##name);
+
+#define CREATE_TEST(name, code)                        \
+       CREATE_TEST_WITH_CUSTOM_SETUP(name, \
+               StandardSetup();                                \
+               StartWatching("base");                  \
+               code                                                    \
+       )
+
+
+static void
+create_tests(std::vector<Test*>& tests)
+{
+       // test coverage:
+       // - file/directory outside
+       // - file/directory at top level
+       // - file/directory at sub level
+       // - move file/directory into/within/out of
+       // - move non-empty directory into/within/out of
+       // - create/move ancestor folder
+       // - remove/move ancestor folder
+       // - touch path, file/directory at top and sub level
+       // - base file instead of directory
+       //
+       // not covered (yet):
+       // - mount/unmount below/in our path
+       // - test symlink in watched path
+       // - attribute watching (should be similar to stat watching)
+
+       CREATE_TEST(FileOutside,
+               CreateFile("file1");
+               MoveEntry("file1", "file2");
+               RemoveEntry("file2");
+       )
+
+       CREATE_TEST(DirectoryOutside,
+               CreateDirectory("dir1");
+               MoveEntry("dir1", "dir2");
+               RemoveEntry("dir2");
+       )
+
+       CREATE_TEST(FileTopLevel,
+               ExpectNotification(CreateFile("base/file1"),
+                       !directoriesOnly && !pathOnly);
+               ExpectNotification(MoveEntry("base/file1", "base/file2"),
+                       !directoriesOnly && !pathOnly);
+               ExpectNotification(RemoveEntry("base/file2"),
+                       !directoriesOnly && !pathOnly);
+       )
+
+       CREATE_TEST(DirectoryTopLevel,
+               ExpectNotification(CreateDirectory("base/dir2"),
+                       !filesOnly && !pathOnly);
+               ExpectNotification(MoveEntry("base/dir2", "base/dir3"),
+                       !filesOnly && !pathOnly);
+               ExpectNotification(RemoveEntry("base/dir3"),
+                       !filesOnly && !pathOnly);
+       )
+
+       CREATE_TEST(FileSubLevel,
+               ExpectNotification(CreateFile("base/dir1/file1"),
+                       recursive && !directoriesOnly);
+               ExpectNotification(MoveEntry("base/dir1/file1", 
"base/dir1/file2"),
+                       recursive && !directoriesOnly);
+               ExpectNotification(RemoveEntry("base/dir1/file2"),
+                       recursive && !directoriesOnly);
+       )
+
+       CREATE_TEST(DirectorySubLevel,
+               ExpectNotification(CreateDirectory("base/dir1/dir2"),
+                       recursive && !filesOnly);
+               ExpectNotification(MoveEntry("base/dir1/dir2", 
"base/dir1/dir3"),
+                       recursive && !filesOnly);
+               ExpectNotification(RemoveEntry("base/dir1/dir3"),
+                       recursive && !filesOnly);
+       )
+
+       CREATE_TEST(FileMoveIntoTopLevel,
+               CreateFile("file1");
+               ExpectNotification(MoveEntry("file1", "base/file2"),
+                       !directoriesOnly && !pathOnly);
+               ExpectNotification(RemoveEntry("base/file2"),
+                       !directoriesOnly && !pathOnly);
+       )
+
+       CREATE_TEST(DirectoryMoveIntoTopLevel,
+               CreateDirectory("dir2");
+               ExpectNotification(MoveEntry("dir2", "base/dir3"),
+                       !filesOnly && !pathOnly);
+               ExpectNotification(RemoveEntry("base/dir3"),
+                       !filesOnly && !pathOnly);
+       )
+
+       CREATE_TEST(FileMoveIntoSubLevel,
+               CreateFile("file1");
+               ExpectNotification(MoveEntry("file1", "base/dir1/file2"),
+                       recursive && !directoriesOnly);
+               ExpectNotification(RemoveEntry("base/dir1/file2"),
+                       recursive && !directoriesOnly);
+       )
+
+       CREATE_TEST(DirectoryMoveIntoSubLevel,
+               CreateDirectory("dir2");
+               ExpectNotification(MoveEntry("dir2", "base/dir1/dir3"),
+                       recursive && !filesOnly);
+               ExpectNotification(RemoveEntry("base/dir1/dir3"),
+                       recursive && !filesOnly);
+       )
+
+       CREATE_TEST(FileMoveOutOfTopLevel,
+               ExpectNotification(CreateFile("base/file1"),
+                       !directoriesOnly && !pathOnly);
+               ExpectNotification(MoveEntry("base/file1", "file2"),
+                       !directoriesOnly && !pathOnly);
+               RemoveEntry("file2");
+       )
+
+       CREATE_TEST(DirectoryMoveOutOfTopLevel,
+               ExpectNotification(CreateDirectory("base/dir2"),
+                       !filesOnly && !pathOnly);
+               ExpectNotification(MoveEntry("base/dir2", "dir3"),
+                       !filesOnly && !pathOnly);
+               RemoveEntry("dir3");
+       )
+
+       CREATE_TEST(FileMoveOutOfSubLevel,
+               ExpectNotification(CreateFile("base/dir1/file1"),
+                       recursive && !directoriesOnly);
+               ExpectNotification(MoveEntry("base/dir1/file1", "file2"),
+                       recursive && !directoriesOnly);
+               RemoveEntry("file2");
+       )
+
+       CREATE_TEST(DirectoryMoveOutOfSubLevel,
+               ExpectNotification(CreateDirectory("base/dir1/dir2"),
+                       recursive && !filesOnly);
+               ExpectNotification(MoveEntry("base/dir1/dir2", "dir3"),
+                       recursive && !filesOnly);
+               RemoveEntry("dir3");
+       )
+
+       CREATE_TEST(FileMoveToTopLevel,
+               ExpectNotification(CreateFile("base/dir1/file1"),
+                       !directoriesOnly && recursive);
+               ExpectNotification(MoveEntry("base/dir1/file1", "base/file2"),
+                       !directoriesOnly && !pathOnly);
+               ExpectNotification(RemoveEntry("base/file2"),
+                       !directoriesOnly && !pathOnly);
+       )
+
+       CREATE_TEST(DirectoryMoveToTopLevel,
+               ExpectNotification(CreateDirectory("base/dir1/dir2"),
+                       !filesOnly && recursive);
+               ExpectNotification(MoveEntry("base/dir1/dir2", "base/dir3"),
+                       !filesOnly && !pathOnly);
+               ExpectNotification(RemoveEntry("base/dir3"),
+                       !filesOnly && !pathOnly);
+       )
+
+       CREATE_TEST(FileMoveToSubLevel,
+               ExpectNotification(CreateFile("base/file1"),
+                       !directoriesOnly && !pathOnly);
+               ExpectNotification(MoveEntry("base/file1", "base/dir1/file2"),
+                       !directoriesOnly && !pathOnly);
+               ExpectNotification(RemoveEntry("base/dir1/file2"),
+                       !directoriesOnly && recursive);
+       )
+
+       CREATE_TEST(DirectoryMoveToSubLevel,
+               ExpectNotification(CreateDirectory("base/dir2"),
+                       !filesOnly && !pathOnly);
+               ExpectNotification(MoveEntry("base/dir2", "base/dir1/dir3"),
+                       !filesOnly && !pathOnly);
+               ExpectNotification(RemoveEntry("base/dir1/dir3"),
+                       !filesOnly && recursive);
+       )
+
+       CREATE_TEST(NonEmptyDirectoryMoveIntoTopLevel,
+               CreateDirectory("dir2");
+               CreateDirectory("dir2/dir3");
+               CreateDirectory("dir2/dir4");
+               CreateFile("dir2/file1");
+               CreateFile("dir2/dir3/file2");
+               ExpectNotification(MoveEntry("dir2", "base/dir5"),
+                       !filesOnly && !pathOnly);
+               if (recursive && filesOnly) {
+                       ExpectNotifications(MonitoringInfoSet()
+                               .Add(B_ENTRY_CREATED, "base/dir5/file1")
+                               .Add(B_ENTRY_CREATED, "base/dir5/dir3/file2"));
+               }
+       )
+
+       CREATE_TEST(NonEmptyDirectoryMoveIntoSubLevel,
+               CreateDirectory("dir2");
+               CreateDirectory("dir2/dir3");
+               CreateDirectory("dir2/dir4");
+               CreateFile("dir2/file1");
+               CreateFile("dir2/dir3/file2");
+               ExpectNotification(MoveEntry("dir2", "base/dir1/dir5"),
+                       !filesOnly && recursive);
+               if (recursive && filesOnly) {
+                       ExpectNotifications(MonitoringInfoSet()
+                               .Add(B_ENTRY_CREATED, "base/dir1/dir5/file1")
+                               .Add(B_ENTRY_CREATED, 
"base/dir1/dir5/dir3/file2"));
+               }
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(NonEmptyDirectoryMoveOutOfTopLevel,
+               StandardSetup();
+               CreateDirectory("base/dir2");
+               CreateDirectory("base/dir2/dir3");
+               CreateDirectory("base/dir2/dir4");
+               CreateFile("base/dir2/file1");
+               CreateFile("base/dir2/dir3/file2");
+               StartWatching("base");
+               MonitoringInfoSet filesRemoved;
+               if (recursive && filesOnly) {
+                       filesRemoved
+                               .Add(B_ENTRY_REMOVED, "base/dir2/file1")
+                               .Add(B_ENTRY_REMOVED, "base/dir2/dir3/file2");
+               }
+               ExpectNotification(MoveEntry("base/dir2", "dir5"),
+                       !filesOnly && !pathOnly);
+               ExpectNotifications(filesRemoved);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(NonEmptyDirectoryMoveOutOfSubLevel,
+               StandardSetup();
+               CreateDirectory("base/dir1/dir2");
+               CreateDirectory("base/dir1/dir2/dir3");
+               CreateDirectory("base/dir1/dir2/dir4");
+               CreateFile("base/dir1/dir2/file1");
+               CreateFile("base/dir1/dir2/dir3/file2");
+               StartWatching("base");
+               MonitoringInfoSet filesRemoved;
+               if (recursive && filesOnly) {
+                       filesRemoved
+                               .Add(B_ENTRY_REMOVED, "base/dir1/dir2/file1")
+                               .Add(B_ENTRY_REMOVED, 
"base/dir1/dir2/dir3/file2");
+               }
+               ExpectNotification(MoveEntry("base/dir1/dir2", "dir5"),
+                       !filesOnly && recursive);
+               ExpectNotifications(filesRemoved);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(NonEmptyDirectoryMoveToTopLevel,
+               StandardSetup();
+               CreateDirectory("base/dir1/dir2");
+               CreateDirectory("base/dir1/dir2/dir3");
+               CreateDirectory("base/dir1/dir2/dir4");
+               CreateFile("base/dir1/dir2/file1");
+               CreateFile("base/dir1/dir2/dir3/file2");
+               StartWatching("base");
+               MonitoringInfoSet filesMoved;
+               if (recursive && filesOnly) {
+                       filesMoved
+                               .Add(B_ENTRY_REMOVED, "base/dir1/dir2/file1")
+                               .Add(B_ENTRY_REMOVED, 
"base/dir1/dir2/dir3/file2");
+               }
+               ExpectNotification(MoveEntry("base/dir1/dir2", "base/dir5"),
+                       !filesOnly && !pathOnly);
+               if (recursive && filesOnly) {
+                       filesMoved
+                               .Add(B_ENTRY_CREATED, "base/dir5/file1")
+                               .Add(B_ENTRY_CREATED, "base/dir5/dir3/file2");
+               }
+               ExpectNotifications(filesMoved);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(NonEmptyDirectoryMoveToSubLevel,
+               StandardSetup();
+               CreateDirectory("base/dir2");
+               CreateDirectory("base/dir2/dir3");
+               CreateDirectory("base/dir2/dir4");
+               CreateFile("base/dir2/file1");
+               CreateFile("base/dir2/dir3/file2");
+               StartWatching("base");
+               MonitoringInfoSet filesMoved;
+               if (recursive && filesOnly) {
+                       filesMoved
+                               .Add(B_ENTRY_REMOVED, "base/dir2/file1")
+                               .Add(B_ENTRY_REMOVED, "base/dir2/dir3/file2");
+               }
+               ExpectNotification(MoveEntry("base/dir2", "base/dir1/dir5"),
+                       !filesOnly && !pathOnly);
+               if (recursive && filesOnly) {
+                       filesMoved
+                               .Add(B_ENTRY_CREATED, "base/dir1/dir5/file1")
+                               .Add(B_ENTRY_CREATED, 
"base/dir1/dir5/dir3/file2");
+               }
+               ExpectNotifications(filesMoved);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(CreateAncestor,
+               StartWatching("ancestor/base");
+               CreateDirectory("ancestor");
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateAncestor,
+               CreateDirectory("ancestorSibling");
+               StartWatching("ancestor/base");
+               MoveEntry("ancestorSibling", "ancestor");
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateAncestorWithBase,
+               CreateDirectory("ancestorSibling");
+               CreateDirectory("ancestorSibling/base");
+               StartWatching("ancestor/base");
+               MoveEntry("ancestorSibling", "ancestor");
+               MonitoringInfoSet entriesCreated;
+               if (!filesOnly)
+                       entriesCreated.Add(B_ENTRY_CREATED, "ancestor/base");
+               ExpectNotifications(entriesCreated);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateAncestorWithBaseAndFile,
+               CreateDirectory("ancestorSibling");
+               CreateDirectory("ancestorSibling/base");
+               CreateFile("ancestorSibling/base/file1");
+               StartWatching("ancestor/base");
+               MoveEntry("ancestorSibling", "ancestor");
+               MonitoringInfoSet entriesCreated;
+               if (!filesOnly)
+                       entriesCreated.Add(B_ENTRY_CREATED, "ancestor/base");
+               else if (!pathOnly)
+                       entriesCreated.Add(B_ENTRY_CREATED, 
"ancestor/base/file1");
+               ExpectNotifications(entriesCreated);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateAncestorWithBaseAndDirectory,
+               CreateDirectory("ancestorSibling");
+               CreateDirectory("ancestorSibling/base");
+               CreateDirectory("ancestorSibling/base/dir1");
+               CreateFile("ancestorSibling/base/dir1/file1");
+               StartWatching("ancestor/base");
+               MoveEntry("ancestorSibling", "ancestor");
+               MonitoringInfoSet entriesCreated;
+               if (!filesOnly) {
+                       entriesCreated.Add(B_ENTRY_CREATED, "ancestor/base");
+               } else if (recursive)
+                       entriesCreated.Add(B_ENTRY_CREATED, 
"ancestor/base/dir1/file1");
+               ExpectNotifications(entriesCreated);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(CreateBase,
+               CreateDirectory("ancestor");
+               StartWatching("ancestor/base");
+               ExpectNotification(CreateDirectory("ancestor/base"),
+                       !filesOnly);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateBase,
+               CreateDirectory("ancestor");
+               CreateDirectory("ancestor/baseSibling");
+               StartWatching("ancestor/base");
+               ExpectNotification(MoveEntry("ancestor/baseSibling", 
"ancestor/base"),
+                       !filesOnly);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateBaseWithFile,
+               CreateDirectory("ancestor");
+               CreateDirectory("ancestor/baseSibling");
+               CreateFile("ancestor/baseSibling/file1");
+               StartWatching("ancestor/base");
+               ExpectNotification(MoveEntry("ancestor/baseSibling", 
"ancestor/base"),
+                       !filesOnly);
+               MonitoringInfoSet entriesCreated;
+               if (filesOnly && !pathOnly)
+                       entriesCreated.Add(B_ENTRY_CREATED, 
"ancestor/base/file1");
+               ExpectNotifications(entriesCreated);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateBaseWithDirectory,
+               CreateDirectory("ancestor");
+               CreateDirectory("ancestor/baseSibling");
+               CreateDirectory("ancestor/baseSibling/dir1");
+               CreateFile("ancestor/baseSibling/dir1/file1");
+               StartWatching("ancestor/base");
+               ExpectNotification(MoveEntry("ancestor/baseSibling", 
"ancestor/base"),
+                       !filesOnly);
+               MonitoringInfoSet entriesCreated;
+               if (filesOnly && recursive)
+                       entriesCreated.Add(B_ENTRY_CREATED, 
"ancestor/base/dir1/file1");
+               ExpectNotifications(entriesCreated);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveRemoveAncestorWithBaseAndFile,
+               CreateDirectory("ancestor");
+               CreateDirectory("ancestor/base");
+               CreateFile("ancestor/base/file1");
+               StartWatching("ancestor/base");
+               MonitoringInfoSet entriesRemoved;
+               if (!filesOnly)
+                       entriesRemoved.Add(B_ENTRY_REMOVED, "ancestor/base");
+               else if (!pathOnly)
+                       entriesRemoved.Add(B_ENTRY_REMOVED, 
"ancestor/base/file1");
+               MoveEntry("ancestor", "ancestorSibling");
+               ExpectNotifications(entriesRemoved);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveRemoveAncestorWithBaseAndDirectory,
+               CreateDirectory("ancestor");
+               CreateDirectory("ancestor/base");
+               CreateDirectory("ancestor/base/dir1");
+               CreateFile("ancestor/base/dir1/file1");
+               StartWatching("ancestor/base");
+               MonitoringInfoSet entriesRemoved;
+               if (!filesOnly)
+                       entriesRemoved.Add(B_ENTRY_REMOVED, "ancestor/base");
+               else if (recursive)
+                       entriesRemoved.Add(B_ENTRY_REMOVED, 
"ancestor/base/dir1/file1");
+               MoveEntry("ancestor", "ancestorSibling");
+               ExpectNotifications(entriesRemoved);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveRemoveBaseWithFile,
+               CreateDirectory("ancestor");
+               CreateDirectory("ancestor/base");
+               CreateFile("ancestor/base/file1");
+               StartWatching("ancestor/base");
+               MonitoringInfoSet entriesRemoved;
+               if (filesOnly && !pathOnly)
+                       entriesRemoved.Add(B_ENTRY_REMOVED, 
"ancestor/base/file1");
+               ExpectNotification(MoveEntry("ancestor/base", 
"ancestor/baseSibling"),
+                       !filesOnly);
+               ExpectNotifications(entriesRemoved);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveRemoveBaseWithDirectory,
+               CreateDirectory("ancestor");
+               CreateDirectory("ancestor/base");
+               CreateDirectory("ancestor/base/dir1");
+               CreateFile("ancestor/base/dir1/file1");
+               StartWatching("ancestor/base");
+               MonitoringInfoSet entriesRemoved;
+               if (filesOnly && recursive)
+                       entriesRemoved.Add(B_ENTRY_REMOVED, 
"ancestor/base/dir1/file1");
+               ExpectNotification(MoveEntry("ancestor/base", 
"ancestor/baseSibling"),
+                       !filesOnly);
+               ExpectNotifications(entriesRemoved);
+       )
+
+       CREATE_TEST(TouchBase,
+               ExpectNotification(TouchEntry("base"), watchStat && !filesOnly);
+       )
+
+       CREATE_TEST(TouchFileTopLevel,
+               ExpectNotification(TouchEntry("base/file0"),
+                       watchStat && recursive && !directoriesOnly);
+       )
+
+       CREATE_TEST(TouchFileSubLevel,
+               ExpectNotification(TouchEntry("base/dir1/file0.0"),
+                       watchStat && recursive && !directoriesOnly);
+       )
+
+       CREATE_TEST(TouchDirectoryTopLevel,
+               ExpectNotification(TouchEntry("base/dir1"),
+                       watchStat && recursive && !filesOnly);
+       )
+
+       CREATE_TEST(TouchDirectorySubLevel,
+               ExpectNotification(TouchEntry("base/dir1/dir0"),
+                       watchStat && recursive && !filesOnly);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(CreateFileBase,
+               StartWatching("file");
+               ExpectNotification(CreateFile("file"),
+                       !directoriesOnly);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateFileBase,
+               CreateFile("fileSibling");
+               StartWatching("file");
+               ExpectNotification(MoveEntry("fileSibling", "file"),
+                       !directoriesOnly);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(RemoveFileBase,
+               CreateFile("file");
+               StartWatching("file");
+               ExpectNotification(RemoveEntry("file"),
+                       !directoriesOnly);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(MoveRemoveFileBase,
+               CreateFile("file");
+               StartWatching("file");
+               ExpectNotification(MoveEntry("file", "fileSibling"),
+                       !directoriesOnly);
+       )
+
+       CREATE_TEST_WITH_CUSTOM_SETUP(TouchFileBase,
+               CreateFile("file");
+               StartWatching("file");
+               ExpectNotification(TouchEntry("file"),
+                       watchStat && !directoriesOnly);
+       )
+}
+
+
+static void
+run_tests(std::set<BString> testNames, uint32 watchFlags,
+       size_t& totalTests, size_t& succeededTests)
+{
+       std::vector<Test*> tests;
+       create_tests(tests);
+
+       // filter the tests, if test names have been specified
+       size_t testCount = tests.size();
+       if (!testNames.empty()) {
+               for (size_t i = 0; i < testCount;) {
+                       Test* test = tests[i];
+                       std::set<BString>::iterator it = 
testNames.find(test->Name());
+                       if (it != testNames.end()) {
+                               testNames.erase(it);
+                               i++;
+                       } else {
+                               tests.erase(tests.begin() + i);
+                               test->Delete();
+                               testCount--;
+                       }
+               }
+
+               if (!testNames.empty()) {
+                       printf("no such test(s):\n");
+                       for (std::set<BString>::iterator it = testNames.begin();
+                               it != testNames.end(); ++it) {
+                               printf("  %s\n", it->String());
+                               exit(1);
+                       }
+               }
+       }
+
+       printf("\nrunning tests with flags: %s\n",
+               watch_flags_to_string(watchFlags).String());
+
+       int32 longestTestName = 0;
+
+       for (size_t i = 0; i < testCount; i++) {
+               Test* test = tests[i];
+               longestTestName = std::max(longestTestName, 
test->Name().Length());
+       }
+
+       for (size_t i = 0; i < testCount; i++) {
+               Test* test = tests[i];
+               bool terminate = false;
+
+               try {
+                       totalTests++;
+                       test->Init(watchFlags);
+                       printf("  %s: %*s", test->Name().String(),
+                               int(longestTestName - test->Name().Length()), 
"");
+                       fflush(stdout);
+                       test->Do();
+                       printf("SUCCEEDED\n");
+                       succeededTests++;
+               } catch (FatalException& exception) {
+                       printf("FAILED FATALLY\n");
+                       printf("%s\n",
+                               indented_string(exception.Message(), "    
").String());
+                       terminate = true;
+               } catch (TestException& exception) {
+                       printf("FAILED\n");
+                       printf("%s\n",
+                               indented_string(exception.Message(), "    
").String());
+               }
+
+               test->Delete();
+
+               if (terminate)
+                       exit(1);
+       }
+}
+
+
+int
+main(int argc, const char* const* argv)
+{
+       // any args are test names
+       std::set<BString> testNames;
+       for (int i = 1; i < argc; i++)
+               testNames.insert(argv[i]);
+
+       // flags that can be combined arbitrarily
+       const uint32 kFlags[] = {
+               B_WATCH_NAME,
+               B_WATCH_STAT,
+               // not that interesting, since similar to B_WATCH_STAT: 
B_WATCH_ATTR
+               B_WATCH_DIRECTORY,
+               B_WATCH_RECURSIVELY,
+       };
+       const size_t kFlagCount = sizeof(kFlags) / sizeof(kFlags[0]);
+
+       size_t totalTests = 0;
+       size_t succeededTests = 0;
+
+       for (size_t i = 0; i < 1 << kFlagCount; i++) {
+               // construct flags mask
+               uint32 flags = 0;
+               for (size_t k = 0; k < kFlagCount; k++) {
+                       if ((i & (1 << k)) != 0)
+                               flags |= kFlags[k];
+               }
+
+               // run tests -- in recursive mode do that additionally for the 
mutually
+               // B_WATCH_FILES_ONLY and B_WATCH_DIRECTORIES_ONLY flags.
+               run_tests(testNames, flags, totalTests, succeededTests);
+               if ((flags & B_WATCH_RECURSIVELY) != 0) {
+                       run_tests(testNames, flags | B_WATCH_FILES_ONLY, 
totalTests,
+                               succeededTests);
+                       run_tests(testNames, flags | B_WATCH_DIRECTORIES_ONLY, 
totalTests,
+                               succeededTests);
+               }
+       }
+
+       printf("\n");
+       if (succeededTests == totalTests) {
+               printf("ALL TESTS SUCCEEDED\n");
+       } else {
+               printf("%zu of %zu TESTS FAILED\n", totalTests - succeededTests,
+                       totalTests);
+       }
+
+       return 0;
+}

############################################################################

Commit:      04382d496e445d90a3aab7c8752ee9cb59825035
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 27 19:46:29 2013 UTC

BPathMonitor: rewrite

This resolves all issues the test suite uncovered. It should also deal
with hard links correctly, though that hasn't been tested. Still
unsupported are:
* changes due to mounting/unmounting a volume,
* tracking of symlinks in the path components.

----------------------------------------------------------------------------

diff --git a/headers/private/storage/PathMonitor.h 
b/headers/private/storage/PathMonitor.h
index 3309eb2..2a511ec 100644
--- a/headers/private/storage/PathMonitor.h
+++ b/headers/private/storage/PathMonitor.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007-2013, Haiku Inc. All Rights Reserved.
+ * Copyright 2007-2013, Haiku, Inc. All Rights Reserved.
  * Distributed under the terms of the MIT License.
  */
 #ifndef _PATH_MONITOR_H
@@ -9,12 +9,23 @@
 #include <NodeMonitor.h>
 
 
+// Monitoring a path always implies B_WATCH_NAME for the path itself. I.e. even
+// if only B_WATCH_STAT is specified, B_ENTRY_{CREATED,MOVED,REMOVED}
+// notifications are sent when the respective entry is created/moved/removed.
+
 // additional flags (combined with those in NodeMonitor.h)
-#define        B_WATCH_FILES_ONLY                      0x0100
-#define B_WATCH_RECURSIVELY                    0x0200
+#define B_WATCH_RECURSIVELY                    0x0100
+       // Watch not only the entry specified by the path, but also recursively 
all
+       // descendents. A recursive B_WATCH_DIRECTORY is implied, i.e.
+       // B_ENTRY_{CREATED,MOVED,REMOVED} notifications will be sent for any 
entry
+       // change below the given path, unless explicitly suppressed by
+       // B_WATCH_{FILES,DIRECTORIES}_ONLY.
+#define        B_WATCH_FILES_ONLY                      0x0200
+       // A notification will only be sent when the node it concerns is not a
+       // directory. No effect in non-recursive mode.
 #define        B_WATCH_DIRECTORIES_ONLY        0x0400
-// NOTE: B_WATCH_RECURSIVELY usually implies to watch for file changes as well,
-// if that is not desired, add B_WATCH_DIRECTORIES_ONLY to the flags.
+       // A notification will only be sent when the node it concerns is a
+       // directory. No effect in non-recursive mode.
 
 #define B_PATH_MONITOR         '_PMN'
 
@@ -69,4 +80,7 @@ public:
 }      // namespace BPrivate
 
 
+using BPrivate::BPathMonitor;
+
+
 #endif // _PATH_MONITOR_H
diff --git a/src/kits/storage/Jamfile b/src/kits/storage/Jamfile
index 6a053c7..dba6e60 100644
--- a/src/kits/storage/Jamfile
+++ b/src/kits/storage/Jamfile
@@ -2,7 +2,7 @@ SubDir HAIKU_TOP src kits storage ;
 
 SetSubDirSupportedPlatforms haiku libbe_test ;
 
-UsePrivateHeaders app libroot shared storage ;
+UsePrivateHeaders app kernel libroot shared storage ;
 UsePrivateSystemHeaders ;
 
 # for libbe_test
diff --git a/src/kits/storage/PathMonitor.cpp b/src/kits/storage/PathMonitor.cpp
index a782171..1167c34 100644
--- a/src/kits/storage/PathMonitor.cpp
+++ b/src/kits/storage/PathMonitor.cpp
@@ -1,10 +1,11 @@
 /*
- * Copyright 2007-2013, Haiku Inc. All Rights Reserved.
+ * Copyright 2007-2013, Haiku, Inc. All Rights Reserved.
  * Distributed under the terms of the MIT License.
  *
  * Authors:
  *             Axel Dörfler, axeld@xxxxxxxxxxxxxxxx
- *             Stephan Aßmus <superstippi@xxxxxx>
+ *             Stephan Aßmus, superstippi@xxxxxx
+ *             Ingo Weinhold, ingo_weinhold@xxxxxx
  */
 
 
@@ -26,217 +27,847 @@
 #include <Path.h>
 #include <String.h>
 
+#include <AutoDeleter.h>
+#include <NotOwningEntryRef.h>
 #include <ObjectList.h>
+#include <util/OpenHashTable.h>
+#include <util/SinglyLinkedList.h>
 
 
 #undef TRACE
 //#define TRACE_PATH_MONITOR
 #ifdef TRACE_PATH_MONITOR
-#      define TRACE(x...) debug_printf(x)
+#      define TRACE(...) debug_printf("BPathMonitor: " __VA_ARGS__)
 #else
-#      define TRACE(x...) ;
+#      define TRACE(...) ;
 #endif
 
 
-using namespace BPrivate;
-using namespace std;
-using std::nothrow; // TODO: Remove this line if the above line is enough.
+// TODO: Support symlink components in the path.
+// TODO: Support mounting/unmounting of volumes in path components and within
+// the watched path tree.
 
 
-// TODO: Use optimizations where stuff is already known to avoid iterating
-// the watched directory and files set too often.
-
 #define WATCH_NODE_FLAG_MASK   0x00ff
 
-namespace BPrivate {
 
-struct FileEntry {
-       entry_ref       ref;
-       ino_t           node;
-};
+namespace {
+
+
+struct Directory;
+struct Node;
+struct WatcherHashDefinition;
+typedef BOpenHashTable<WatcherHashDefinition> WatcherMap;
+
+
+static pthread_once_t sInitOnce = PTHREAD_ONCE_INIT;
+static WatcherMap* sWatchers = NULL;
+static BLocker* sLocker = NULL;
+static BLooper* sLooper = NULL;
+static BPathMonitor::BWatchingInterface* sDefaultWatchingInterface = NULL;
+static BPathMonitor::BWatchingInterface* sWatchingInterface = NULL;
+
+
+//     #pragma mark -
+
+
+/*! Returns empty path, if either \a parent or \a subPath is empty or an
+       allocation fails.
+ */
+static BString
+make_path(const BString& parent, const char* subPath)
+{
+       BString path = parent;
+       int32 length = path.Length();
+       if (length == 0 || subPath[0] == '\0')
+               return BString();
+
+       if (parent.ByteAt(length - 1) != '/') {
+               path << '/';
+               if (path.Length() < ++length)
+                       return BString();
+       }
+
+       path << subPath;
+       if (path.Length() <= length)
+               return BString();
+       return path;
+}
+
 
-#if __GNUC__ > 3
-       bool operator<(const FileEntry& a, const FileEntry& b);
-       class FileEntryLess : public binary_function<FileEntry, FileEntry, bool>
+//     #pragma mark - Ancestor
+
+
+class Ancestor {
+public:
+       Ancestor(Ancestor* parent, const BString& path, size_t 
pathComponentOffset)
+               :
+               fParent(parent),
+               fChild(NULL),
+               fPath(path),
+               fEntryRef(-1, -1, fPath.String() + pathComponentOffset),
+               fNodeRef(),
+               fWatchingFlags(0),
+               fIsDirectory(false)
        {
-               public:
-               bool operator() (const FileEntry& a, const FileEntry& b) const
-               {
-                       return a < b;
+               if (pathComponentOffset == 0) {
+                       // must be "/"
+                       fEntryRef.SetTo(-1, -1, ".");

[ *** diff truncated: 2720 lines dropped *** ]


############################################################################

Commit:      c143884fdf03ec3a8b7a89c88baa477f8629395c
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Thu Jun 27 19:54:43 2013 UTC

Use incorrect use of BPathMonitor in input/midi/net server

The B_ENTRY_* constants aren't valid watch flags.

----------------------------------------------------------------------------


Other related posts:

  • » [haiku-commits] BRANCH HaikuPM-github.package-management [c143884] src/kits/storage src/tests/kits/storage/testapps src/kits headers/private/storage - HaikuPM-github . package-management