Module: wine Branch: refs/heads/master Commit: a2813f7c2ef879fee94948eb4f935448e2f48152 URL: http://source.winehq.org/git/?p=wine.git;a=commit;h=a2813f7c2ef879fee94948eb...
Author: Mike McCormack mike@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 )