Eio: improve path monitoring on Windows
authorVincent Torri <vincent.torri@gmail.com>
Thu, 6 Sep 2012 06:30:29 +0000 (06:30 +0000)
committerVincent Torri <vincent.torri@gmail.com>
Thu, 6 Sep 2012 06:30:29 +0000 (06:30 +0000)
Not perfect but i'm fed up with that Windows stuff. I need to work on
something else

SVN revision: 76215

legacy/eio/ChangeLog
legacy/eio/NEWS
legacy/eio/TODO [new file with mode: 0644]
legacy/eio/src/lib/Eio.h
legacy/eio/src/lib/eio_monitor.c
legacy/eio/src/lib/eio_monitor_win32.c

index cb58e90..0dc04a1 100644 (file)
@@ -35,3 +35,7 @@
 2012-08-30  Carsten Haitzler (The Rasterman)
 
         1.7.0 release
+
+2012-09-06  Vincent Torri
+
+       * Improve file monitoring on Windows to mimic more inotify behavior.
index 5da27bd..aedcbee 100644 (file)
@@ -14,6 +14,7 @@ Fixes:
        - Fix memory leak when using file_associate.
 
 Improvements:
-       - Add log debuggong macros
+       - Add log debugging macros.
+       - path monitoring on Windows.
 
 Removal:
diff --git a/legacy/eio/TODO b/legacy/eio/TODO
new file mode 100644 (file)
index 0000000..01c6f05
--- /dev/null
@@ -0,0 +1 @@
+ * Fix file monitoring on Windows when we pass a file and not just a directory
\ No newline at end of file
index 07d1e1f..e21a269 100644 (file)
@@ -1107,11 +1107,11 @@ EAPI Eio_File *eio_eet_write_cipher(Eet_File *ef,
 EAPI extern int EIO_MONITOR_FILE_CREATED; /**< A new file was created in a watched directory */
 EAPI extern int EIO_MONITOR_FILE_DELETED; /**< A watched file was deleted, or a file in a watched directory was deleted */
 EAPI extern int EIO_MONITOR_FILE_MODIFIED; /**< A file was modified in a watched directory */
-EAPI extern int EIO_MONITOR_FILE_CLOSED; /**< A file was closed in a watched directory */
+EAPI extern int EIO_MONITOR_FILE_CLOSED; /**< A file was closed in a watched directory. This event is never sent on Windows */
 EAPI extern int EIO_MONITOR_DIRECTORY_CREATED; /**< A new directory was created in a watched directory */
 EAPI extern int EIO_MONITOR_DIRECTORY_DELETED; /**< A directory has been deleted: this can be either a watched directory or one of its subdirectories */
 EAPI extern int EIO_MONITOR_DIRECTORY_MODIFIED; /**< A directory has been modified in a watched directory */
-EAPI extern int EIO_MONITOR_DIRECTORY_CLOSED; /**< A directory has been closed in a watched directory */
+EAPI extern int EIO_MONITOR_DIRECTORY_CLOSED; /**< A directory has been closed in a watched directory. This event is never sent on Windows */
 EAPI extern int EIO_MONITOR_SELF_RENAME; /**< The monitored path has been renamed, an error could happen just after if the renamed path doesn't exist */
 EAPI extern int EIO_MONITOR_SELF_DELETED; /**< The monitored path has been removed */
 EAPI extern int EIO_MONITOR_ERROR; /**< During operation the monitor failed and will no longer work. eio_monitor_del must be called on it. */
index 50d9ef4..c21b1f4 100644 (file)
@@ -273,7 +273,11 @@ eio_monitor_stringshared_add(const char *path)
        eio_monitor_init();
      }
 
-   if (stat(path, &st) != 0) return NULL;
+   if (stat(path, &st) != 0)
+     {
+        INF("monitored path not found");
+        return NULL;
+     }
 
    monitor = eina_hash_find(_eio_monitors, path);
 
index 676ba2f..16dbaa7 100644 (file)
@@ -41,16 +41,19 @@ struct _Eio_Monitor_Win32_Watcher
    HANDLE               event;
    Eio_Monitor         *monitor;
    Ecore_Win32_Handler *h;
+   char                *current;
+   char                *file;
    DWORD                buf_length;
