server: Distinguish between a directory and a file changing in
ReadDirectoryChangesW.
Add a test for it.
diff --git a/server/change.c b/server/change.c
index aa8e0cb..d0de942 100644
--- a/server/change.c
+++ b/server/change.c
@@ -485,24 +485,20 @@
return POLLIN;
}
-static void inotify_do_change_notify( struct dir *dir, struct inotify_event *ie )
+static void inotify_do_change_notify( struct dir *dir, unsigned int action,
+ const char *relpath )
{
struct change_record *record;
if (dir->want_data)
{
- size_t len = strlen(ie->name);
+ size_t len = strlen(relpath);
record = malloc( offsetof(struct change_record, name[len]) );
if (!record)
return;
- if( ie->mask & IN_CREATE )
- record->action = FILE_ACTION_ADDED;
- else if( ie->mask & IN_DELETE )
- record->action = FILE_ACTION_REMOVED;
- else
- record->action = FILE_ACTION_MODIFIED;
- memcpy( record->name, ie->name, len );
+ record->action = action;
+ memcpy( record->name, relpath, len );
record->len = len;
list_add_tail( &dir->change_records, &record->entry );
@@ -535,11 +531,39 @@
return filter;
}
+static int inode_entry_is_dir( struct inode *inode, const char *name )
+{
+ /* distinguish a created file from a directory */
+ char *path;
+ struct list *head;
+ struct stat st;
+ int unix_fd, r;
+
+ head = list_head( &inode->dirs );
+ if (!head)
+ return -1;
+
+ path = malloc( strlen(name) + 32 );
+ if (!path)
+ return -1;
+
+ unix_fd = get_unix_fd( LIST_ENTRY( head, struct dir, in_entry )->fd );
+ sprintf( path, "/proc/self/fd/%u/%s", unix_fd, name );
+ r = stat( path, &st );
+ free( path );
+ if (-1 == r)
+ return -1;
+
+ if (S_ISDIR(st.st_mode))
+ return 1;
+ return 0;
+}
+
static void inotify_notify_all( struct inotify_event *ie )
{
+ unsigned int filter, action;
struct inode *inode;
struct dir *dir;
- unsigned int filter;
inode = inode_from_wd( ie->wd );
if (!inode)
@@ -549,10 +573,31 @@
}
filter = filter_from_event( ie );
+
+ if (ie->mask & IN_CREATE)
+ {
+ switch (inode_entry_is_dir( inode, ie->name ))
+ {
+ case 1:
+ filter &= ~FILE_NOTIFY_CHANGE_FILE_NAME;
+ break;
+ case 0:
+ filter &= ~FILE_NOTIFY_CHANGE_DIR_NAME;
+ break;
+ default:
+ break;
+ /* Maybe the file disappeared before we could check it? */
+ }
+ action = FILE_ACTION_ADDED;
+ }
+ else if (ie->mask & IN_DELETE)
+ action = FILE_ACTION_REMOVED;
+ else
+ action = FILE_ACTION_MODIFIED;
LIST_FOR_EACH_ENTRY( dir, &inode->dirs, struct dir, in_entry )
if (filter & dir->filter)
- inotify_do_change_notify( dir, ie );
+ inotify_do_change_notify( dir, action, ie->name );
}
static void inotify_poll_event( struct fd *fd, int event )