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

List:       kde-core-devel
Subject:    [PATCH] kdirwatch can not detect link change quite well
From:       sycao <sycao () redflag-linux ! com>
Date:       2008-11-25 3:16:23
Message-ID: 492B6E07.80201 () redflag-linux ! com
[Download RAW message or body]

Hi, all
    I don't know where is best ML to post, so I post to both kde-devel
and kde-core-devel.
the problem comes up  when  I  traced the problem when using kcmshell4
clock
to set timezone, which is when you changed timezone second time,
the setting just lost. Leo peng and I have tried to fix it, and I found
the real problem is that
KDirWatch loses watch on /etc/localtime ( in ktimezoned ).

   I can reproduce the watch lose very easily:
   # inotifywait -m /etc/localtime
   # zic -l Asia/Shanghai
   # zic -l Asia/Muscat
   # touch /etc/localtime

After Change timezone to Muscat, subsequent changes to /etc/localtime
won't be watched. because KDirWatch use inotify, which monitoring file
with respect to inode , not dir entry. zic relink ( hardlink )
/etc/localtime, so inotify just tells attrib change ( st_nlink change )
of the original file.
I try hard to fix the problem and it works right now. please check it if
there are any problems.

the patches works both for 4.1.3 and trunk
  best regards,
    Siyuan Cao


["kio-kdirwatch-hardlink.patch" (text/plain)]

diff --exclude='*~' --exclude='*.orig' -Nur kdelibs-4.1.2/kio/kio/kdirwatch.cpp \
                kdelibs-4.1.2-new/kio/kio/kdirwatch.cpp
--- kdelibs-4.1.2/kio/kio/kdirwatch.cpp	2008-07-15 23:50:57.000000000 +0400
+++ kdelibs-4.1.2-new/kio/kio/kdirwatch.cpp	2008-11-25 06:28:10.000000000 +0400
@@ -47,6 +47,7 @@
 
 #include <sys/stat.h>
 #include <assert.h>
+#include <errno.h>
 #include <QtCore/QDir>
 #include <QtCore/QFile>
 #include <QtCore/QSocketNotifier>
@@ -111,6 +112,7 @@
     statEntries( 0 ),
     m_ref( 0 ),
     delayRemove( false ),
+	m_delayedRelinkedEntry( NULL ),
     rescan_all( false ),
     rescan_timer()
 {
@@ -668,6 +670,7 @@
 #endif
     e->m_status = Normal;
     e->m_nlink = stat_buf.st_nlink;
+	e->m_ino = stat_buf.st_ino;
   }
   else {
     e->isDir = isDir;
@@ -949,6 +952,7 @@
 #endif
         e->m_status = Normal;
         e->m_nlink = stat_buf.st_nlink;
+		e->m_ino = stat_buf.st_ino;
       }
       else {
         e->m_ctime = invalid_ctime;
@@ -1054,15 +1058,39 @@
 #endif
       e->m_status = Normal;
       e->m_nlink = stat_buf.st_nlink;
+	  e->m_ino = stat_buf.st_ino;
       return Created;
     }
 