-   int                  is_dir;
+   Eina_Bool            monitor_file : 1;
+   Eina_Bool            monitor_parent : 1;
 };
 
 struct _Eio_Monitor_Backend
 {
    Eio_Monitor               *parent;
-
-   Eio_Monitor_Win32_Watcher *file;
-   Eio_Monitor_Win32_Watcher *dir;
+   Eio_Monitor_Win32_Watcher *watcher_file;
+   Eio_Monitor_Win32_Watcher *watcher_dir;
+   Eio_Monitor_Win32_Watcher *watcher_parent;
 };
 
 static Eina_Bool _eio_monitor_win32_native = EINA_FALSE;
@@ -58,7 +61,6 @@ static Eina_Bool _eio_monitor_win32_native = EINA_FALSE;
 static Eina_Bool
 _eio_monitor_win32_cb(void *data, Ecore_Win32_Handler *wh __UNUSED__)
 {
-   char                       filename[PATH_MAX];
    PFILE_NOTIFY_INFORMATION   fni;
    Eio_Monitor_Win32_Watcher *w;
    wchar_t                   *wname;
@@ -66,7 +68,7 @@ _eio_monitor_win32_cb(void *data, Ecore_Win32_Handler *wh __UNUSED__)
    DWORD                      filter;
    DWORD                      offset;
    DWORD                      buf_length;
-   int                        event = EIO_MONITOR_ERROR;
+   int                        event;
 
    w = (Eio_Monitor_Win32_Watcher *)data;
 
@@ -79,76 +81,99 @@ _eio_monitor_win32_cb(void *data, Ecore_Win32_Handler *wh __UNUSED__)
         break;
       offset = fni->NextEntryOffset;
 
-      wname = (wchar_t *)malloc(sizeof(wchar_t) * (fni->FileNameLength + 1));
+      wname = (wchar_t *)malloc(fni->FileNameLength + sizeof(wchar_t));
       if (!wname)
         return 0;
 
       memcpy(wname, fni->FileName, fni->FileNameLength);
-      wname[fni->FileNameLength]='\0';
+      wname[fni->FileNameLength / sizeof(wchar_t)] = 0;
       name = evil_wchar_to_char(wname);
       free(wname);
       if (!name)
         return ECORE_CALLBACK_CANCEL;
 
-      _snprintf(filename, PATH_MAX, "%s\\%s", w->monitor->path, name);
-      free(name);
-
-      name = filename;
-      while (*name)
-        {
-          if (*name == '/') *name = '\\';
-          name++;
-        }
-
+      event = -1;
       switch (fni->Action)
         {
         case FILE_ACTION_ADDED:
-          if (w->is_dir)
-            event = EIO_MONITOR_DIRECTORY_CREATED;
-          else
-            event = EIO_MONITOR_FILE_CREATED;
+          if (!w->monitor_parent)
+            {
+               if (w->monitor_file)
+                 event = EIO_MONITOR_FILE_CREATED;
+               else
+                 event = EIO_MONITOR_DIRECTORY_CREATED;
+            }
           break;
         case FILE_ACTION_REMOVED:
-          if (w->is_dir)
-            event = EIO_MONITOR_DIRECTORY_DELETED;
+          if (w->monitor_parent)
+            {
+               char path[MAX_PATH];
+               char *res;
+
+               res = _fullpath(path, name, MAX_PATH);
+               if (res && (strcmp(res, w->current) == 0))
+                 event = EIO_MONITOR_SELF_DELETED;
+            }
           else
-            event = EIO_MONITOR_FILE_DELETED;
+            {
+               if (w->monitor_file)
+                 event = EIO_MONITOR_FILE_DELETED;
+               else
+                 event = EIO_MONITOR_DIRECTORY_DELETED;
+            }
           break;
         case FILE_ACTION_MODIFIED:
-          if (!w->is_dir)
-            event = EIO_MONITOR_FILE_MODIFIED;
+          if (!w->monitor_parent)
+            {
+               if (w->monitor_file)
+                 event = EIO_MONITOR_FILE_MODIFIED;
+               else
+                 event = EIO_MONITOR_DIRECTORY_MODIFIED;
+            }
           break;
         case FILE_ACTION_RENAMED_OLD_NAME:
-          if (w->is_dir)
-            event = EIO_MONITOR_DIRECTORY_DELETED;
-          else
-            event = EIO_MONITOR_FILE_DELETED;
+          if (!w->monitor_parent)
+            {
+               if (w->monitor_file)
+                 event = EIO_MONITOR_FILE_DELETED;
+               else
+                 event = EIO_MONITOR_DIRECTORY_DELETED;
+            }
           break;
         case FILE_ACTION_RENAMED_NEW_NAME:
-          if (w->is_dir)
-            event = EIO_MONITOR_DIRECTORY_CREATED;
-          else
-            event = EIO_MONITOR_FILE_CREATED;
+          if (!w->monitor_parent)
+            {
+               if (w->monitor_file)
+                 event = EIO_MONITOR_FILE_CREATED;
+               else
+                 event = EIO_MONITOR_DIRECTORY_CREATED;
+            }
           break;
         default:
-          fprintf(stderr, "unknown event\n");
+          ERR("unknown event");
           event = EIO_MONITOR_ERROR;
           break;
         }
-      if (event != EIO_MONITOR_ERROR)
-        _eio_monitor_send(w->monitor, filename, event);
+
+      if (event >= 0)
+        _eio_monitor_send(w->monitor, name, event);
+
+      free(name);
 
       fni = (PFILE_NOTIFY_INFORMATION)((LPBYTE)fni + offset);
    } while (offset);
 
