[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    branches/KDE/4.5/kdebase/runtime/nepomuk/services/filewatch
From:       Sebastian Trueg <sebastian () trueg ! de>
Date:       2010-11-23 13:16:36
Message-ID: 20101123131636.CF32FAC8A2 () svn ! kde ! org
[Download RAW message or body]

SVN commit 1199996 by trueg:

Backport of two important fixes:
* Fixed KInotify which sometimes thought / was deleted which resulted in removal of \
                all indexed files from the index.
* Do not delete all graphs which contain information about removed files. Instead \
remove the resources since the  graphs might also contain information about other \
resources.

BUG: 251446


 M  +1 -0      CMakeLists.txt  
 AM            invalidfileresourcecleaner.cpp   [License: GPL (v2/3)]
 AM            invalidfileresourcecleaner.h   [License: GPL (v2/3)]
 M  +67 -45    kinotify.cpp  
 M  +2 -2      kinotify.h  
 M  +3 -0      metadatamover.cpp  
 M  +12 -68    nepomukfilewatch.cpp  
 M  +1 -1      nepomukfilewatch.h  


--- branches/KDE/4.5/kdebase/runtime/nepomuk/services/filewatch/CMakeLists.txt \
#1199995:1199996 @@ -16,6 +16,7 @@
   nepomukfilewatch.cpp
   metadatamover.cpp
   updaterequest.cpp
+  invalidfileresourcecleaner.cpp
   )
 
 qt4_add_dbus_interface(SRCS ../../interfaces/org.kde.nepomuk.Strigi.xml \
                strigiserviceinterface)
--- branches/KDE/4.5/kdebase/runtime/nepomuk/services/filewatch/kinotify.cpp \
#1199995:1199996 @@ -44,13 +44,21 @@
     // is a variable length array
     const int EVENT_BUFFER_SIZE = EVENT_STRUCT_SIZE + 1024*16;
 
-    QByteArray normalizePath( const QByteArray& path ) {
+    QByteArray stripTrailingSlash( const QByteArray& path ) {
         QByteArray p( path );
         if ( p.endsWith( '/' ) )
             p.truncate( p.length()-1 );
         return p;
     }
+
+    QByteArray concatPath( const QByteArray& p1, const QByteArray& p2 ) {
+        QByteArray p(p1);
+        if( p.isEmpty() || p[p.length()-1] != '/' )
+            p.append('/');
+        p.append(p2);
+        return p;
 }
+}
 
 class KInotify::Private
 {
@@ -66,8 +74,9 @@
         close();
     }
 
-    QHash<int, QString> cookies;
-    QHash<int, QByteArray> pathHash;
+    QHash<int, QByteArray> cookies;
+    QHash<int, QByteArray> watchPathHash;
+    QHash<QByteArray, int> pathWatchHash;
 
     /// queue of paths to install watches for
     QQueue<QByteArray> pathsToWatch;
