generic: use QFilesystemWatcher instead of inotify directly

this allows for less complexity and more abstraction offloaded to
Qt itself which supports dnotify, inotify, kqueue and fsevents.

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2016-04-29 15:25:11 +00:00
parent eefd1b6b66
commit 3d12c6d2e8
8 changed files with 36 additions and 529 deletions

View file

@ -21,9 +21,6 @@ configure_file(
# Configure checks for kdirwatch
macro_bool_to_01(FAM_FOUND HAVE_FAM)
check_include_files(sys/inotify.h SYS_INOTIFY_H_FOUND)
macro_bool_to_01(SYS_INOTIFY_H_FOUND HAVE_SYS_INOTIFY_H)
# Generate io/config-kdirwatch.h
configure_file(
io/config-kdirwatch.h.cmake

View file

@ -1,3 +1 @@
#cmakedefine HAVE_FAM 1
#cmakedefine HAVE_SYS_INOTIFY_H 1

View file

@ -88,17 +88,8 @@ static KDirWatchPrivate* createPrivate() {
static KDirWatch::Method methodFromString(const QString& method) {
if (method == QLatin1String("Fam")) {
return KDirWatch::FAM;
} else if (method == QLatin1String("Stat")) {
return KDirWatch::Stat;
} else if (method == QLatin1String("QFSWatch")) {
return KDirWatch::QFSWatch;
} else {
#ifdef Q_OS_LINUX
// inotify supports delete+recreate+modify, which QFSWatch doesn't support
return KDirWatch::INotify;
#else
return KDirWatch::QFSWatch;
#endif
}
}
@ -108,10 +99,6 @@ static const char* methodToString(KDirWatch::Method method)
switch (method) {
case KDirWatch::FAM:
return "Fam";
case KDirWatch::INotify:
return "INotify";
case KDirWatch::Stat:
return "Stat";
case KDirWatch::QFSWatch:
return "QFSWatch";
default:
@ -164,7 +151,7 @@ KDirWatchPrivate::KDirWatchPrivate()
m_nfsPollInterval = config.readEntry("NFSPollInterval", 5000);
m_PollInterval = config.readEntry("PollInterval", 500);
QString method = config.readEntry("PreferredMethod", "inotify");
QString method = config.readEntry("PreferredMethod", "QFSWatch");
m_preferredMethod = methodFromString(method);
// The nfs method defaults to the normal (local) method
@ -172,9 +159,7 @@ KDirWatchPrivate::KDirWatchPrivate()
QList<QByteArray> availableMethods;
availableMethods << "Stat";
// used for FAM and inotify
// used for FAM
rescan_timer.setObjectName(QString::fromLatin1("KDirWatchPrivate::rescan_timer"));
rescan_timer.setSingleShot( true );
connect(&rescan_timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
@ -195,50 +180,11 @@ KDirWatchPrivate::KDirWatchPrivate()
}
#endif
#ifdef HAVE_SYS_INOTIFY_H
supports_inotify = true;
m_inotify_fd = inotify_init();
if ( m_inotify_fd <= 0 ) {
kDebug(7001) << "Can't use Inotify, kernel doesn't support it";
supports_inotify = false;
}
{
struct utsname uts;
int major, minor, patch;
if (uname(&uts) < 0) {
supports_inotify = false;
kDebug(7001) << "Unable to get uname";
} else if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
supports_inotify = false;
kDebug(7001) << "The version is malformed: " << uts.release;
} else if(major == 2 && minor == 6) { // If it is 2.6 check further...
if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3) {
supports_inotify = false;
kDebug() << "Detected 2.6 kernel but can't know more: " << uts.release;
} else if (major * 1000000 + minor * 1000 + patch < 2006014 ){
supports_inotify = false;
kDebug(7001) << "Can't use INotify, Linux kernel too old " << uts.release;
}
}
}
kDebug(7001) << "INotify available: " << supports_inotify;
if ( supports_inotify ) {
availableMethods << "INotify";
(void)fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
mSn = new QSocketNotifier( m_inotify_fd, QSocketNotifier::Read, this );
connect( mSn, SIGNAL(activated(int)),
this, SLOT(inotifyEventReceived()) );
}
#endif
#ifndef QT_NO_FILESYSTEMWATCHER
availableMethods << "QFileSystemWatcher";
fsWatcher = 0;
#endif
#ifndef NDEBUG
kDebug(7001) << "Available methods: " << availableMethods << "preferred=" << methodToString(m_preferredMethod);
#endif
@ -257,193 +203,11 @@ KDirWatchPrivate::~KDirWatchPrivate()
FAMClose(&fc);
}
#endif
#ifdef HAVE_SYS_INOTIFY_H
if ( supports_inotify )
::close( m_inotify_fd );
#endif
#ifndef QT_NO_FILESYSTEMWATCHER
delete fsWatcher;
#endif
}
void KDirWatchPrivate::inotifyEventReceived()
{
//kDebug(7001);
#ifdef HAVE_SYS_INOTIFY_H
if ( !supports_inotify )
return;
int pending = -1;
int offsetStartRead = 0; // where we read into buffer
char buf[8192];
assert( m_inotify_fd > -1 );
ioctl( m_inotify_fd, FIONREAD, &pending );
while ( pending > 0 ) {
const int bytesToRead = qMin( pending, (int)sizeof( buf ) - offsetStartRead );
int bytesAvailable = read( m_inotify_fd, &buf[offsetStartRead], bytesToRead );
pending -= bytesAvailable;
bytesAvailable += offsetStartRead;
offsetStartRead = 0;
int offsetCurrent = 0;
while ( bytesAvailable >= (int)sizeof( struct inotify_event ) ) {
const struct inotify_event * const event = (struct inotify_event *) &buf[offsetCurrent];
const int eventSize = sizeof( struct inotify_event ) + event->len;
if ( bytesAvailable < eventSize ) {
break;
}
bytesAvailable -= eventSize;
offsetCurrent += eventSize;
QString path;
QByteArray cpath(event->name, event->len);
if(event->len)
path = QFile::decodeName ( cpath );
if ( path.length() && isNoisyFile( cpath ) )
continue;
// Is set to true if the new event is a directory, false otherwise. This prevents a stat call in clientsForFileOrDir
const bool isDir = (event->mask & (IN_ISDIR));
// now we're in deep trouble of finding the
// associated entries
// for now, we suck and iterate
for ( EntryMap::Iterator it = m_mapEntries.begin();
it != m_mapEntries.end(); ) {
Entry* e = &( *it );
++it;
if ( e->wd == event->wd ) {
const bool wasDirty = e->dirty;
e->dirty = true;
const QString tpath = e->path + QLatin1Char('/') + path;
//if (s_verboseDebug) {
// kDebug(7001) << "got event" << "0x"+QString::number(event->mask, 16) << "for" << e->path;
//}
if( event->mask & IN_DELETE_SELF) {
if (s_verboseDebug) {
kDebug(7001) << "-->got deleteself signal for" << e->path;
}
e->m_status = NonExistent;
e->wd = -1;
e->m_ctime = invalid_ctime;
emitEvent(e, Deleted, e->path);
// If the parent dir was already watched, tell it something changed
Entry* parentEntry = entry(e->parentDirectory());
if (parentEntry)
parentEntry->dirty = true;
// Add entry to parent dir to notice if the entry gets recreated
addEntry(0, e->parentDirectory(), e, true /*isDir*/);
}
if ( event->mask & IN_IGNORED ) {
// Causes bug #207361 with kernels 2.6.31 and 2.6.32!
//e->wd = -1;
}
if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
Entry* sub_entry = e->findSubEntry(tpath);
if (s_verboseDebug) {
kDebug(7001) << "-->got CREATE signal for" << (tpath) << "sub_entry=" << sub_entry;
kDebug(7001) << *e;
}
// The code below is very similar to the one in checkFAMEvent...
if (sub_entry) {
// We were waiting for this new file/dir to be created
sub_entry->dirty = true;
rescan_timer.start(0); // process this asap, to start watching that dir
} else if (e->isDir && !e->m_clients.empty()) {
const QList<Client *> clients = e->inotifyClientsForFileOrDir(isDir);
Q_FOREACH(Client *client, clients) {
// See discussion in addEntry for why we don't addEntry for individual
// files in WatchFiles mode with inotify.
if (isDir) {
addEntry(client->instance, tpath, 0, isDir,
isDir ? client->m_watchModes : KDirWatch::WatchDirOnly);
}
}
if (!clients.isEmpty()) {
emitEvent(e, Created, tpath);
kDebug(7001).nospace() << clients.count() << " instance(s) monitoring the new "
<< (isDir ? "dir " : "file ") << tpath;
}
e->m_pendingFileChanges.append(e->path);
if (!rescan_timer.isActive())
rescan_timer.start(m_PollInterval); // singleshot
}
}
if (event->mask & (IN_DELETE|IN_MOVED_FROM)) {
if (s_verboseDebug) {
kDebug(7001) << "-->got DELETE signal for" << tpath;
}
if ((e->isDir) && (!e->m_clients.empty())) {
Client* client = 0;
// A file in this directory has been removed. It wasn't an explicitly
// watched file as it would have its own watch descriptor, so
// no addEntry/ removeEntry bookkeeping should be required. Emit
// the event immediately if any clients are interested.
KDirWatch::WatchModes flag = isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles;
int counter = 0;
Q_FOREACH(client, e->m_clients) { // krazy:exclude=foreach
if (client->m_watchModes & flag) {
counter++;
}
}
if (counter != 0) {
emitEvent(e, Deleted, tpath);
}
}
}
if (event->mask & (IN_MODIFY|IN_ATTRIB)) {
if ((e->isDir) && (!e->m_clients.empty())) {
if (s_verboseDebug) {
kDebug(7001) << "-->got MODIFY signal for" << (tpath);
}
// A file in this directory has been changed. No
// addEntry/ removeEntry bookkeeping should be required.
// Add the path to the list of pending file changes if
// there are any interested clients.
//KDE_struct_stat stat_buf;
//QByteArray tpath = QFile::encodeName(e->path+'/'+path);
//KDE_stat(tpath, &stat_buf);
//bool isDir = S_ISDIR(stat_buf.st_mode);
// The API doc is somewhat vague as to whether we should emit
// dirty() for implicitly watched files when WatchFiles has
// not been specified - we'll assume they are always interested,
// regardless.
// Don't worry about duplicates for the time
// being; this is handled in slotRescan.
e->m_pendingFileChanges.append(tpath);
// Avoid stat'ing the directory if only an entry inside it changed.
e->dirty = (wasDirty || (path.isEmpty() && (event->mask & IN_ATTRIB)));
}
}
if (!rescan_timer.isActive())
rescan_timer.start(m_PollInterval); // singleshot
break;
}
}
}
if (bytesAvailable > 0) {
// copy partial event to beginning of buffer
memmove(buf, &buf[offsetCurrent], bytesAvailable);
offsetStartRead = bytesAvailable;
}
}
#endif
}
/* In FAM mode, only entries which are marked dirty are scanned.
* We first need to mark all yet nonexistent, but possible created
* entries as dirty...
@ -542,33 +306,13 @@ QList<KDirWatchPrivate::Client *> KDirWatchPrivate::Entry::clientsForFileOrDir(c
return ret;
}
// inotify specific function that doesn't call KDE::stat to figure out if we have a file or folder.
// isDir is determined through inotify's "IN_ISDIR" flag in KDirWatchPrivate::inotifyEventReceived
QList<KDirWatchPrivate::Client *> KDirWatchPrivate::Entry::inotifyClientsForFileOrDir(bool isDir) const
{
QList<Client *> ret;
const KDirWatch::WatchModes flag = isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles;
Q_FOREACH(Client *client, this->m_clients) {
if (client->m_watchModes & flag) {
ret.append(client);
}
}
return ret;
}
QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry)
{
debug.nospace() << "[ Entry for " << entry.path << ", " << (entry.isDir ? "dir" : "file");
if (entry.m_status == KDirWatchPrivate::NonExistent)
debug << ", non-existent";
debug << ", using " << ((entry.m_mode == KDirWatchPrivate::FAMMode) ? "FAM" :
(entry.m_mode == KDirWatchPrivate::INotifyMode) ? "INotify" :
(entry.m_mode == KDirWatchPrivate::QFSWatchMode) ? "QFSWatch" :
(entry.m_mode == KDirWatchPrivate::StatMode) ? "Stat" : "Unknown Method");
#ifdef HAVE_SYS_INOTIFY_H
if (entry.m_mode == KDirWatchPrivate::INotifyMode)
debug << " inotify_wd=" << entry.wd;
#endif
(entry.m_mode == KDirWatchPrivate::QFSWatchMode) ? "QFSWatch" : "Unknown Method");
debug << ", has " << entry.m_clients.count() << " clients";
debug.space();
if (!entry.m_entries.isEmpty()) {
@ -672,40 +416,6 @@ bool KDirWatchPrivate::useFAM(Entry* e)
}
#endif
#ifdef HAVE_SYS_INOTIFY_H
// setup INotify notification, returns false if not possible
bool KDirWatchPrivate::useINotify( Entry* e )
{
//kDebug (7001) << "trying to use inotify for monitoring";
e->wd = -1;
e->dirty = false;
if (!supports_inotify) return false;
e->m_mode = INotifyMode;
if ( e->m_status == NonExistent ) {
addEntry(0, e->parentDirectory(), e, true);
return true;
}
// May as well register for almost everything - it's free!
int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW|IN_MOVED_FROM|IN_MODIFY|IN_ATTRIB;
if ( ( e->wd = inotify_add_watch( m_inotify_fd,
QFile::encodeName( e->path ), mask) ) >= 0)
{
if (s_verboseDebug) {
kDebug(7001) << "inotify successfully used for monitoring" << e->path << "wd=" << e->wd;
}
return true;
}
kDebug(7001) << "inotify failed for monitoring" << e->path << ":" << strerror(errno);
return false;
}
#endif
#ifndef QT_NO_FILESYSTEMWATCHER
bool KDirWatchPrivate::useQFSWatch(Entry* e)
{
@ -728,30 +438,6 @@ bool KDirWatchPrivate::useQFSWatch(Entry* e)
}
#endif
bool KDirWatchPrivate::useStat(Entry* e)
{
if (KFileSystemType::fileSystemType(e->path) == KFileSystemType::Nfs) // TODO: or Smbfs?
useFreq(e, m_nfsPollInterval);
else
useFreq(e, m_PollInterval);
if (e->m_mode != StatMode) {
e->m_mode = StatMode;
statEntries++;
if ( statEntries == 1 ) {
// if this was first STAT entry (=timer was stopped)
timer.start(freq); // then start the timer
kDebug(7001) << " Started Polling Timer, freq " << freq;
}
}
kDebug(7001) << " Setup Stat (freq " << e->freq << ") for " << e->path;
return true;
}
/* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
* providing in <isDir> the type of the entry to be watched.
* Sometimes, entries are dependant on each other: if <sub_entry> !=0,
@ -780,21 +466,6 @@ void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
kDebug(7001) << "Added already watched Entry" << path
<< "(for" << sub_entry->path << ")";
}
#ifdef HAVE_SYS_INOTIFY_H
Entry* e = &(*it);
if( (e->m_mode == INotifyMode) && (e->wd >= 0) ) {
int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
if(!e->isDir)
mask |= IN_MODIFY|IN_ATTRIB;
else
mask |= IN_ONLYDIR;
inotify_rm_watch (m_inotify_fd, e->wd);
e->wd = inotify_add_watch( m_inotify_fd, QFile::encodeName( e->path ),
mask);
//Q_ASSERT(e->wd >= 0); // fails in KDirListerTest::testDeleteCurrentDir
}
#endif
}
else {
(*it).addClient(instance, watchModes);
@ -879,17 +550,6 @@ void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
filters |= QDir::Files;
}
#if defined(HAVE_SYS_INOTIFY_H)
if (e->m_mode == INotifyMode || (e->m_mode == UnknownMode && m_preferredMethod == KDirWatch::INotify) )
{
//kDebug(7001) << "Ignoring WatchFiles directive - this is implicit with inotify";
// Placing a watch on individual files is redundant with inotify
// (inotify gives us WatchFiles functionality "for free") and indeed
// actively harmful, so prevent it. WatchSubDirs is necessary, though.
filters &= ~QDir::Files;
}
#endif
QDir basedir (e->path);
const QFileInfoList contents = basedir.entryInfoList(filters);
foreach(const QFileInfo iter, contents)
@ -912,8 +572,8 @@ void KDirWatchPrivate::addWatch(Entry* e)
// default, otherwise use preferredMethod as the default, if the methods are
// the same we can skip the mountpoint check
// This allows to configure a different method for NFS mounts, since inotify
// cannot detect changes made by other machines. However as a default inotify
// This allows to configure a different method for NFS mounts, since QFSWatch
// cannot detect changes made by other machines. However as a default QFSWatch
// is fine, since the most common case is a NFS-mounted home, where all changes
// are made locally. #177892.
KDirWatch::Method preferredMethod = m_preferredMethod;
@ -929,28 +589,20 @@ void KDirWatchPrivate::addWatch(Entry* e)
#if defined(HAVE_FAM)
case KDirWatch::FAM: entryAdded = useFAM(e); break;
#endif
#if defined(HAVE_SYS_INOTIFY_H)
case KDirWatch::INotify: entryAdded = useINotify(e); break;
#endif
#ifndef QT_NO_FILESYSTEMWATCHER
case KDirWatch::QFSWatch: entryAdded = useQFSWatch(e); break;
#endif
case KDirWatch::Stat: entryAdded = useStat(e); break;
default: break;
}
// Failing that try in order INotify, FAM, QFSWatch, Stat
// Failing that try in order FAM, QFSWatch
if (!entryAdded) {
#if defined(HAVE_SYS_INOTIFY_H)
if (useINotify(e)) return;
#endif
#if defined(HAVE_FAM)
if (useFAM(e)) return;
#endif
#ifndef QT_NO_FILESYSTEMWATCHER
if (useQFSWatch(e)) return;
#endif
useStat(e);
}
}
@ -963,15 +615,6 @@ void KDirWatchPrivate::removeWatch(Entry* e)
<< ") for " << e->path;
}
#endif
#ifdef HAVE_SYS_INOTIFY_H
if (e->m_mode == INotifyMode) {
(void) inotify_rm_watch( m_inotify_fd, e->wd );
if (s_verboseDebug) {
kDebug(7001).nospace() << "Cancelled INotify (fd " << m_inotify_fd << ", "
<< e->wd << ") for " << e->path;
}
}
#endif
#ifndef QT_NO_FILESYSTEMWATCHER
if (e->m_mode == QFSWatchMode && fsWatcher) {
if (s_verboseDebug)
@ -1027,14 +670,6 @@ void KDirWatchPrivate::removeEntry(KDirWatch* instance,
removeEntry(0, QFileInfo(e->path).absolutePath(), e);
}
if (e->m_mode == StatMode) {
statEntries--;
if ( statEntries == 0 ) {
timer.stop(); // stop timer if lists are empty
kDebug(7001) << " Stopped Polling Timer";
}
}
if (s_verboseDebug) {
kDebug(7001).nospace() << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
<< " for " << (sub_entry ? sub_entry->path : QString())
@ -1066,8 +701,6 @@ void KDirWatchPrivate::removeEntries( KDirWatch* instance )
c->count = 1; // forces deletion of instance as client
pathList.append((*it).path);
}
else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
minfreq = (*it).freq;
}
foreach(const QString &path, pathList)
@ -1098,10 +731,8 @@ bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
if (stillWatching == 0) {
// if nobody is interested, we don't watch
if ( e->m_mode != INotifyMode ) {
e->m_ctime = invalid_ctime; // invalid
e->m_status = NonExistent;
}
e->m_ctime = invalid_ctime; // invalid
e->m_status = NonExistent;
// e->m_status = Normal;
}
return true;
@ -1206,22 +837,12 @@ int KDirWatchPrivate::scanEntry(Entry* e)
// Shouldn't happen: Ignore "unknown" notification method
if (e->m_mode == UnknownMode) return NoChange;
if (e->m_mode == FAMMode || e->m_mode == INotifyMode) {
if (e->m_mode == FAMMode) {
// we know nothing has changed, no need to stat
if(!e->dirty) return NoChange;
e->dirty = false;
}
if (e->m_mode == StatMode) {
// only scan if timeout on entry timer happens;
// e.g. when using 500msec global timer, a entry
// with freq=5000 is only watched every 10th time
e->msecLeft -= freq;
if (e->msecLeft>0) return NoChange;
e->msecLeft += e->freq;
}
KDE_struct_stat stat_buf;
const bool exists = (KDE::stat(e->path, &stat_buf) == 0);
if (exists) {
@ -1355,7 +976,7 @@ void KDirWatchPrivate::slotRemoveDelayed()
{
delayRemove = false;
// Removing an entry could also take care of removing its parent
// (e.g. in FAM or inotify mode), which would remove other entries in removeList,
// (e.g. in FAM mode), which would remove other entries in removeList,
// so don't use foreach or iterators here...
while (!removeList.isEmpty()) {
Entry* entry = *removeList.begin();
@ -1364,7 +985,7 @@ void KDirWatchPrivate::slotRemoveDelayed()
}
/* Scan all entries to be watched for changes. This is done regularly
* when polling. FAM and inotify use a single-shot timer to call this slot delayed.
* when polling. FAM use a single-shot timer to call this slot delayed.
*/
void KDirWatchPrivate::slotRescan()
{
@ -1399,14 +1020,10 @@ void KDirWatchPrivate::slotRescan()
// progate dirty flag to dependant entries (e.g. file watches)
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
if (((*it).m_mode == INotifyMode || (*it).m_mode == QFSWatchMode) && (*it).dirty )
if ((*it).m_mode == QFSWatchMode && (*it).dirty )
(*it).propagate_dirty();
}
#ifdef HAVE_SYS_INOTIFY_H
QList<Entry*> cList;
#endif
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it ) {
// we don't check invalid entries (i.e. remove delayed)
@ -1418,22 +1035,6 @@ void KDirWatchPrivate::slotRescan()
kDebug(7001) << "scanEntry for" << entry->path << "says" << ev;
switch(entry->m_mode) {
#ifdef HAVE_SYS_INOTIFY_H
case INotifyMode:
if ( ev == Deleted ) {
if (s_verboseDebug)
kDebug(7001) << "scanEntry says" << entry->path << "was deleted";
addEntry(0, entry->parentDirectory(), entry, true);
} else if (ev == Created) {
if (s_verboseDebug)
kDebug(7001) << "scanEntry says" << entry->path << "was created. wd=" << entry->wd;
if (entry->wd < 0) {
cList.append(entry);
addWatch(entry);
}
}
break;
#endif
case FAMMode:
case QFSWatchMode:
if (ev == Created) {
@ -1445,24 +1046,6 @@ void KDirWatchPrivate::slotRescan()
break;
}
#ifdef HAVE_SYS_INOTIFY_H
if (entry->isDir) {
// Report and clear the the list of files that have changed in this directory.
// Remove duplicates by changing to set and back again:
// we don't really care about preserving the order of the
// original changes.
QStringList pendingFileChanges = entry->m_pendingFileChanges;
pendingFileChanges.removeDuplicates();
Q_FOREACH(const QString &changedFilename, pendingFileChanges) {
if (s_verboseDebug) {
kDebug(7001) << "processing pending file change for" << changedFilename;
}
emitEvent(entry, Changed, changedFilename);
}
entry->m_pendingFileChanges.clear();
}
#endif
if ( ev != NoChange ) {
emitEvent(entry, ev);
}
@ -1471,12 +1054,6 @@ void KDirWatchPrivate::slotRescan()
if ( timerRunning )
timer.start(freq);
#ifdef HAVE_SYS_INOTIFY_H
// Remove watch of parent of new created directories
Q_FOREACH(Entry* e, cList)
removeEntry(0, e->parentDirectory(), e);
#endif
QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
}
@ -1509,7 +1086,7 @@ void KDirWatchPrivate::famEventReceived()
use_fam = false;
delete sn; sn = 0;
// Replace all FAMMode entries with INotify/Stat
// Replace all FAMMode entries with QFSWatch
EntryMap::Iterator it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
@ -1598,7 +1175,6 @@ void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
addEntry(0, e->parentDirectory(), e, true /*isDir*/);
} else {
// A file in this directory has been removed, and wasn't explicitly watched.
// We could still inform clients, like inotify does? But stat can't.
// For now we just marked e dirty and slotRescan will emit the dir as dirty.
//kDebug(7001) << "Got FAMDeleted for" << QFile::decodeName(fe->filename) << "in" << e->path << ". Absolute path -> NOOP!";
}
@ -1608,7 +1184,6 @@ void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
// check for creation of a directory we have to watch
QString tpath(e->path + QLatin1Char('/') + QFile::decodeName(fe->filename));
// This code is very similar to the one in inotifyEventReceived...
Entry* sub_entry = e->findSubEntry(tpath);
if (sub_entry /*&& sub_entry->isDir*/) {
// We were waiting for this new file/dir to be created

View file

@ -49,14 +49,14 @@ class KDirWatchPrivate;
* and restarted. The whole class can be stopped and restarted.
* Directories and files can be added/removed from the list in any state.
*
* The implementation uses the INOTIFY functionality on LINUX.
* The implementation uses the QFileSystemWatch functionality on most systems.
* Otherwise the FAM service is used, when available.
* As a last resort, a regular polling for change of modification times
* is done; the polling interval is a global config option:
* DirWatch/PollInterval and DirWatch/NFSPollInterval for NFS mounted
* directories.
* The choice of implementation can be adjusted by the user, with the key
* [DirWatch] PreferredMethod={Fam|Stat|QFSWatch|inotify}
* [DirWatch] PreferredMethod={Fam|QFSWatch}
*
* @see self()
* @author Sven Radej (in 1998)
@ -220,7 +220,7 @@ class KDECORE_EXPORT KDirWatch : public QObject
*/
static void statistics(); // TODO implement a QDebug operator for KDirWatch instead.
enum Method { FAM, INotify, Stat, QFSWatch };
enum Method { FAM, QFSWatch };
/**
* Returns the preferred internal method to
* watch for changes.

View file

@ -43,21 +43,6 @@ class QSocketNotifier;
#include <fam.h>
#endif
#ifdef HAVE_SYS_INOTIFY_H
#include <unistd.h>
#include <fcntl.h>
#include <sys/inotify.h>
#ifndef IN_DONT_FOLLOW
#define IN_DONT_FOLLOW 0x02000000
#endif
#ifndef IN_ONLYDIR
#define IN_ONLYDIR 0x01000000
#endif
#endif
#include <sys/time.h>
#include <sys/param.h> // ino_t
#include <sys/stat.h> // ino_t
@ -81,7 +66,7 @@ class KDirWatchPrivate : public QObject
public:
enum entryStatus { Normal = 0, NonExistent };
enum entryMode { UnknownMode = 0, StatMode, INotifyMode, FAMMode, QFSWatchMode };
enum entryMode { UnknownMode = 0, FAMMode, QFSWatchMode };
enum { NoChange=0, Changed=1, Created=2, Deleted=4 };
@ -133,21 +118,10 @@ public:
void propagate_dirty();
QList<Client *> clientsForFileOrDir(const QString& tpath, bool* isDir) const;
QList<Client *> inotifyClientsForFileOrDir(bool isDir) const;
#ifdef HAVE_FAM
FAMRequest fr;
#endif
#ifdef HAVE_SYS_INOTIFY_H
int wd;
// Creation and Deletion of files happens infrequently, so
// can safely be reported as they occur. File changes i.e. those that emity "dirty()" can
// happen many times per second, though, so maintain a list of files in this directory
// that can be emitted and flushed at the next slotRescan(...).
// This will be unused if the Entry is not a directory.
QList<QString> m_pendingFileChanges;
#endif
};
typedef QMap<QString,Entry> EntryMap;
@ -184,7 +158,6 @@ public:
public Q_SLOTS:
void slotRescan();
void famEventReceived(); // for FAM
void inotifyEventReceived(); // for inotify
void slotRemoveDelayed();
void fswEventReceived(const QString &path); // for QFileSystemWatcher
@ -197,7 +170,6 @@ public:
int statEntries;
int m_nfsPollInterval, m_PollInterval;
int m_ref;
bool useStat(Entry*);
// removeList is allowed to contain any entry at most once
QSet<Entry *> removeList;
@ -215,13 +187,6 @@ public:
bool useFAM(Entry*);
#endif
#ifdef HAVE_SYS_INOTIFY_H
QSocketNotifier *mSn;
bool supports_inotify;
int m_inotify_fd;
bool useINotify(Entry*);
#endif
#ifndef QT_NO_FILESYSTEMWATCHER
KFileSystemWatcher *fsWatcher;
bool useQFSWatch(Entry* e);

View file

@ -41,10 +41,6 @@ static const char* methodToString(KDirWatch::Method method)
switch (method) {
case KDirWatch::FAM:
return "Fam";
case KDirWatch::INotify:
return "INotify";
case KDirWatch::Stat:
return "Stat";
case KDirWatch::QFSWatch:
return "QFSWatch";
default:
@ -72,7 +68,7 @@ public:
config.writeEntry("PollInterval", 50);
KDirWatch foo;
m_slow = (foo.internalMethod() == KDirWatch::FAM || foo.internalMethod() == KDirWatch::Stat);
m_slow = (foo.internalMethod() == KDirWatch::FAM);
kDebug() << "Using method" << methodToString(foo.internalMethod());
}
@ -277,7 +273,7 @@ bool KDirWatch_UnitTest::waitForOneSignal(KDirWatch& watch, const char* sig, con
if (got == expectedPath)
return true;
if (got.startsWith(expectedPath + '/')) {
kDebug() << "Ignoring (inotify) notification of" << sig << '(' << got << ')';
kDebug() << "Ignoring notification of" << sig << '(' << got << ')';
continue;
}
kWarning() << "Expected" << sig << '(' << removeTrailingSlash(path) << ')' << "but got" << sig << '(' << got << ')';
@ -344,13 +340,8 @@ void KDirWatch_UnitTest::touch1000Files()
}
QList<QVariantList> spy = waitForDirtySignal(watch, fileCount);
if (watch.internalMethod() == KDirWatch::INotify) {
QVERIFY(spy.count() >= fileCount);
qDebug() << spy.count();
} else {
// More stupid backends just see one mtime change on the directory
QVERIFY(spy.count() >= 1);
}
// More stupid backends just see one mtime change on the directory
QVERIFY(spy.count() >= 1);
for (int i = 0; i < fileCount; ++i) {
removeFile(i);
@ -382,8 +373,7 @@ void KDirWatch_UnitTest::removeAndReAdd()
// Just like KDirLister does: remove the watch, then re-add it.
watch.removeDir(m_path);
watch.addDir(m_path);
if (watch.internalMethod() != KDirWatch::INotify)
waitUntilMTimeChange(m_path); // necessary for FAM and QFSWatcher
waitUntilMTimeChange(m_path); // necessary for FAM and QFSWatcher
const QString file1 = createFile(1);
//kDebug() << "created" << file1;
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), m_path));
@ -442,9 +432,9 @@ void KDirWatch_UnitTest::watchNonExistentWithSingleton()
const QString file = "/root/.ssh/authorized_keys";
KDirWatch::self()->addFile(file);
// When running this test in KDIRWATCHTEST_METHOD=QFSWatch, or when FAM is not available
// and we fallback to qfswatch when inotify fails above, we end up creating the fsWatch
// in the kdirwatch singleton. Bug 261541 discovered that Qt hanged when deleting fsWatch
// once QCoreApp was gone, this is what this test is about.
// and we fallback to qfswatch, we end up creating the fsWatch in the kdirwatch singleton.
// Bug 261541 discovered that Qt hanged when deleting fsWatch once QCoreApp was gone,
// this is what this test is about.
}
void KDirWatch_UnitTest::testDelete()
@ -490,18 +480,16 @@ void KDirWatch_UnitTest::testDeleteAndRecreateFile() // Useful for /etc/localtim
createFile(file1);
// gamin does not signal the change in this case; probably because it uses polling internally...
if (watch.internalMethod() == KDirWatch::FAM || watch.internalMethod() == KDirWatch::Stat) {
QSKIP("Deleting and recreating a file is not detected by FAM (at least with gamin) or Stat", SkipAll);
if (watch.internalMethod() == KDirWatch::FAM) {
QSKIP("Deleting and recreating a file is not detected by FAM (at least with gamin)", SkipAll);
}
//QCOMPARE(KDE::stat(QFile::encodeName(file1), &stat_buf), 0);
//kDebug() << "new inode" << stat_buf.st_ino; // same!
if (watch.internalMethod() == KDirWatch::INotify) {
QVERIFY(waitForOneSignal(watch, SIGNAL(deleted(QString)), file1));
QVERIFY(waitForOneSignal(watch, SIGNAL(created(QString)), file1));
} else {
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), file1));
}
// NOTE: QFSWatch does not emit these
// QVERIFY(waitForOneSignal(watch, SIGNAL(deleted(QString)), file1));
// QVERIFY(waitForOneSignal(watch, SIGNAL(created(QString)), file1));
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), file1));
// QFileSystemWatcher, as documented, stops watching when the file is deleted
// so the appendToFile below will fail. Or further changes to /etc/localtime...
@ -539,7 +527,7 @@ void KDirWatch_UnitTest::testMoveTo()
{
// This reproduces the famous digikam crash, #222974
// A watched file was being rewritten (overwritten by ksavefile),
// which gives inotify notifications "moved_to" followed by "delete_self"
// which gives notifications "moved_to" followed by "delete_self"
//
// What happened then was that the delayed slotRescan
// would adjust things, making it status==Normal but the entry was
@ -555,8 +543,7 @@ void KDirWatch_UnitTest::testMoveTo()
watch.addFile(file1);
watch.startScan();
if (watch.internalMethod() != KDirWatch::INotify)
waitUntilMTimeChange(m_path);
waitUntilMTimeChange(m_path);
// Atomic rename of "temp" to "file1", much like KAutoSave would do when saving file1 again
const QString filetemp = m_path + "temp";
@ -567,12 +554,6 @@ void KDirWatch_UnitTest::testMoveTo()
QSignalSpy spyCreated(&watch, SIGNAL(created(QString)));
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), m_path));
// Getting created() on an unwatched file is an inotify bonus, it's not part of the requirements.
if (watch.internalMethod() == KDirWatch::INotify) {
QCOMPARE(spyCreated.count(), 1);
QCOMPARE(spyCreated[0][0].toString(), file1);
}
// make sure we're still watching it
appendToFile(file1);
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), file1));
@ -643,7 +624,6 @@ void KDirWatch_UnitTest::testHardlinkChange()
// described on kde-core-devel (2009-07-03).
// It shows that watching a specific file doesn't inform us that the file is
// being recreated. Better watch the directory, for that.
// Well, it works with inotify (and fam - which uses inotify I guess?)
const QString existingFile = m_path + "ExistingFile";
KDirWatch watch;
@ -659,7 +639,7 @@ void KDirWatch_UnitTest::testHardlinkChange()
QVERIFY(QFile::exists(existingFile));
//QVERIFY(waitForOneSignal(watch, SIGNAL(deleted(QString)), existingFile));
if (watch.internalMethod() == KDirWatch::INotify || watch.internalMethod() == KDirWatch::FAM)
if (watch.internalMethod() == KDirWatch::FAM)
QVERIFY(waitForOneSignal(watch, SIGNAL(created(QString)), existingFile));
else
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), existingFile));

View file

@ -32,8 +32,6 @@ QTEST_KDEMAIN( KDirListerTest, NoGUI )
#include <kio/job.h>
#include <kio/copyjob.h>
#define WORKAROUND_BROKEN_INOTIFY 0
void MyDirLister::handleError(KIO::Job* job)
{
// Currently we don't expect any errors.
@ -278,9 +276,6 @@ void KDirListerTest::testNewItemsInSymlink() // #213799
const QString fileName = "toplevelfile_newinlink";
createSimpleFile(path + fileName);
#if WORKAROUND_BROKEN_INOTIFY
org::kde::KDirNotify::emitFilesAdded(path);
#endif
int numTries = 0;
// Give time for KDirWatch to notify us
while (m_items2.count() == origItemCount) {
@ -505,9 +500,6 @@ void KDirListerTest::testRenameAndOverwrite() // has to be run after testRenameI
const QString dirPath = m_tempDir.name();
const QString path = dirPath+"toplevelfile_2";
createTestFile(path);
#if WORKAROUND_BROKEN_INOTIFY
org::kde::KDirNotify::emitFilesAdded(dirPath);
#endif
KFileItem existingItem;
while (existingItem.isNull()) {
QTest::qWait(100);

View file

@ -76,7 +76,7 @@ void KDirModelTest::recreateTestData()
if (m_tempDir) {
kDebug() << "Deleting old tempdir" << m_tempDir->name();
delete m_tempDir;
qApp->processEvents(); // process inotify events so they don't pollute us later on
qApp->processEvents(); // process events so they don't pollute us later on
}