-   filter = (w->is_dir == 0) ? FILE_NOTIFY_CHANGE_FILE_NAME : FILE_NOTIFY_CHANGE_DIR_NAME;
-   filter |=
+   filter =
      FILE_NOTIFY_CHANGE_ATTRIBUTES |
      FILE_NOTIFY_CHANGE_SIZE |
      FILE_NOTIFY_CHANGE_LAST_WRITE |
      FILE_NOTIFY_CHANGE_LAST_ACCESS |
      FILE_NOTIFY_CHANGE_CREATION |
      FILE_NOTIFY_CHANGE_SECURITY;
+   if (w->monitor_file)
+     filter |= FILE_NOTIFY_CHANGE_FILE_NAME;
+   else
+     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
 
     ReadDirectoryChangesW(w->handle,
                           (LPVOID)w->buffer,
@@ -163,17 +188,32 @@ _eio_monitor_win32_cb(void *data, Ecore_Win32_Handler *wh __UNUSED__)
 }
 
 static Eio_Monitor_Win32_Watcher *
-_eio_monitor_win32_watcher_new(Eio_Monitor *monitor, unsigned char is_dir)
+_eio_monitor_win32_watcher_new(Eio_Monitor *monitor,
+                               char        *current,
+                               char        *file,
+                               Eina_Bool    monitor_file,
+                               Eina_Bool    monitor_parent)
 {
-   char path[PATH_MAX];
    Eio_Monitor_Win32_Watcher *w;
+   char                      *monitored;
    DWORD                      filter;
 
    w = (Eio_Monitor_Win32_Watcher *)calloc(1, sizeof(Eio_Monitor_Win32_Watcher));
    if (!w) return NULL;
 
-   realpath(monitor->path, path);
-   w->handle = CreateFile(path,
+   if (!monitor_parent)
+     monitored = current;
+   else
+     {
+        char *tmp;
+
+        tmp = strrchr(current, '\\');
+        monitored = (char *)alloca((tmp - current) + 1);
+        memcpy(monitored, current, tmp - current);
+        monitored[tmp - current] = '\0';
+     }
+
+   w->handle = CreateFile(monitored,
                           FILE_LIST_DIRECTORY,
                           FILE_SHARE_READ |
                           FILE_SHARE_WRITE,
@@ -192,14 +232,17 @@ _eio_monitor_win32_watcher_new(Eio_Monitor *monitor, unsigned char is_dir)
    ZeroMemory (&w->overlapped, sizeof(w->overlapped));
    w->overlapped.hEvent = w->event;
 
-   filter = (is_dir == 0) ? FILE_NOTIFY_CHANGE_FILE_NAME : FILE_NOTIFY_CHANGE_DIR_NAME;
-   filter |=
+   filter =
      FILE_NOTIFY_CHANGE_ATTRIBUTES |
      FILE_NOTIFY_CHANGE_SIZE |
      FILE_NOTIFY_CHANGE_LAST_WRITE |
      FILE_NOTIFY_CHANGE_LAST_ACCESS |
      FILE_NOTIFY_CHANGE_CREATION |
      FILE_NOTIFY_CHANGE_SECURITY;
+   if (monitor_file)
+     filter |= FILE_NOTIFY_CHANGE_FILE_NAME;
+   else
+     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
 
    if (!ReadDirectoryChangesW(w->handle,
                               (LPVOID)w->buffer,
@@ -228,7 +271,10 @@ _eio_monitor_win32_watcher_new(Eio_Monitor *monitor, unsigned char is_dir)
      goto close_event;
 
    w->monitor = monitor;
-   w->is_dir = is_dir;
+   w->monitor_file = monitor_file;
+   w->monitor_parent = monitor_parent;
+   w->file = file;
+   w->current = current;
 
    return w;
 
@@ -247,6 +293,9 @@ _eio_monitor_win32_watcher_free(Eio_Monitor_Win32_Watcher *w)
 {
    if (!w) return;
 
+   if (w->file)
+     free(w->file);
+   free(w->current);
    CloseHandle(w->event);
    CloseHandle (w->handle);
    free (w);
@@ -274,37 +323,80 @@ void eio_monitor_backend_shutdown(void)
 
 void eio_monitor_backend_add(Eio_Monitor *monitor)
 {
+   char path[PATH_MAX];
+   struct _stat s;
+   char *res;
+   char *current;
+   char *file = NULL;
    Eio_Monitor_Backend *backend;
+   int ret;
 
-   backend = calloc(1, sizeof (Eio_Monitor_Backend));
-   if (!backend)
-     {
-        eio_monitor_fallback_add(monitor);
-        return;
-     }
+   res = _fullpath(path, monitor->path, MAX_PATH);
+   if (!res)
+     goto fallback;
 
-   backend->parent = monitor;
-   backend->file = _eio_monitor_win32_watcher_new(monitor, 0);
-   if (!backend->file)
+   ret = _stat(res, &s);
+   if (ret != 0)
+     goto fallback;
+
+   if (_S_IFDIR & s.st_mode)
      {
-        INF("falling back to poll monitoring");
-        free(backend);
-        eio_monitor_fallback_add(monitor);
-        return;
+        current = strdup(path);
+        if (!current)
+          goto fallback;
      }
-
-   backend->dir = _eio_monitor_win32_watcher_new(monitor, 1);
-   if (!backend->dir)
+   else if (_S_IFREG & s.st_mode)
      {
-        INF("falling back to poll monitoring");
-        _eio_monitor_win32_watcher_free(backend->file);
-        free(backend);
-        eio_monitor_fallback_add(monitor);
-        return;
+        char *tmp;
+
+        tmp = strrchr(path, '\\');
+        file = strdup(tmp + 1);
+        if (!file)
+          goto fallback;
+
+        *tmp = '\0';
+        current = strdup(path);
+        if (!current)
+          {
+             free(file);
+             goto fallback;
+          }
      }
+   else
+     goto fallback;
+
+   backend = calloc(1, sizeof (Eio_Monitor_Backend));
+   if (!backend)
+     goto fallback;
+
+   backend->parent = monitor;
+
+   backend->watcher_file = _eio_monitor_win32_watcher_new(monitor, current, file, EINA_TRUE, EINA_FALSE);
+   if (!backend->watcher_file)
+     goto free_backend;
+
+   backend->watcher_dir = _eio_monitor_win32_watcher_new(monitor, current, file, EINA_FALSE, EINA_FALSE);
+   if (!backend->watcher_dir)
+     goto free_backend_file;
+
+   backend->watcher_parent = _eio_monitor_win32_watcher_new(monitor, current, file, EINA_FALSE, EINA_TRUE);
+   if (!backend->watcher_parent)
+     goto free_backend_dir;
 
    _eio_monitor_win32_native = EINA_TRUE;
    monitor->backend = backend;
+
+   return;
+
+ free_backend_dir:
+   _eio_monitor_win32_watcher_free(backend->watcher_dir);
+ free_backend_file:
+   _eio_monitor_win32_watcher_free(backend->watcher_file);
+ free_backend:
+   free(backend);
+ fallback:
+   INF("falling back to poll monitoring");
+   eio_monitor_fallback_add(monitor);
 }
 
 void eio_monitor_backend_del(Eio_Monitor *monitor)
@@ -315,8 +407,9 @@ void eio_monitor_backend_del(Eio_Monitor *monitor)
         return ;
      }
 
-   _eio_monitor_win32_watcher_free(monitor->backend->file);
-   _eio_monitor_win32_watcher_free(monitor->backend->dir);
+   _eio_monitor_win32_watcher_free(monitor->backend->watcher_parent);
+   _eio_monitor_win32_watcher_free(monitor->backend->watcher_dir);
+   _eio_monitor_win32_watcher_free(monitor->backend->watcher_file);
    free(monitor->backend);
    monitor->backend = NULL;
 }