summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfreetz2005-04-14 19:17:28 (GMT)
committercvs2git2012-06-24 12:13:13 (GMT)
commit999608656e8dfef743135f52bd3af70951c7af75 (patch)
tree9d6c6ae93bd74ec08fcef1d7d804d2559096a42e
parenta0b8e7912fcbcde2ef9ed2e2f6a6f716a5d2c797 (diff)
downloadgamin-999608656e8dfef743135f52bd3af70951c7af75.zip
gamin-999608656e8dfef743135f52bd3af70951c7af75.tar.gz
- for inotify 0.22
Changed files: gamin-inotify-redux-3.patch -> 1.1
-rw-r--r--gamin-inotify-redux-3.patch964
1 files changed, 964 insertions, 0 deletions
diff --git a/gamin-inotify-redux-3.patch b/gamin-inotify-redux-3.patch
new file mode 100644
index 0000000..4637d15
--- /dev/null
+++ b/gamin-inotify-redux-3.patch
@@ -0,0 +1,964 @@
+Index: server/gam_debugging.h
+===================================================================
+RCS file: /cvs/gnome/gamin/server/gam_debugging.h,v
+retrieving revision 1.2
+diff -u -r1.2 gam_debugging.h
+--- server/gam_debugging.h 23 Mar 2005 09:50:11 -0000 1.2
++++ server/gam_debugging.h 14 Apr 2005 16:44:34 -0000
+@@ -12,7 +12,12 @@
+ GAMDnotifyDelete=2,
+ GAMDnotifyChange=3,
+ GAMDnotifyFlowOn=4,
+- GAMDnotifyFlowOff=5
++ GAMDnotifyFlowOff=5,
++ GAMinotifyCreate=6,
++ GAMinotifyDelete=7,
++ GAMinotifyChange=8,
++ GAMinotifyFlowOn=9,
++ GAMinotifyFlowOff=10
+ } GAMDebugEvent;
+
+ void gam_debug_add(GamConnDataPtr conn, const char *value, int options);
+Index: server/gam_inotify.c
+===================================================================
+RCS file: /cvs/gnome/gamin/server/gam_inotify.c,v
+retrieving revision 1.17
+diff -u -r1.17 gam_inotify.c
+--- server/gam_inotify.c 7 Apr 2005 09:11:17 -0000 1.17
++++ server/gam_inotify.c 14 Apr 2005 16:44:34 -0000
+@@ -1,5 +1,8 @@
+-/*
+- * Copyright (C) 2004 John McCutchan, James Willcox, Corey Bowers
++/* gamin inotify backend
++ * Copyright (C) 2005 John McCutchan
++ *
++ * Based off of code,
++ * Copyright (C) 2003 James Willcox, Corey Bowers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+@@ -14,261 +17,404 @@
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+- * TODO:
+- * - *properly* Handle removal of subscriptions when we get IGNORE event
+- * - this backend does not produce the same events as the dnotify/poll backend.
+- * for example, the dp backend allows for watching non-exist files/folders,
+- * and be notified when they are created. there are more places where
+- * the events are not consistent.
+ */
+
+
+ #include <config.h>
+ #define _GNU_SOURCE
+ #include <fcntl.h>
+-#include <sys/ioctl.h>
+ #include <signal.h>
+ #include <unistd.h>
++#include <sys/ioctl.h>
+ #include <stdio.h>
+-#include <string.h>
+ #include <glib.h>
++#include "gam_error.h"
++#include "gam_poll.h"
+ #ifdef HAVE_LINUX_INOTIFY_H
+ #include <linux/inotify.h>
+ #else
+ #include "local_inotify.h"
+ #endif
+-#include "gam_error.h"
+ #include "gam_inotify.h"
+ #include "gam_tree.h"
+ #include "gam_event.h"
+ #include "gam_server.h"
+ #include "gam_event.h"
++#ifdef GAMIN_DEBUG_API
++#include "gam_debugging.h"
++#endif
++
++#define MIN_POLL_TIME 1.0
+
+ typedef struct {
+ char *path;
+- char *path_file;
+ int wd;
+ int refcount;
+ GList *subs;
+-} INotifyData;
++ int busy;
++
++ gboolean dirty;
++ GTimer *poll_timer;
++} inotify_data_t;
+
+ static GHashTable *path_hash = NULL;
+ static GHashTable *wd_hash = NULL;
+-
+-static GList *new_subs = NULL;
+-
+-G_LOCK_DEFINE_STATIC(new_subs);
+-static GList *removed_subs = NULL;
+-
+-G_LOCK_DEFINE_STATIC(removed_subs);
++static GList *dirty_list = NULL;
+
+ G_LOCK_DEFINE_STATIC(inotify);
++
+ static GIOChannel *inotify_read_ioc = NULL;
+
+ static gboolean have_consume_idler = FALSE;
+
+-int fd = -1; // the device fd
++static int inotify_device_fd = -1;
++
++static guint should_poll_mask = IN_MODIFY|IN_ATTRIB|IN_CLOSE_WRITE|IN_MOVED_FROM|IN_MOVED_TO|IN_DELETE_SUBDIR|IN_DELETE_FILE|IN_CREATE_SUBDIR|IN_CREATE_FILE|IN_DELETE_SELF|IN_UNMOUNT;
++
++static void print_mask(int mask)
++{
++ if (mask & IN_ACCESS)
++ {
++ GAM_DEBUG(DEBUG_INFO, "ACCESS\n");
++ }
++ if (mask & IN_MODIFY)
++ {
++ GAM_DEBUG(DEBUG_INFO, "MODIFY\n");
++ }
++ if (mask & IN_ATTRIB)
++ {
++ GAM_DEBUG(DEBUG_INFO, "ATTRIB\n");
++ }
++ if (mask & IN_CLOSE_WRITE)
++ {
++ GAM_DEBUG(DEBUG_INFO, "CLOSE_WRITE\n");
++ }
++ if (mask & IN_CLOSE_NOWRITE)
++ {
++ GAM_DEBUG(DEBUG_INFO, "CLOSE_WRITE\n");
++ }
++ if (mask & IN_OPEN)
++ {
++ GAM_DEBUG(DEBUG_INFO, "OPEN\n");
++ }
++ if (mask & IN_MOVED_FROM)
++ {
++ GAM_DEBUG(DEBUG_INFO, "MOVE_FROM\n");
++ }
++ if (mask & IN_MOVED_TO)
++ {
++ GAM_DEBUG(DEBUG_INFO, "MOVE_TO\n");
++ }
++ if (mask & IN_DELETE_SUBDIR)
++ {
++ GAM_DEBUG(DEBUG_INFO, "DELETE_SUBDIR\n");
++ }
++ if (mask & IN_DELETE_FILE)
++ {
++ GAM_DEBUG(DEBUG_INFO, "DELETE_FILE\n");
++ }
++ if (mask & IN_CREATE_SUBDIR)
++ {
++ GAM_DEBUG(DEBUG_INFO, "CREATE_SUBDIR\n");
++ }
++ if (mask & IN_CREATE_FILE)
++ {
++ GAM_DEBUG(DEBUG_INFO, "CREATE_FILE\n");
++ }
++ if (mask & IN_DELETE_SELF)
++ {
++ GAM_DEBUG(DEBUG_INFO, "DELETE_SELF\n");
++ }
++ if (mask & IN_UNMOUNT)
++ {
++ GAM_DEBUG(DEBUG_INFO, "UNMOUNT\n");
++ }
++ if (mask & IN_Q_OVERFLOW)
++ {
++ GAM_DEBUG(DEBUG_INFO, "Q_OVERFLOW\n");
++ }
++ if (mask & IN_IGNORED)
++ {
++ GAM_DEBUG(DEBUG_INFO, "IGNORED\n");
++ }
++}
+
+-static INotifyData *
+-gam_inotify_data_new(const char *path, char *path_file, int wd)
++static inotify_data_t *
++gam_inotify_data_new(const char *path, int wd)
+ {
+- INotifyData *data;
++ inotify_data_t *data;
+
+- data = g_new0(INotifyData, 1);
++ data = g_new0(inotify_data_t, 1);
+ data->path = g_strdup(path);
+- data->path_file = path_file;
+ data->wd = wd;
++ data->busy = 0;
+ data->refcount = 1;
+- data->subs = NULL;
++ data->dirty = FALSE;
++ data->poll_timer = g_timer_new ();
+
+ return data;
+ }
+
+ static void
+-gam_inotify_data_free(INotifyData * data)
++gam_inotify_data_free(inotify_data_t * data)
+ {
++ if (data->refcount != 0)
++ GAM_DEBUG(DEBUG_INFO, "gam_inotify_data_free called with reffed data.\n");
+ g_free(data->path);
+- g_free(data->path_file);
++ g_timer_destroy (data->poll_timer);
+ g_free(data);
+ }
+
+ static void
+-gam_inotify_add_rm_handler(const char *path, GamSubscription * sub,
+- pollHandlerMode mode)
++gam_inotify_directory_handler_internal(const char *path, pollHandlerMode mode)
+ {
+- INotifyData *data;
++ inotify_data_t *data;
++ int path_fd;
++ int path_wd;
+ struct inotify_watch_request iwr;
+- struct stat st;
+- char *path_file;
+- char *path_t;
+- int wd, r;
+
++
++ switch (mode) {
++ case GAMIN_ACTIVATE:
++ GAM_DEBUG(DEBUG_INFO, "Adding %s to inotify\n", path);
++ break;
++ case GAMIN_DESACTIVATE:
++ GAM_DEBUG(DEBUG_INFO, "Removing %s from inotify\n", path);
++ break;
++ case GAMIN_FLOWCONTROLSTART:
++ GAM_DEBUG(DEBUG_INFO, "Start flow control for %s\n", path);
++ break;
++ case GAMIN_FLOWCONTROLSTOP:
++ GAM_DEBUG(DEBUG_INFO, "Stop flow control for %s\n", path);
++ break;
++ default:
++ gam_error(DEBUG_INFO, "Unknown inotify operation %d for %s\n",
++ mode, path);
++ return;
++ }
+ G_LOCK(inotify);
+
+ if (mode == GAMIN_ACTIVATE) {
+- GList *subs;
+-
+- subs = NULL;
+- subs = g_list_append(subs, sub);
+-
+ if ((data = g_hash_table_lookup(path_hash, path)) != NULL) {
+ data->refcount++;
+- data->subs = g_list_prepend(data->subs, sub);
++ GAM_DEBUG(DEBUG_INFO, " found incremented refcount: %d\n",
++ data->refcount);
+ G_UNLOCK(inotify);
++#ifdef GAMIN_DEBUG_API
++ gam_debug_report(GAMinotifyChange, path, data->refcount);
++#endif
+ GAM_DEBUG(DEBUG_INFO, "inotify updated refcount\n");
+- /*
+- * hum might need some work to check if the path is a dir,
+- * setting 0 and forcing to bypass checks right now.
+- */
+- gam_server_emit_event(path, 0, GAMIN_EVENT_EXISTS, subs, 1);
+- gam_server_emit_event(path, 0, GAMIN_EVENT_ENDEXISTS, subs, 1);
+ return;
+ }
+
+- {
+- if (stat(path, &st)) {
+- G_UNLOCK(inotify);
+- return;
+- }
+-
+- path_t = g_strdup(path);
+- path_file = NULL;
+-
+- if (S_ISREG(st.st_mode)) {
+- char *ch;
+-
+- ch = strrchr(path_t, '/');
+- if (!ch) {
+- g_free(path_t);
+- G_UNLOCK(inotify);
+- return;
+- }
+- path_file = g_strdup(++ch);
+- *ch = '\0';
+- }
+-
+-
+- int file_fd = open(path_t, O_RDONLY);
++ path_fd = open(path, O_RDONLY);
+
+- g_free(path_t);
+- if (file_fd < 0) {
+- G_UNLOCK(inotify);
+- return;
+- }
+-
+- iwr.fd = file_fd;
+- iwr.mask = 0xffffffff; // all events
+- wd = ioctl(fd, INOTIFY_WATCH, &iwr);
+- close(file_fd);
+- }
+-
+- if (wd < 0) {
++ if (path_fd < 0) {
+ G_UNLOCK(inotify);
+ return;
+ }
+
+- data = gam_inotify_data_new(path, path_file, wd);
+- path_file = NULL;
+- data->subs = g_list_prepend(data->subs, sub);
++ iwr.fd = path_fd;
++ iwr.mask = should_poll_mask;
++ path_wd = ioctl (inotify_device_fd, INOTIFY_WATCH, &iwr);
++ close (path_fd);
++
++ data = gam_inotify_data_new(path, path_wd);
+ g_hash_table_insert(wd_hash, GINT_TO_POINTER(data->wd), data);
+ g_hash_table_insert(path_hash, data->path, data);
+
+- GAM_DEBUG(DEBUG_INFO, "added inotify watch for %s\n", path);
+-
+- gam_server_emit_event(path, 0, GAMIN_EVENT_EXISTS, subs, 1);
+- gam_server_emit_event(path, 0, GAMIN_EVENT_ENDEXISTS, subs, 1);
++ GAM_DEBUG(DEBUG_INFO, "activated inotify for %s\n", path);
++#ifdef GAMIN_DEBUG_API
++ gam_debug_report(GAMinotifyCreate, path, 0);
++#endif
+ } else if (mode == GAMIN_DESACTIVATE) {
+- data = g_hash_table_lookup(path_hash, path);
++ char *dir = (char *) path;
+
+- if (!data) {
+- G_UNLOCK(inotify);
+- return;
+- }
++ data = g_hash_table_lookup(path_hash, path);
++
++ if (!data) {
++ dir = g_path_get_dirname(path);
++ data = g_hash_table_lookup(path_hash, dir);
++
++ if (!data) {
++ GAM_DEBUG(DEBUG_INFO, " not found !!!\n");
++
++ if (dir != NULL)
++ g_free(dir);
++
++ G_UNLOCK(inotify);
++ return;
++ }
++ GAM_DEBUG(DEBUG_INFO, " not found using parent\n");
++ }
+
+- if (g_list_find(data->subs, sub)) {
+- data->subs = g_list_remove_all(data->subs, sub);
+- }
+ data->refcount--;
+ GAM_DEBUG(DEBUG_INFO, "inotify decremeneted refcount for %s\n",
+ path);
+
+ if (data->refcount == 0) {
+- r = ioctl(fd, INOTIFY_IGNORE, &data->wd);
+- if (r < 0) {
+- GAM_DEBUG(DEBUG_INFO,
+- "INOTIFY_IGNORE failed for %s (wd = %d)\n",
+- data->path, data->wd);
++ int wd = data->wd;
++
++ GAM_DEBUG(DEBUG_INFO, "removed inotify watch for %s\n", data->path);
++
++ g_hash_table_remove(path_hash, data->path);
++ g_hash_table_remove(wd_hash, GINT_TO_POINTER(data->wd));
++ gam_inotify_data_free(data);
++
++ if (ioctl (inotify_device_fd, INOTIFY_IGNORE, &wd) < 0) {
++ GAM_DEBUG (DEBUG_INFO, "INOTIFY_IGNORE failed for %s (wd = %d)\n", data->path, data->wd);
++ }
++#ifdef GAMIN_DEBUG_API
++ gam_debug_report(GAMinotifyDelete, dir, 0);
++#endif
++ } else {
++ GAM_DEBUG(DEBUG_INFO, " found decremented refcount: %d\n",
++ data->refcount);
++#ifdef GAMIN_DEBUG_API
++ gam_debug_report(GAMinotifyChange, dir, data->refcount);
++#endif
++ }
++ if ((dir != path) && (dir != NULL))
++ g_free(dir);
++ } else if ((mode == GAMIN_FLOWCONTROLSTART) ||
++ (mode == GAMIN_FLOWCONTROLSTOP)) {
++ char *dir = (char *) path;
++
++ data = g_hash_table_lookup(path_hash, path);
++ if (!data) {
++ dir = g_path_get_dirname(path);
++ data = g_hash_table_lookup(path_hash, dir);
++
++ if (!data) {
++ GAM_DEBUG(DEBUG_INFO, " not found !!!\n");
++
++ if (dir != NULL)
++ g_free(dir);
++ G_UNLOCK(inotify);
++ return;
+ }
+- GAM_DEBUG(DEBUG_INFO, "removed inotify watch for %s\n",
+- data->path);
+- g_hash_table_remove(path_hash, data->path);
+- g_hash_table_remove(wd_hash, GINT_TO_POINTER(data->wd));
+- gam_inotify_data_free(data);
++ GAM_DEBUG(DEBUG_INFO, " not found using parent\n");
+ }
++ if (data != NULL) {
++ if (mode == GAMIN_FLOWCONTROLSTART) {
++ if (data->wd >= 0) {
++ if (ioctl (inotify_device_fd, INOTIFY_IGNORE, &data->wd) < 0) {
++ GAM_DEBUG (DEBUG_INFO, "INOTIFY_IGNORE failed for %s (wd = %d)\n", data->path, data->wd);
++ }
++ g_hash_table_remove(wd_hash, GINT_TO_POINTER(data->wd));
++ data->wd = -1;
++ GAM_DEBUG(DEBUG_INFO, "deactivated inotify for %s\n",
++ data->path);
++#ifdef GAMIN_DEBUG_API
++ gam_debug_report(GAMinotifyFlowOn, dir, 0);
++#endif
++ }
++ data->busy++;
++ } else {
++ if (data->busy > 0) {
++ data->busy--;
++ if (data->busy == 0) {
++ path_fd = open(data->path, O_RDONLY);
++ if (path_fd < 0) {
++ G_UNLOCK(inotify);
++ GAM_DEBUG(DEBUG_INFO,
++ "Failed to reactivate inotify for %s\n",
++ data->path);
++
++ if ((dir != path) && (dir != NULL))
++ g_free(dir);
++ return;
++ }
++
++ iwr.fd = path_fd;
++ iwr.mask = 0xffffffff;
++ path_wd = ioctl (inotify_device_fd, INOTIFY_WATCH, &iwr);
++ close (path_fd);
++
++ data->wd = path_wd;
++
++ g_hash_table_insert(wd_hash, GINT_TO_POINTER(data->wd),
++ data);
++ GAM_DEBUG(DEBUG_INFO, "Reactivated inotify for %s\n",
++ data->path);
++#ifdef GAMIN_DEBUG_API
++ gam_debug_report(GAMinotifyFlowOff, path, 0);
++#endif
++ }
++ }
++ }
++ }
++ if ((dir != path) && (dir != NULL))
++ g_free(dir);
+ } else {
+- GAM_DEBUG(DEBUG_INFO, "Inotify: unimplemented mode request %d\n",
+- mode);
++ GAM_DEBUG(DEBUG_INFO, "Unimplemented operation\n");
+ }
++
+ G_UNLOCK(inotify);
+ }
+
+-static GaminEventType
+-inotify_event_to_gamin_event(int mask)
++static void
++gam_inotify_directory_handler(const char *path, pollHandlerMode mode)
+ {
+- switch (mask) {
+- case IN_ATTRIB:
+- case IN_MODIFY:
+- return GAMIN_EVENT_CHANGED;
+- break;
+- case IN_MOVED_TO:
+- case IN_CREATE_SUBDIR:
+- case IN_CREATE_FILE:
+- return GAMIN_EVENT_CREATED;
+- break;
+- case IN_MOVED_FROM:
+- case IN_DELETE_SUBDIR:
+- case IN_DELETE_FILE:
+- return GAMIN_EVENT_DELETED;
+- break;
+- default:
+- return GAMIN_EVENT_UNKNOWN;
++ GAM_DEBUG(DEBUG_INFO, "gam_inotify_directory_handler %s : %d\n",
++ path, mode);
++
++ if ((mode == GAMIN_DESACTIVATE) ||
++ (g_file_test(path, G_FILE_TEST_IS_DIR))) {
++ gam_inotify_directory_handler_internal(path, mode);
++ } else {
++ char *dir;
++
++ dir = g_path_get_dirname(path);
++ GAM_DEBUG(DEBUG_INFO, " not a dir using parent %s\n", dir);
++ gam_inotify_directory_handler_internal(dir, mode);
++ g_free(dir);
+ }
+ }
+
+ static void
+-gam_inotify_emit_event(INotifyData * data, struct inotify_event *event)
++gam_inotify_file_handler(const char *path, pollHandlerMode mode)
+ {
+- GaminEventType gevent;
+- char *event_path;
+-
+- if (!data || !event)
+- return;
+- gevent = inotify_event_to_gamin_event(event->mask);
++ GAM_DEBUG(DEBUG_INFO, "gam_inotify_file_handler %s : %d\n", path, mode);
++
++ if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
++ gam_inotify_directory_handler_internal(path, mode);
++ } else {
++ char *dir;
+
+- // gamins event vocabulary is very small compared to inotify
+- // so we often will receieve events that have no equivelant
+- // in gamin
+- if (gevent == GAMIN_EVENT_UNKNOWN) {
+- return;
++ dir = g_path_get_dirname(path);
++ GAM_DEBUG(DEBUG_INFO, " not a dir using parent %s\n", dir);
++ gam_inotify_directory_handler_internal(dir, mode);
++ g_free(dir);
+ }
+- if (event->name[0] != '\0' && !data->path_file) {
+- int pathlen = strlen(data->path);
++}
+
+- if (data->path[pathlen - 1] == '/') {
+- event_path = g_strconcat(data->path, event->name, NULL);
+- } else {
+- event_path = g_strconcat(data->path, "/", event->name, NULL);
+- }
+- } else {
+- event_path = g_strdup(data->path);
+- }
++/* Must be called with inotify lock locked */
++static void
++gam_inotify_dirty_list_cleaner ()
++{
++ GList *l;
++
++ /* Here we walk the old dirty list and create a new one.
++ * if we don't poll a node on the old list, we add it to the new one */
++
++ l = dirty_list;
++ dirty_list = NULL;
+
+- GAM_DEBUG(DEBUG_INFO, "inotify emitting event %s for %s\n",
+- gam_event_to_string(gevent), event_path);
++ for (l = l; l; l = l->next) {
++ inotify_data_t *data = l->data;
+
+- gam_server_emit_event(event_path, 0, gevent, data->subs, 1);
++ g_assert (data->dirty);
++
++ if (g_timer_elapsed (data->poll_timer, NULL) >= MIN_POLL_TIME) {
++ data->dirty = FALSE;
++ gam_poll_scan_directory (data->path);
++ } else {
++ dirty_list = g_list_append (dirty_list, data);
++ }
++ }
+
+- g_free(event_path);
++ g_list_free (l);
+ }
+
+ static gboolean
+@@ -276,71 +422,76 @@
+ {
+ char *buffer;
+ int buffer_size;
++ int events;
+ gsize buffer_i, read_size;
+
+ G_LOCK(inotify);
+
+- if (ioctl(fd, FIONREAD, &buffer_size) < 0) {
+- G_UNLOCK(inotify);
+- GAM_DEBUG(DEBUG_INFO, "inotify FIONREAD < 0. kaboom!\n");
+- return FALSE;
++#if 0
++ gam_inotify_dirty_list_cleaner ();
++#endif
++
++ if (ioctl(inotify_device_fd, FIONREAD, &buffer_size) < 0) {
++ G_UNLOCK(inotify);
++ GAM_DEBUG(DEBUG_INFO, "inotify FIONREAD < 0. kaboom!\n");
++ return FALSE;
+ }
+
+ buffer = g_malloc(buffer_size);
+
+- if (g_io_channel_read_chars
+- (inotify_read_ioc, (char *) buffer, buffer_size, &read_size,
+- NULL) != G_IO_STATUS_NORMAL) {
+- G_UNLOCK(inotify);
+- GAM_DEBUG(DEBUG_INFO,
+- "inotify failed to read events from inotify fd.\n");
+- g_free(buffer);
+- return FALSE;
++ if (g_io_channel_read_chars(inotify_read_ioc, (char *)buffer, buffer_size, &read_size, NULL) != G_IO_STATUS_NORMAL) {
++ G_UNLOCK(inotify);
++ GAM_DEBUG(DEBUG_INFO, "inotify failed to read events from inotify fd.\n");
++ g_free (buffer);
++ return FALSE;
+ }
+
+ buffer_i = 0;
++ events = 0;
+ while (buffer_i < read_size) {
+- struct inotify_event *event;
+- gsize event_size;
+- INotifyData *data;
+-
+- event = (struct inotify_event *) &buffer[buffer_i];
+- event_size = sizeof(struct inotify_event) + event->len;
+-
+- data = g_hash_table_lookup(wd_hash, GINT_TO_POINTER(event->wd));
+- if (!data) {
+- GAM_DEBUG(DEBUG_INFO, "inotify can't find wd %d\n", event->wd);
+- GAM_DEBUG(DEBUG_INFO,
+- "weird things have happened to inotify.\n");
+- } else {
+- /* Do the shit with the event */
+- if (event->mask == IN_IGNORED) {
+- GList *l;
+-
+- l = data->subs;
+- data->subs = NULL;
+- for (l = l; l; l = l->next) {
+- GamSubscription *sub = l->data;
+-
+- gam_inotify_remove_subscription(sub);
+- }
+- } else {
+- if (data->path_file) {
+- if (event->name[0] != '\0') {
+- int pathlen = strlen(data->path_file);
+-
+- if (strcmp(data->path_file, event->name)) {
+- buffer_i += event_size;
+- continue;
+- }
+- }
+- }
+- gam_inotify_emit_event(data, event);
+- }
+- }
++ struct inotify_event *event;
++ gsize event_size;
++ inotify_data_t *data;
++
++ event = (struct inotify_event *)&buffer[buffer_i];
++ event_size = sizeof(struct inotify_event) + event->len;
++
++ data = g_hash_table_lookup (wd_hash, GINT_TO_POINTER(event->wd));
++ if (!data) {
++ GAM_DEBUG(DEBUG_INFO, "inotify can't find wd %d\n", event->wd);
++ } else {
++ if (event->mask == IN_IGNORED || event->mask == IN_UNMOUNT) {
++ GList *l;
++
++ l = data->subs;
++ data->subs = NULL;
++ for (l = l; l; l = l->next) {
++ GamSubscription *sub = l->data;
++ gam_inotify_remove_subscription (sub);
++ }
++ } else if (event->mask != IN_Q_OVERFLOW) {
++ if (event->mask & should_poll_mask) {
++ GAM_DEBUG(DEBUG_INFO, "inotify requesting poll for %s\n", data->path);
++ GAM_DEBUG(DEBUG_INFO, "poll was requested for event = ");
++ print_mask (event->mask);
++ gam_poll_scan_directory (data->path);
++#if 0
++ /* if node isn't dirty */
++ if (!data->dirty) {
++ /* Put this node on the dirty list */
++ data->dirty = TRUE;
++ g_timer_start(data->poll_timer);
++ dirty_list = g_list_append (dirty_list, data);
++ }
++#endif
++ }
++ }
++ }
+
+ buffer_i += event_size;
++ events++;
+ }
++ GAM_DEBUG(DEBUG_INFO, "inotify recieved %d events\n", events);
+
+ g_free(buffer);
+ G_UNLOCK(inotify);
+@@ -348,48 +499,12 @@
+ return TRUE;
+ }
+
++
+ static gboolean
+ gam_inotify_consume_subscriptions_real(gpointer data)
+ {
+- GList *subs, *l;
+-
+- G_LOCK(new_subs);
+- if (new_subs) {
+- subs = new_subs;
+- new_subs = NULL;
+- G_UNLOCK(new_subs);
+-
+- for (l = subs; l; l = l->next) {
+- GamSubscription *sub = l->data;
+-
+- GAM_DEBUG(DEBUG_INFO, "called gam_inotify_add_handler()\n");
+- gam_inotify_add_rm_handler(gam_subscription_get_path(sub), sub,
+- TRUE);
+- }
+-
+- } else {
+- G_UNLOCK(new_subs);
+- }
+-
+- G_LOCK(removed_subs);
+- if (removed_subs) {
+- subs = removed_subs;
+- removed_subs = NULL;
+- G_UNLOCK(removed_subs);
+-
+- for (l = subs; l; l = l->next) {
+- GamSubscription *sub = l->data;
+-
+- GAM_DEBUG(DEBUG_INFO, "called gam_inotify_rm_handler()\n");
+- gam_inotify_add_rm_handler(gam_subscription_get_path(sub), sub,
+- FALSE);
+- }
+- } else {
+- G_UNLOCK(removed_subs);
+- }
+-
+- GAM_DEBUG(DEBUG_INFO, "gam_inotify_consume_subscriptions()\n");
+-
++ GAM_DEBUG(DEBUG_INFO, "gam_inotify_consume_subscriptions_real()\n");
++ gam_poll_consume_subscriptions();
+ have_consume_idler = FALSE;
+ return FALSE;
+ }
+@@ -402,30 +517,31 @@
+ if (have_consume_idler)
+ return;
+
++ GAM_DEBUG(DEBUG_INFO, "gam_inotify_consume_subscriptions()\n");
+ have_consume_idler = TRUE;
+-
+ source = g_idle_source_new();
+ g_source_set_callback(source, gam_inotify_consume_subscriptions_real,
+ NULL, NULL);
+-
+ g_source_attach(source, NULL);
+ }
+
+ /**
+- * @defgroup inotify inotify backend
++ * @defgroup inotify inotify Backend
+ * @ingroup Backends
+ * @brief inotify backend API
+ *
+ * Since version 2.6.X, Linux kernels have included the Linux Inode
+ * Notification system (inotify). This backend uses inotify to know when
+- * files are changed/created/deleted.
++ * files are changed/created/deleted. Since inotify can't watch files/dirs that
++ * don't exist we still have to cache stat() information. For this,
++ * we can just use the code in the polling backend.
+ *
+ * @{
+ */
+
+
+ /**
+- * Initializes the inotify system. This must be called before
++ * Initializes the inotify backend. This must be called before
+ * any other functions in this module.
+ *
+ * @returns TRUE if initialization succeeded, FALSE otherwise
+@@ -435,14 +551,16 @@
+ {
+ GSource *source;
+
+- fd = open("/dev/inotify", O_RDONLY);
++ inotify_device_fd = open("/dev/inotify", O_RDONLY);
+
+- if (fd < 0) {
+- GAM_DEBUG(DEBUG_INFO, "Could not open /dev/inotify\n");
+- return FALSE;
++ if (inotify_device_fd < 0) {
++ GAM_DEBUG(DEBUG_INFO, "Could not open /dev/inotify\n");
++ return FALSE;
+ }
+
+- inotify_read_ioc = g_io_channel_unix_new(fd);
++ g_return_val_if_fail(gam_poll_init_full(FALSE), FALSE);
++
++ inotify_read_ioc = g_io_channel_unix_new(inotify_device_fd);
+
+ /* For binary data */
+ g_io_channel_set_encoding(inotify_read_ioc, NULL, NULL);
+@@ -450,13 +568,15 @@
+ g_io_channel_set_flags(inotify_read_ioc, G_IO_FLAG_NONBLOCK, NULL);
+
+ source = g_io_create_watch(inotify_read_ioc,
+- G_IO_IN | G_IO_HUP | G_IO_ERR);
++ G_IO_IN | G_IO_HUP | G_IO_ERR);
+ g_source_set_callback(source, gam_inotify_read_handler, NULL, NULL);
+
+ g_source_attach(source, NULL);
+
+ path_hash = g_hash_table_new(g_str_hash, g_str_equal);
+ wd_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
++ gam_poll_set_directory_handler(gam_inotify_directory_handler);
++ gam_poll_set_file_handler(gam_inotify_file_handler);
+
+ GAM_DEBUG(DEBUG_INFO, "inotify initialized\n");
+
+@@ -476,15 +596,14 @@
+ gboolean
+ gam_inotify_add_subscription(GamSubscription * sub)
+ {
+- gam_listener_add_subscription(gam_subscription_get_listener(sub), sub);
+-
+- G_LOCK(new_subs);
+- new_subs = g_list_prepend(new_subs, sub);
+- G_UNLOCK(new_subs);
+-
+- GAM_DEBUG(DEBUG_INFO, "inotify_add_sub\n");
++ GAM_DEBUG(DEBUG_INFO, "gam_inotify_add_subscription\n");
++ if (!gam_poll_add_subscription(sub)) {
++ return FALSE;
++ }
+
+ gam_inotify_consume_subscriptions();
++
++ GAM_DEBUG(DEBUG_INFO, "gam_inotify_add_subscription: done\n");
+ return TRUE;
+ }
+
+@@ -497,26 +616,15 @@
+ gboolean
+ gam_inotify_remove_subscription(GamSubscription * sub)
+ {
+- G_LOCK(new_subs);
+- if (g_list_find(new_subs, sub)) {
+- GAM_DEBUG(DEBUG_INFO, "removed sub found on new_subs\n");
+- new_subs = g_list_remove_all(new_subs, sub);
+- G_UNLOCK(new_subs);
+- return TRUE;
+- }
+- G_UNLOCK(new_subs);
+-
+- gam_subscription_cancel(sub);
+- gam_listener_remove_subscription(gam_subscription_get_listener(sub),
+- sub);
+-
+- G_LOCK(removed_subs);
+- removed_subs = g_list_prepend(removed_subs, sub);
+- G_UNLOCK(removed_subs);
++ GAM_DEBUG(DEBUG_INFO, "gam_inotify_remove_subscription\n");
++
++ if (!gam_poll_remove_subscription(sub)) {
++ return FALSE;
++ }
+
+- GAM_DEBUG(DEBUG_INFO, "inotify_remove_sub\n");
+ gam_inotify_consume_subscriptions();
+
++ GAM_DEBUG(DEBUG_INFO, "gam_inotify_remove_subscription: done\n");
+ return TRUE;
+ }
+
+@@ -529,26 +637,13 @@
+ gboolean
+ gam_inotify_remove_all_for(GamListener * listener)
+ {
+- GList *subs, *l = NULL;
+-
+- subs = gam_listener_get_subscriptions(listener);
+-
+- for (l = subs; l; l = l->next) {
+- GamSubscription *sub = l->data;
+-
+- g_assert(sub != NULL);
+-
+- gam_inotify_remove_subscription(sub);
+-
+- }
+-
+- if (subs) {
+- g_list_free(subs);
+- gam_inotify_consume_subscriptions();
+- return TRUE;
+- } else {
++ if (!gam_poll_remove_all_for(listener)) {
+ return FALSE;
+ }
++
++ gam_inotify_consume_subscriptions();
++
++ return TRUE;
+ }
+
+ /** @} */
+Index: server/gam_inotify.h
+===================================================================
+RCS file: /cvs/gnome/gamin/server/gam_inotify.h,v
+retrieving revision 1.1
+diff -u -r1.1 gam_inotify.h
+--- server/gam_inotify.h 27 Jul 2004 10:24:43 -0000 1.1
++++ server/gam_inotify.h 14 Apr 2005 16:44:34 -0000
+@@ -1,6 +1,5 @@
+-
+-#ifndef __MD_INOTIFY_H__
+-#define __MD_INOTIFY_H__
++#ifndef __GAM_INOTIFY_H__
++#define __GAM_INOTIFY_H__
+
+ #include <glib.h>
+ #include "gam_poll.h"
+@@ -15,4 +14,4 @@
+
+ G_END_DECLS
+
+-#endif /* __MD_INOTIFY_H__ */
++#endif /* __GAM_INOTIFY_H__ */
+