+	if ( e->m_ctime != invalid_ctime && stat_buf.st_ino != e->m_ino ) {
+	  kDebug(7001) << "checked hard link change for " << e->path;
+	  int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF
+		|IN_DONT_FOLLOW|IN_MODIFY|IN_ATTRIB;
+	  
+	  inotify_rm_watch (m_inotify_fd, e->wd);
+	  int wd = inotify_add_watch( m_inotify_fd, QFile::encodeName( e->path ), mask );
+	  if ( wd == -1 ) {
+		kDebug(7001) << "re-watch failed with errno " << errno;
+		m_delayedRelinkedEntry = e;
+		QTimer::singleShot( 200, this, SLOT(slotRewatchDelayed()) );
+		return Changed;
+		
+	  } else {
+		e->wd = wd;
+		e->m_ctime = stat_buf.st_ctime;
+		e->m_nlink = stat_buf.st_nlink;
+		e->m_ino = stat_buf.st_ino;
+		kDebug(7001) << "re-watch for " << e->path << " finished";
+		return Changed;
+	  }
+	}
+	
 #ifdef Q_OS_WIN
     stat_buf.st_ctime = stat_buf.st_mtime;
 #endif
     if ( (e->m_ctime != invalid_ctime) &&
-          ((stat_buf.st_ctime != e->m_ctime) ||
-          (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
+		 ((stat_buf.st_ctime != e->m_ctime) ||
+		  (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
       e->m_ctime = stat_buf.st_ctime;
       e->m_nlink = stat_buf.st_nlink;
       return Changed;
@@ -1139,6 +1167,44 @@
   }
 }
 
+void KDirWatchPrivate::slotRewatchDelayed()
+{
+  if ( m_delayedRelinkedEntry ) {
+	kDebug(7001) << "try re-watch for " << m_delayedRelinkedEntry->path;
+
+	KDE_struct_stat stat_buf;
+	QByteArray tpath = QFile::encodeName(m_delayedRelinkedEntry->path);
+	bool try_again = false;
+	int ret = 0;
+	do {
+	  ret = KDE_stat(tpath, &stat_buf);
+	  if ( ret == -1 && errno == ENOENT ) {
+		usleep( 100 );
+		try_again = true;
+	  } else
+		try_again = false;
+	} while ( try_again );
+
+	if ( ret )
+	  return;
+	
+	int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF
+	  |IN_DONT_FOLLOW|IN_MODIFY|IN_ATTRIB;
+	inotify_rm_watch( m_inotify_fd, m_delayedRelinkedEntry->wd );
+	ret = inotify_add_watch( m_inotify_fd, QFile::encodeName( \
m_delayedRelinkedEntry->path ), +							   mask);
+	if ( !ret ) {
+	  m_delayedRelinkedEntry->wd = ret;
+	  emitEvent( m_delayedRelinkedEntry, Changed );
+	  kDebug(7001) << "re-watch for " << m_delayedRelinkedEntry->path << " successed";
+	} else {
+	  kDebug(7001) << "re-watch for " << m_delayedRelinkedEntry->path << " failed";
+	}
+	
+	m_delayedRelinkedEntry = NULL;
+  }
+}
+
 // Remove entries which were marked to be removed
 void KDirWatchPrivate::slotRemoveDelayed()
 {
@@ -1554,7 +1620,24 @@
 
 void KDirWatch::addFile( const QString& _path )
 {
-  if (d) d->addEntry(this, _path, 0, false);
+  if ( !d )
+	return;
+  
+  //TODO: detect link change, ino change
+  KDirWatchPrivate::Entry* e = d->entry(_path);
+  if ( e ) {
+	KDE_struct_stat lstat_buf;
+	QByteArray tpath (QFile::encodeName(_path));
+	if ( KDE_lstat(tpath, &lstat_buf) == 0 ) {
+	  if ( S_ISREG(lstat_buf.st_mode) && e->m_status != KDirWatchPrivate::NonExistent 
+		   && lstat_buf.st_ino != e->m_ino ) {
+		kDebug(7001) << "detected an exist entry with different inode, delete watch \
first"; +		d->removeEntry(this, _path, 0);
+	  }
+	}
+  }
+	  
+  d->addEntry(this, _path, 0, false);
 }
 
 QDateTime KDirWatch::ctime( const QString &_path ) const
--- kdelibs-4.1.2/kio/kio/kdirwatch_p.h	2008-07-15 23:50:57.000000000 +0400
+++ kdelibs-4.1.2-new/kio/kio/kdirwatch_p.h	2008-11-25 05:04:30.000000000 +0400
@@ -136,6 +136,8 @@
     time_t m_ctime;
     // the last observed link count
     int m_nlink;
+	// last observed inode ( considering hard link changes )
+	ino_t m_ino;
     entryStatus m_status;
     entryMode m_mode;
     bool isDir;
@@ -199,7 +201,8 @@
   void inotifyEventReceived(); // for inotify
   void slotRemoveDelayed();
   void fswEventReceived(const QString &path);  // for QFileSystemWatcher
-
+  void slotRewatchDelayed();
+  
 public:
   QTimer timer;
   EntryMap m_mapEntries;
@@ -215,6 +218,8 @@
   QSet<Entry *> removeList;
   bool delayRemove;
 
+  Entry *m_delayedRelinkedEntry;
+  
   bool rescan_all;
   QTimer rescan_timer;
 



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

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