Merge pull request #2819 from BrzVlad/fix-major-log
[mono.git] / mcs / class / System / System.IO / InotifyWatcher.cs
index a717f40922c2959fe131dd01c639baf3c0af717d..fe023429a0cb021c4b0b2756ae74c3e55dac8ba6 100644 (file)
@@ -3,6 +3,7 @@
 //
 // Authors:
 //     Gonzalo Paniagua (gonzalo@novell.com)
+//     Anders Rune Jensen (anders@iola.dk)
 //
 // (c) 2006 Novell, Inc. (http://www.novell.com)
 
@@ -77,21 +78,25 @@ namespace System.IO {
                }
        }
 
+       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;
@@ -130,7 +135,7 @@ namespace System.IO {
                
                public void StartDispatching (FileSystemWatcher fsw)
                {
-                       InotifyData data;
+                       ParentInotifyData parent;
                        lock (this) {
                                if ((long) FD == -1)
                                        FD = GetInotifyInstance ();
@@ -141,31 +146,36 @@ namespace System.IO {
                                        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) {
@@ -224,12 +234,11 @@ namespace System.IO {
                                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) {
@@ -252,16 +261,16 @@ namespace System.IO {
                        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);
                                }
@@ -271,30 +280,32 @@ namespace System.IO {
                        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.
                                }
                        }
 
@@ -302,14 +313,15 @@ namespace System.IO {
                                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);
+                                                       }
                                                }
                                        }
                                }
@@ -318,14 +330,14 @@ namespace System.IO {
 
                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) {
@@ -335,13 +347,14 @@ namespace System.IO {
                                        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);
+                                   }
                                }
                        }
                }
@@ -442,18 +455,16 @@ namespace System.IO {
                        * 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);
@@ -469,7 +480,9 @@ namespace System.IO {
                                        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;
@@ -479,13 +492,17 @@ namespace System.IO {
 
                                        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;
@@ -493,23 +510,18 @@ namespace System.IO {
                                        } 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;
@@ -527,21 +539,42 @@ namespace System.IO {
 
                                                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;
                                        }
 
@@ -558,15 +591,12 @@ namespace System.IO {
                        }
 
                        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 ();
                        }