Mike McCormack : server: Distinguish between a directory and a file changing in

Alexandre Julliard julliard at wine.codeweavers.com
Mon Feb 20 05:41:35 CST 2006


Module: wine
Branch: refs/heads/master
Commit: a2813f7c2ef879fee94948eb4f935448e2f48152
URL:    http://source.winehq.org/git/?p=wine.git;a=commit;h=a2813f7c2ef879fee94948eb4f935448e2f48152

Author: Mike McCormack <mike at codeweavers.com>
Date:   Mon Feb 20 12:28:46 2006 +0100

server: Distinguish between a directory and a file changing in
ReadDirectoryChangesW.
Add a test for it.

---

 dlls/kernel/tests/change.c |   80 ++++++++++++++++++++++++++++++++++++++++++++
 server/change.c            |   67 +++++++++++++++++++++++++++++++------
 2 files changed, 136 insertions(+), 11 deletions(-)

diff --git a/dlls/kernel/tests/change.c b/dlls/kernel/tests/change.c
index 3c8e245..dfdaa9e 100644
--- a/dlls/kernel/tests/change.c
+++ b/dlls/kernel/tests/change.c
@@ -560,6 +560,85 @@ static void test_readdirectorychanges_nu
     ok( r == TRUE, "failed to remove directory\n");
 }
 
+static void test_readdirectorychanges_filedir(void)
+{
+    NTSTATUS r;
+    HANDLE hdir, hfile;
+    char buffer[0x1000];
+    DWORD fflags, filter = 0;
+    OVERLAPPED ov;
+    WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
+    static const WCHAR szBoo[] = { '\\','b','o','o',0 };
+    static const WCHAR szHoo[] = { '\\','h','o','o',0 };
+    static const WCHAR szFoo[] = { '\\','f','o','o',0 };
+    PFILE_NOTIFY_INFORMATION pfni;
+
+    r = GetTempPathW( MAX_PATH, path );
+    ok( r != 0, "temp path failed\n");
+    if (!r)
+        return;
+
+    lstrcatW( path, szBoo );
+    lstrcpyW( subdir, path );
+    lstrcatW( subdir, szHoo );
+
+    lstrcpyW( file, path );
+    lstrcatW( file, szFoo );
+
+    DeleteFileW( file );
+    RemoveDirectoryW( subdir );
+    RemoveDirectoryW( path );
+    
+    r = CreateDirectoryW(path, NULL);
+    ok( r == TRUE, "failed to create directory\n");
+
+    fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
+    hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
+                        FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
+                        OPEN_EXISTING, fflags, NULL);
+    ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
+
+    ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
+
+    filter = FILE_NOTIFY_CHANGE_FILE_NAME;
+
+    r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
+    ok(r==TRUE, "should return true\n");
+
+    r = WaitForSingleObject( ov.hEvent, 10 );
+    ok( r == WAIT_TIMEOUT, "should timeout\n" );
+
+    r = CreateDirectoryW( subdir, NULL );
+    ok( r == TRUE, "failed to create directory\n");
+
+    hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
+    ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
+    ok( CloseHandle(hfile), "failed toc lose file\n");
+
+    r = WaitForSingleObject( ov.hEvent, INFINITE );
+    ok( r == WAIT_OBJECT_0, "event should be ready\n" );
+
+    ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
+    ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
+
+    pfni = (PFILE_NOTIFY_INFORMATION) buffer;
+    ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
+    ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
+    ok( pfni->FileNameLength == 6, "len wrong\n" );
+    ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
+
+    r = DeleteFileW( file );
+    ok( r == TRUE, "failed to delete file\n");
+
+    r = RemoveDirectoryW( subdir );
+    ok( r == TRUE, "failed to remove directory\n");
+
+    CloseHandle(hdir);
+
+    r = RemoveDirectoryW( path );
+    ok( r == TRUE, "failed to remove directory\n");
+}
+
 START_TEST(change)
 {
     HMODULE hkernel32 = GetModuleHandle("kernel32");
@@ -570,4 +649,5 @@ START_TEST(change)
     test_ffcn();
     test_readdirectorychanges();
     test_readdirectorychanges_null();
+    test_readdirectorychanges_filedir();
 }
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 @@ static int inotify_get_poll_events( stru
     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 @@ static unsigned int filter_from_event( s
     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 @@ static void inotify_notify_all( struct i
     }
 
     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 )




More information about the wine-cvs mailing list