//
// Authors:
// Gonzalo Paniagua (gonzalo@novell.com)
+// Anders Rune Jensen (anders@iola.dk)
//
// (c) 2006 Novell, Inc. (http://www.novell.com)
}
}
+ class ParentInotifyData
+ {
+ public bool IncludeSubdirs;
+ public bool Enabled;
+ public ArrayList children; // InotifyData
+ public InotifyData data;
+ }
+
class InotifyData {
public FileSystemWatcher FSW;
public string Directory;
- public string FileMask;
- public bool IncludeSubdirs;
- public bool Enabled;
public int Watch;
- public Hashtable SubDirs;
}
class InotifyWatcher : IFileWatcher
{
static bool failed;
static InotifyWatcher instance;
- static Hashtable watches; // FSW to InotifyData
+ static Hashtable watches; // FSW to ParentInotifyData
static Hashtable requests; // FSW to InotifyData
static IntPtr FD;
static Thread thread;
public void StartDispatching (FileSystemWatcher fsw)
{
- InotifyData data;
+ ParentInotifyData parent;
lock (this) {
if ((long) FD == -1)
FD = GetInotifyInstance ();
thread.Start ();
}
- data = (InotifyData) watches [fsw];
+ parent = (ParentInotifyData) watches [fsw];
}
- if (data == null) {
- data = new InotifyData ();
+ if (parent == null) {
+ InotifyData data = new InotifyData ();
data.FSW = fsw;
data.Directory = fsw.FullPath;
- data.FileMask = fsw.MangledFilter;
- data.IncludeSubdirs = fsw.IncludeSubdirectories;
- if (data.IncludeSubdirs)
- data.SubDirs = new Hashtable ();
- data.Enabled = true;
- StartMonitoringDirectory (data, false);
- lock (this) {
- watches [fsw] = data;
- AppendRequestData (data);
- stop = false;
- }
+ parent = new ParentInotifyData();
+ parent.IncludeSubdirs = fsw.IncludeSubdirectories;
+ parent.Enabled = true;
+ parent.children = new ArrayList();
+ parent.data = data;
+
+ watches [fsw] = parent;
+
+ try {
+ StartMonitoringDirectory (data, false);
+ lock (this) {
+ AppendRequestData (data);
+ stop = false;
+ }
+ } catch {} // ignore the directory if StartMonitoringDirectory fails.
}
}
static void AppendRequestData (InotifyData data)
{
int wd = data.Watch;
+
object obj = requests [wd];
ArrayList list = null;
if (obj == null) {
mask |= InotifyMask.Attrib;
mask |= InotifyMask.Access;
mask |= InotifyMask.Modify;
- mask |= InotifyMask.CloseWrite;
}
if ((filters & NotifyFilters.LastWrite) != 0) {
mask |= InotifyMask.Attrib;
- mask |= InotifyMask.CloseWrite;
+ mask |= InotifyMask.Modify;
}
if ((filters & NotifyFilters.FileName) != 0) {
if (wd == -1) {
int error = Marshal.GetLastWin32Error ();
if (error == 4) { // Too many open watches
- string watches = "(unknown)";
+ string nr_watches = "(unknown)";
try {
using (StreamReader reader = new StreamReader ("/proc/sys/fs/inotify/max_user_watches")) {
- watches = reader.ReadLine ();
+ nr_watches = reader.ReadLine ();
}
} catch {}
string msg = String.Format ("The per-user inotify watches limit of {0} has been reached. " +
"If you're experiencing problems with your application, increase that limit " +
- "in /proc/sys/fs/inotify/max_user_watches.", watches);
+ "in /proc/sys/fs/inotify/max_user_watches.", nr_watches);
throw new Win32Exception (error, msg);
}
FileSystemWatcher fsw = data.FSW;
data.Watch = wd;
- if (data.IncludeSubdirs) {
+ ParentInotifyData parent = (ParentInotifyData) watches[fsw];
+
+ if (parent.IncludeSubdirs) {
foreach (string directory in Directory.GetDirectories (data.Directory)) {
InotifyData fd = new InotifyData ();
fd.FSW = fsw;
fd.Directory = directory;
- fd.FileMask = data.FSW.MangledFilter;
- fd.IncludeSubdirs = true;
- fd.SubDirs = new Hashtable ();
- fd.Enabled = true;
if (justcreated) {
lock (fsw) {
RenamedEventArgs renamed = null;
- fsw.DispatchEvents (FileAction.Added, directory, ref renamed);
- if (fsw.Waiting) {
- fsw.Waiting = false;
- System.Threading.Monitor.PulseAll (fsw);
+ if (fsw.Pattern.IsMatch (directory)) {
+ fsw.DispatchEvents (FileAction.Added, directory, ref renamed);
+ if (fsw.Waiting) {
+ fsw.Waiting = false;
+ System.Threading.Monitor.PulseAll (fsw);
+ }
}
}
}
- StartMonitoringDirectory (fd, justcreated);
- fd.SubDirs [directory] = fd;
- AppendRequestData (fd);
+ try {
+ StartMonitoringDirectory (fd, justcreated);
+ AppendRequestData (fd);
+ parent.children.Add(fd);
+ } catch {} // ignore errors and don't add directory.
}
}
foreach (string filename in Directory.GetFiles (data.Directory)) {
lock (fsw) {
RenamedEventArgs renamed = null;
+ if (fsw.Pattern.IsMatch (filename)) {
+ fsw.DispatchEvents (FileAction.Added, filename, ref renamed);
+ /* If a file has been created, then it has been written to */
+ fsw.DispatchEvents (FileAction.Modified, filename, ref renamed);
- fsw.DispatchEvents (FileAction.Added, filename, ref renamed);
- /* If a file has been created, then it has been written to */
- fsw.DispatchEvents (FileAction.Modified, filename, ref renamed);
-
- if (fsw.Waiting) {
- fsw.Waiting = false;
- System.Threading.Monitor.PulseAll(fsw);
+ if (fsw.Waiting) {
+ fsw.Waiting = false;
+ System.Threading.Monitor.PulseAll(fsw);
+ }
}
}
}
public void StopDispatching (FileSystemWatcher fsw)
{
- InotifyData data;
+ ParentInotifyData parent;
lock (this) {
- data = (InotifyData) watches [fsw];
- if (data == null)
+ parent = (ParentInotifyData) watches [fsw];
+ if (parent == null)
return;
- if (RemoveRequestData (data)) {
- StopMonitoringDirectory (data);
+ if (RemoveRequestData (parent.data)) {
+ StopMonitoringDirectory (parent.data);
}
watches.Remove (fsw);
if (watches.Count == 0) {
Close (fd);
}
- if (!data.IncludeSubdirs)
+ if (!parent.IncludeSubdirs)
return;
- foreach (InotifyData idata in data.SubDirs.Values) {
- if (RemoveRequestData (idata)) {
- StopMonitoringDirectory (idata);
- }
+ foreach (InotifyData idata in parent.children)
+ {
+ if (RemoveRequestData (idata)) {
+ StopMonitoringDirectory (idata);
+ }
}
}
}
* Create
* Delete
* DeleteSelf
- * CloseWrite
*/
static InotifyMask Interesting = InotifyMask.Modify | InotifyMask.Attrib | InotifyMask.MovedFrom |
InotifyMask.MovedTo | InotifyMask.Create | InotifyMask.Delete |
- InotifyMask.DeleteSelf | InotifyMask.CloseWrite;
+ InotifyMask.DeleteSelf;
void ProcessEvents (byte [] buffer, int length)
{
ArrayList newdirs = null;
InotifyEvent evt;
int nread = 0;
- bool new_name_needed = false;
RenamedEventArgs renamed = null;
while (length > nread) {
int bytes_read = ReadEvent (buffer, nread, length, out evt);
continue;
foreach (InotifyData data in GetEnumerator (requests [evt.WatchDescriptor])) {
- if (data == null || data.Enabled == false)
+ ParentInotifyData parent = (ParentInotifyData) watches[data.FSW];
+
+ if (data == null || parent.Enabled == false)
continue;
string directory = data.Directory;
FileSystemWatcher fsw = data.FSW;
FileAction action = 0;
- if ((mask & (InotifyMask.Modify | InotifyMask.CloseWrite | InotifyMask.Attrib)) != 0) {
+ if ((mask & (InotifyMask.Modify | InotifyMask.Attrib)) != 0) {
action = FileAction.Modified;
} else if ((mask & InotifyMask.Create) != 0) {
action = FileAction.Added;
} else if ((mask & InotifyMask.Delete) != 0) {
action = FileAction.Removed;
} else if ((mask & InotifyMask.DeleteSelf) != 0) {
+ if (data.Watch != parent.data.Watch) {
+ // To avoid duplicate events handle DeleteSelf only for the top level directory.
+ continue;
+ }
action = FileAction.Removed;
} else if ((mask & InotifyMask.MoveSelf) != 0) {
//action = FileAction.Removed;
} else if ((mask & InotifyMask.MovedFrom) != 0) {
InotifyEvent to;
int i = ReadEvent (buffer, nread, length, out to);
- if (i == -1 || (to.Mask & InotifyMask.MovedTo) == 0) {
+ if (i == -1 || (to.Mask & InotifyMask.MovedTo) == 0 || evt.WatchDescriptor != to.WatchDescriptor) {
action = FileAction.Removed;
} else {
nread += i;
action = FileAction.RenamedNewName;
- if (evt.Name == data.Directory || fsw.Pattern.IsMatch (evt.Name)) {
- renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, data.Directory, to.Name, evt.Name);
- } else {
- renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, data.Directory, evt.Name, to.Name);
+ renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, data.Directory, to.Name, evt.Name);
+ if (evt.Name != data.Directory && !fsw.Pattern.IsMatch (evt.Name))
filename = to.Name;
- }
}
} else if ((mask & InotifyMask.MovedTo) != 0) {
- action = (new_name_needed) ? FileAction.RenamedNewName : FileAction.Added;
- new_name_needed = false;
+ action = FileAction.Added;
}
-
if (fsw.IncludeSubdirectories) {
string full = fsw.FullPath;
string datadir = data.Directory;
if (action == FileAction.Added && is_directory) {
if (newdirs == null)
- newdirs = new ArrayList (4);
+ newdirs = new ArrayList (2);
InotifyData fd = new InotifyData ();
fd.FSW = fsw;
fd.Directory = datadir;
- fd.FileMask = fsw.MangledFilter;
- fd.IncludeSubdirs = true;
- fd.SubDirs = new Hashtable ();
- fd.Enabled = true;
newdirs.Add (fd);
- newdirs.Add (data);
+ }
+
+ if (action == FileAction.RenamedNewName && is_directory) {
+ string renamedOldFullPath = renamed.OldFullPath;
+ string renamedFullPath = renamed.FullPath;
+ int renamedOldFullPathLength = renamedOldFullPath.Length;
+
+ foreach (InotifyData child in parent.children) {
+
+ if (child.Directory.StartsWith (renamedOldFullPath
+ , StringComparison.Ordinal
+ )) {
+ child.Directory = renamedFullPath +
+ child.Directory.Substring (renamedOldFullPathLength);
+ }
+ }
}
}
- if (filename != data.Directory && !fsw.Pattern.IsMatch (filename)) {
+ if (action == FileAction.Removed && filename == data.Directory) {
+ int idx = parent.children.IndexOf (data);
+ if (idx != -1) {
+ parent.children.RemoveAt (idx);
+ if (!fsw.Pattern.IsMatch (Path.GetFileName (filename))) {
+ continue;
+ }
+ }
+ }
+
+ if (filename != data.Directory && !fsw.Pattern.IsMatch (Path.GetFileName (filename))) {
continue;
}
}
if (newdirs != null) {
- int count = newdirs.Count;
- for (int n = 0; n < count; n += 2) {
- InotifyData newdir = (InotifyData) newdirs [n];
- InotifyData parent = (InotifyData) newdirs [n + 1];
- StartMonitoringDirectory (newdir, true);
- AppendRequestData (newdir);
- lock (parent) {
- parent.SubDirs [newdir.Directory] = newdir;
- }
+ foreach (InotifyData newdir in newdirs) {
+ try {
+ StartMonitoringDirectory (newdir, true);
+ AppendRequestData (newdir);
+ ((ParentInotifyData) watches[newdir.FSW]).children.Add(newdir);
+ } catch {} // ignore the given directory
}
newdirs.Clear ();
}