@@ -97,13 +106,16 @@
     }
 
     bool addWatch( const QByteArray& path ) {
+        kDebug() << path;
         // we always need the unmount event to maintain our path hash
         const int mask = mode|flags|EventUnmount;
 
         int wd = inotify_add_watch( inotify(), path.data(), mask );
         if ( wd > 0 ) {
 //            kDebug() << "Successfully added watch for" << path << \
                pathHash.count();
-            pathHash.insert( wd, normalizePath( path ) );
+            QByteArray normalized = stripTrailingSlash( path );
+            watchPathHash.insert( wd, normalized );
+            pathWatchHash.insert( normalized, wd );
             return true;
         }
         else {
@@ -123,7 +135,7 @@
         if ( !addWatch( path ) )
             return false;
 
-        int len = offsetof(struct dirent, d_name) +
+        const int len = offsetof(struct dirent, d_name) +
                   pathconf(path.data(), _PC_NAME_MAX) + 1;
         struct dirent* entry = ( struct dirent* )new char[len];
 
@@ -144,7 +156,7 @@
                      qstrcmp( entry->d_name, "." ) &&
                      qstrcmp( entry->d_name, ".." ) ) {
                     bool isDir = true;
-                    QByteArray subDir = path + '/' + QByteArray::fromRawData( \
entry->d_name, qstrlen( entry->d_name ) ); +                    QByteArray subDir = \
concatPath( path, QByteArray::fromRawData( entry->d_name, qstrlen( entry->d_name ) ) \
);  if ( entry->d_type == DT_UNKNOWN ) {
                         struct stat buf;
                         lstat( subDir.data(), &buf );
@@ -169,7 +181,8 @@
     }
 
     void removeWatch( int wd ) {
-        pathHash.remove( wd );
+        kDebug() << wd << watchPathHash[wd];
+        pathWatchHash.remove( watchPathHash.take( wd ) );
         inotify_rm_watch( inotify(), wd );
     }
 
@@ -248,15 +261,9 @@
 
 bool KInotify::watchingPath( const QString& path ) const
 {
-    QByteArray p( normalizePath( QFile::encodeName( path ) ) );
-    QHash<int, QByteArray>::const_iterator end = d->pathHash.constEnd();
-    for ( QHash<int, QByteArray>::const_iterator it = d->pathHash.constBegin();
-          it != end; ++it ) {
-        if ( it.value() == p )
-            return true;
+    const QByteArray p( stripTrailingSlash( QFile::encodeName( path ) ) );
+    return d->pathWatchHash.contains(p);
     }
-    return false;
-}
 
 
 bool KInotify::addWatch( const QString& path, WatchEvents mode, WatchFlags flags )
@@ -271,15 +278,16 @@
 }
 
 
-// TODO: do this more efficiently
 bool KInotify::removeWatch( const QString& path )
 {
+    kDebug() << path;
     QByteArray encodedPath = QFile::encodeName( path );
-    QHash<int, QByteArray>::iterator it = d->pathHash.begin();
-    while ( it != d->pathHash.end() ) {
+    QHash<int, QByteArray>::iterator it = d->watchPathHash.begin();
+    while ( it != d->watchPathHash.end() ) {
         if ( it.value().startsWith( encodedPath ) ) {
             inotify_rm_watch( d->inotify(), it.key() );
-            it = d->pathHash.erase( it );
+            d->pathWatchHash.remove(it.value());
+            it = d->watchPathHash.erase( it );
         }
         else {
             ++it;
@@ -292,61 +300,64 @@
 void KInotify::slotEvent( int socket )
 {
     // read at least one event
-    int len = read( socket, d->eventBuffer, EVENT_BUFFER_SIZE );
+    const int len = read( socket, d->eventBuffer, EVENT_BUFFER_SIZE );
     int i = 0;
     while ( i < len && len-i >= EVENT_STRUCT_SIZE  ) {
-        struct inotify_event* event = ( struct inotify_event* )&d->eventBuffer[i];
+        const struct inotify_event* event = ( struct inotify_event* \
)&d->eventBuffer[i];  
-        QByteArray encodedPath = QByteArray::fromRawData( event->name, event->len );
+        QByteArray path;
 
-        if ( encodedPath[0] != '/' ) {
-            encodedPath = d->pathHash.value( event->wd ) + '/' + encodedPath;
+        // the event name only contains an interesting value if we get an event for \
a file/folder inside +        // a watched folder. Otherwise we should ignore it
+        if ( event->mask & (EventDeleteSelf|EventMoveSelf) ) {
+            path = d->watchPathHash.value( event->wd );
         }
+        else {
+            // we cannot use event->len here since it contains the size of the \
buffer and not the length of the string +            const QByteArray eventName = \
QByteArray::fromRawData( event->name, qstrlen(event->name) ); +            const \
QByteArray hashedPath = d->watchPathHash.value( event->wd ); +            path = \
concatPath( hashedPath, eventName ); +        }
 
-        QString path = QFile::decodeName( encodedPath );
-
         // now signal the event
         if ( event->mask & EventAccess) {
 //            kDebug() << path << "EventAccess";
-            emit accessed( path );
+            emit accessed( QFile::decodeName(path) );
         }
         if ( event->mask & EventAttributeChange ) {
 //            kDebug() << path << "EventAttributeChange";
-            emit attributeChanged( path );
+            emit attributeChanged( QFile::decodeName(path) );
         }
         if ( event->mask & EventCloseWrite ) {
 //            kDebug() << path << "EventCloseWrite";
-            emit closedWrite( path );
+            emit closedWrite( QFile::decodeName(path) );
         }
         if ( event->mask & EventCloseRead ) {
 //            kDebug() << path << "EventCloseRead";
-            emit closedRead( path );
+            emit closedRead( QFile::decodeName(path) );
         }
         if ( event->mask & EventCreate ) {
 //            kDebug() << path << "EventCreate";
             if ( event->mask & IN_ISDIR ) {
                 // FIXME: store the mode and flags somewhere
-                addWatch( encodedPath, d->mode, d->flags );
+                addWatch( path, d->mode, d->flags );
             }
-            emit created( path );
+            emit created( QFile::decodeName(path) );
         }
         if ( event->mask & EventDelete ) {
 //            kDebug() << path << "EventDelete";
-            if ( event->mask & IN_ISDIR ) {
-                d->removeWatch( event->wd );
+            // we watch all folders recursively. Thus, folder removing is reported \
in DeleteSelf. +            if( !(event->mask & IN_ISDIR) )
+                emit deleted( QFile::decodeName(path), false );
             }
-            emit deleted( path );
-        }
         if ( event->mask & EventDeleteSelf ) {
 //            kDebug() << path << "EventDeleteSelf";
-            if ( event->mask & IN_ISDIR ) {
                 d->removeWatch( event->wd );
+            emit deleted( QFile::decodeName(path), event->mask & IN_ISDIR );
             }
-            emit deleted( path );
-        }
         if ( event->mask & EventModify ) {
 //            kDebug() << path << "EventModify";
-            emit modified( path );
+            emit modified( QFile::decodeName(path) );
         }
         if ( event->mask & EventMoveSelf ) {
 //            kDebug() << path << "EventMoveSelf";
@@ -358,10 +369,21 @@
         if ( event->mask & EventMoveTo ) {
             // check if we have a cookie for this one
             if ( d->cookies.contains( event->cookie ) ) {
-                QString oldPath = d->cookies[event->cookie];
-                d->cookies.remove( event->cookie );
+                const QByteArray oldPath = d->cookies.take(event->cookie);
+
+                // update the path cache
+                if( event->mask & IN_ISDIR ) {
+                    QHash<QByteArray, int>::iterator it = \
d->pathWatchHash.find(oldPath); +                    if( it != d->pathWatchHash.end() \
) { +                        kDebug() << oldPath << path;
+                        const int wd = it.value();
+                        d->watchPathHash[wd] = path;
+                        d->pathWatchHash.erase(it);
+                        d->pathWatchHash.insert( path, wd );
+                    }
+                }
 //                kDebug() << oldPath << "EventMoveTo" << path;
-                emit moved( oldPath, path );
+                emit moved( QFile::encodeName(oldPath), QFile::decodeName(path) );
             }
             else {
                 kDebug() << "No cookie for move information of" << path;
@@ -369,14 +391,14 @@
         }
         if ( event->mask & EventOpen ) {
 //            kDebug() << path << "EventOpen";
-            emit opened( path );
+            emit opened( QFile::decodeName(path) );
         }
         if ( event->mask & EventUnmount ) {
 //            kDebug() << path << "EventUnmount. removing from path hash";
             if ( event->mask & IN_ISDIR ) {
                 d->removeWatch( event->wd );
             }
-            emit unmounted( path );
+            emit unmounted( QFile::decodeName(path) );
         }
         if ( event->mask & EventQueueOverflow ) {
             // This should not happen since we grab all events as soon as they \
                arrive
--- branches/KDE/4.5/kdebase/runtime/nepomuk/services/filewatch/kinotify.h \
#1199995:1199996 @@ -32,7 +32,7 @@
     Q_OBJECT
 
 public:
-    KInotify( QObject* parent );
+    KInotify( QObject* parent = 0 );
     ~KInotify();
 
     /**
@@ -127,7 +127,7 @@
      * Emitted if a watched file or folder has been deleted.
      * This includes files in watched foldes (KInotify::EventDelete and \
                KInotify::EventDeleteSelf)
      */
-    void deleted( const QString& file );
+    void deleted( const QString& file, bool isFolder );
 
     /**
      * Emitted if a watched file is modified (KInotify::EventModify)
--- branches/KDE/4.5/kdebase/runtime/nepomuk/services/filewatch/metadatamover.cpp \
#1199995:1199996 @@ -176,12 +176,14 @@
         kDebug() << "empty path. Looks like a bug somewhere...";
     }
     else {
+        const bool isFolder = url.url().endsWith('/');
         Resource res( url );
         if ( res.exists() ) {
             kDebug() << "removing metadata for file" << url << "with resource URI" \
<< res.resourceUri();  res.remove();
         }
 
+        if( isFolder ) {
         //
         // Recursively remove children
         // We cannot use the nie:isPartOf relation since only children could have \
metadata. Thus, we do a regex @@ -217,6 +219,7 @@
         }
     }
 }
+}
 
 
 void Nepomuk::MetadataMover::updateMetadata( const KUrl& from, const KUrl& to, bool \
                includeChildren )
--- branches/KDE/4.5/kdebase/runtime/nepomuk/services/filewatch/nepomukfilewatch.cpp \
#1199995:1199996 @@ -19,6 +19,7 @@
 #include "nepomukfilewatch.h"
 #include "metadatamover.h"
 #include "strigiserviceinterface.h"
+#include "invalidfileresourcecleaner.h"
 #include "nie.h"
 
 #ifdef BUILD_KINOTIFY
@@ -45,69 +46,6 @@
 
 NEPOMUK_EXPORT_SERVICE( Nepomuk::FileWatch, "nepomukfilewatch")
 
-
-namespace {
-
-    class RemoveInvalidThread : public QThread {
-    public :
-        RemoveInvalidThread(QObject* parent = 0);
-        ~RemoveInvalidThread();
-        void run();
-
-    private:
-        bool m_stopped;
-    };
-
-    RemoveInvalidThread::RemoveInvalidThread(QObject* parent)
-        : QThread(parent),
-          m_stopped( false )
-    {
-        connect( this, SIGNAL(finished()), this, SLOT(deleteLater()) );
-    }
-
-    RemoveInvalidThread::~RemoveInvalidThread()
-    {
-        // gently terminate the thread
-        m_stopped = true;
-        wait();
-    }
-
-    void RemoveInvalidThread::run()
-    {
-        kDebug() << "Searching for invalid local file entries";
-        //
-        // Since the removal of the graphs could intefere with the iterator and \
                result
-        // in jumping of rows (worst case) we cache all graphs to remove
-        //
-        QList<Soprano::Node> graphsToRemove;
-        QString query = QString::fromLatin1( "select distinct ?g ?url where { "
-                                             "?r %1 ?url. "
-                                             "FILTER(regex(str(?url), 'file://')) . \
                "
-                                             "graph ?g { ?r ?p ?o. } }" )
-                        .arg( Soprano::Node::resourceToN3( \
                Nepomuk::Vocabulary::NIE::url() ) );
-        Soprano::QueryResultIterator it = \
Nepomuk::ResourceManager::instance()->mainModel()->executeQuery( query, \
                Soprano::Query::QueryLanguageSparql );
-
-        while( it.next() && !m_stopped ) {
-            QUrl url( it["url"].uri() );
-            QString file = url.toLocalFile();
-
-            if( !file.isEmpty() && !QFile::exists(file) ) {
-                kDebug() << "REMOVING " << file;
-                graphsToRemove << it["g"];
-            }
-        }
-
-        Q_FOREACH( const Soprano::Node& g, graphsToRemove ) {
-            if ( m_stopped )
-                break;
-            Nepomuk::ResourceManager::instance()->mainModel()->removeContext( g );
-        }
-        kDebug() << "Done searching for invalid local file entries";
-    }
-
-}
-
-
 Nepomuk::FileWatch::FileWatch( QObject* parent, const QList<QVariant>& )
     : Service( parent )
 {
@@ -126,8 +64,8 @@
 
     connect( m_dirWatch, SIGNAL( moved( const QString&, const QString& ) ),
              this, SLOT( slotFileMoved( const QString&, const QString& ) ) );
-    connect( m_dirWatch, SIGNAL( deleted( const QString& ) ),
-             this, SLOT( slotFileDeleted( const QString& ) ) );
+    connect( m_dirWatch, SIGNAL( deleted( const QString&, bool ) ),
+             this, SLOT( slotFileDeleted( const QString&, bool ) ) );
     connect( m_dirWatch, SIGNAL( created( const QString& ) ),
              this, SLOT( slotFileCreated( const QString& ) ) );
     connect( m_dirWatch, SIGNAL( modified( const QString& ) ),
@@ -149,7 +87,7 @@
     connectToKDirWatch();
 #endif
 
-    (new RemoveInvalidThread(this))->start();
+    (new InvalidFileResourceCleaner(this))->start();
 }
 
 
@@ -192,14 +130,20 @@
 
     kDebug() << urls;
 
+    if(!urls.isEmpty())
     m_metadataMover->removeFileMetadata( urls );
 }
 
 
-void Nepomuk::FileWatch::slotFileDeleted( const QString& urlString )
+void Nepomuk::FileWatch::slotFileDeleted( const QString& urlString, bool isDir )
 {
-    slotFilesDeleted( QStringList( urlString ) );
+    // Directories must always end with a trailing slash '/'
+    QString url = urlString;
+    if( isDir && url[ url.length() - 1 ] != '/') {
+        url.append('/');
 }
+    slotFilesDeleted( QStringList( url ) );
+}
 
 
 void Nepomuk::FileWatch::slotFileCreated( const QString& path )
--- branches/KDE/4.5/kdebase/runtime/nepomuk/services/filewatch/nepomukfilewatch.h \
#1199995:1199996 @@ -59,7 +59,7 @@
 
     private Q_SLOTS:
         void slotFileMoved( const QString& from, const QString& to );
-        void slotFileDeleted( const QString& path );
+        void slotFileDeleted( const QString& path, bool isDir );
         void slotFilesDeleted( const QStringList& path );
         void slotFileCreated( const QString& );
         void slotFileModified( const QString& );


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic