Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Server-side change notification management |
| 3 | * |
| 4 | * Copyright (C) 1998 Alexandre Julliard |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 5 | * Copyright (C) 2006 Mike McCormack |
Alexandre Julliard | 0799c1a | 2002-03-09 23:29:33 +0000 | [diff] [blame] | 6 | * |
| 7 | * This library is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public |
| 9 | * License as published by the Free Software Foundation; either |
| 10 | * version 2.1 of the License, or (at your option) any later version. |
| 11 | * |
| 12 | * This library is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * Lesser General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU Lesser General Public |
| 18 | * License along with this library; if not, write to the Free Software |
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 20 | */ |
| 21 | |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 22 | #include "config.h" |
| 23 | #include "wine/port.h" |
| 24 | |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 25 | #include <assert.h> |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 26 | #include <fcntl.h> |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 27 | #include <stdio.h> |
| 28 | #include <stdlib.h> |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 29 | #include <signal.h> |
Hans Leidekker | ff49652 | 2004-02-05 01:45:58 +0000 | [diff] [blame] | 30 | #include <sys/stat.h> |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 31 | #include <errno.h> |
| 32 | #ifdef HAVE_SYS_ERRNO_H |
| 33 | #include <sys/errno.h> |
| 34 | #endif |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 35 | |
Ge van Geldorp | 1a1583a | 2005-11-28 17:32:54 +0100 | [diff] [blame] | 36 | #include "ntstatus.h" |
| 37 | #define WIN32_NO_STATUS |
Alexandre Julliard | 435e2e6 | 2002-12-10 22:56:43 +0000 | [diff] [blame] | 38 | #include "windef.h" |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 39 | |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 40 | #include "file.h" |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 41 | #include "handle.h" |
| 42 | #include "thread.h" |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 43 | #include "request.h" |
Ge van Geldorp | 1a1583a | 2005-11-28 17:32:54 +0100 | [diff] [blame] | 44 | #include "winternl.h" |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 45 | |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 46 | /* dnotify support */ |
| 47 | |
Huw Davies | bdb63ec | 2004-02-10 20:08:24 +0000 | [diff] [blame] | 48 | #ifdef linux |
| 49 | #ifndef F_NOTIFY |
| 50 | #define F_NOTIFY 1026 |
| 51 | #define DN_ACCESS 0x00000001 /* File accessed */ |
| 52 | #define DN_MODIFY 0x00000002 /* File modified */ |
| 53 | #define DN_CREATE 0x00000004 /* File created */ |
| 54 | #define DN_DELETE 0x00000008 /* File removed */ |
| 55 | #define DN_RENAME 0x00000010 /* File renamed */ |
| 56 | #define DN_ATTRIB 0x00000020 /* File changed attibutes */ |
| 57 | #define DN_MULTISHOT 0x80000000 /* Don't remove notifier */ |
| 58 | #endif |
| 59 | #endif |
| 60 | |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 61 | /* inotify support */ |
| 62 | |
| 63 | #if defined(__linux__) && defined(__i386__) |
| 64 | |
| 65 | #define SYS_inotify_init 291 |
| 66 | #define SYS_inotify_add_watch 292 |
| 67 | #define SYS_inotify_rm_watch 293 |
| 68 | |
| 69 | struct inotify_event { |
| 70 | int wd; |
| 71 | unsigned int mask; |
| 72 | unsigned int cookie; |
| 73 | unsigned int len; |
| 74 | char name[1]; |
| 75 | }; |
| 76 | |
| 77 | #define IN_ACCESS 0x00000001 |
| 78 | #define IN_MODIFY 0x00000002 |
| 79 | #define IN_ATTRIB 0x00000004 |
| 80 | #define IN_CLOSE_WRITE 0x00000008 |
| 81 | #define IN_CLOSE_NOWRITE 0x00000010 |
| 82 | #define IN_OPEN 0x00000020 |
| 83 | #define IN_MOVED_FROM 0x00000040 |
| 84 | #define IN_MOVED_TO 0x00000080 |
| 85 | #define IN_CREATE 0x00000100 |
| 86 | #define IN_DELETE 0x00000200 |
| 87 | #define IN_DELETE_SELF 0x00000400 |
| 88 | |
| 89 | static inline int inotify_init( void ) |
| 90 | { |
| 91 | int ret; |
| 92 | __asm__ __volatile__( "int $0x80" |
| 93 | : "=a" (ret) |
| 94 | : "0" (SYS_inotify_init)); |
| 95 | if (ret<0) { errno = -ret; ret = -1; } |
| 96 | return ret; |
| 97 | } |
| 98 | |
| 99 | static inline int inotify_add_watch( int fd, const char *name, unsigned int mask ) |
| 100 | { |
| 101 | int ret; |
| 102 | __asm__ __volatile__( "pushl %%ebx;\n\t" |
| 103 | "movl %2,%%ebx;\n\t" |
| 104 | "int $0x80;\n\t" |
| 105 | "popl %%ebx" |
| 106 | : "=a" (ret) : "0" (SYS_inotify_add_watch), |
| 107 | "r" (fd), "c" (name), "d" (mask) ); |
| 108 | if (ret<0) { errno = -ret; ret = -1; } |
| 109 | return ret; |
| 110 | } |
| 111 | |
| 112 | static inline int inotify_remove_watch( int fd, int wd ) |
| 113 | { |
| 114 | int ret; |
| 115 | __asm__ __volatile__( "pushl %%ebx;\n\t" |
| 116 | "movl %2,%%ebx;\n\t" |
| 117 | "int $0x80;\n\t" |
| 118 | "popl %%ebx" |
| 119 | : "=a" (ret) : "0" (SYS_inotify_rm_watch), |
| 120 | "r" (fd), "c" (wd) ); |
| 121 | if (ret<0) { errno = -ret; ret = -1; } |
| 122 | return ret; |
| 123 | } |
| 124 | |
Alexandre Julliard | cf9ced5 | 2006-02-05 12:19:56 +0100 | [diff] [blame] | 125 | #define USE_INOTIFY |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 126 | |
| 127 | #endif |
| 128 | |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 129 | struct change_record { |
| 130 | struct list entry; |
| 131 | int action; |
| 132 | int len; |
| 133 | char name[1]; |
| 134 | }; |
| 135 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 136 | struct dir |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 137 | { |
| 138 | struct object obj; /* object header */ |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 139 | struct fd *fd; /* file descriptor to the directory */ |
| 140 | struct list entry; /* entry in global change notifications list */ |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 141 | struct event *event; |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 142 | unsigned int filter; /* notification filter */ |
Alexandre Julliard | 43c6396 | 2005-09-26 13:51:58 +0000 | [diff] [blame] | 143 | int notified; /* SIGIO counter */ |
Mike McCormack | 0790f95 | 2006-02-07 16:50:36 +0100 | [diff] [blame^] | 144 | int want_data; /* return change data */ |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 145 | long signaled; /* the file changed */ |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 146 | struct fd *inotify_fd; /* inotify file descriptor */ |
| 147 | int wd; /* inotify watch descriptor */ |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 148 | struct list change_q; /* change readers */ |
| 149 | struct list change_records; /* data for the change */ |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 150 | }; |
| 151 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 152 | static struct fd *dir_get_fd( struct object *obj ); |
| 153 | static unsigned int dir_map_access( struct object *obj, unsigned int access ); |
| 154 | static void dir_dump( struct object *obj, int verbose ); |
| 155 | static void dir_destroy( struct object *obj ); |
| 156 | static int dir_signaled( struct object *obj, struct thread *thread ); |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 157 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 158 | static const struct object_ops dir_ops = |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 159 | { |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 160 | sizeof(struct dir), /* size */ |
| 161 | dir_dump, /* dump */ |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 162 | add_queue, /* add_queue */ |
| 163 | remove_queue, /* remove_queue */ |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 164 | dir_signaled, /* signaled */ |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 165 | no_satisfied, /* satisfied */ |
Mike McCormack | f92fff6 | 2005-04-24 17:35:52 +0000 | [diff] [blame] | 166 | no_signal, /* signal */ |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 167 | dir_get_fd, /* get_fd */ |
| 168 | dir_map_access, /* map_access */ |
Vitaliy Margolen | baffcb9 | 2005-11-22 14:55:42 +0000 | [diff] [blame] | 169 | no_lookup_name, /* lookup_name */ |
Alexandre Julliard | b9b1ea9 | 2005-06-09 15:39:52 +0000 | [diff] [blame] | 170 | no_close_handle, /* close_handle */ |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 171 | dir_destroy /* destroy */ |
| 172 | }; |
| 173 | |
| 174 | static int dir_get_poll_events( struct fd *fd ); |
| 175 | static int dir_get_info( struct fd *fd ); |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 176 | static void dir_cancel_async( struct fd *fd ); |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 177 | |
| 178 | static const struct fd_ops dir_fd_ops = |
| 179 | { |
| 180 | dir_get_poll_events, /* get_poll_events */ |
| 181 | default_poll_event, /* poll_event */ |
| 182 | no_flush, /* flush */ |
| 183 | dir_get_info, /* get_file_info */ |
| 184 | default_fd_queue_async, /* queue_async */ |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 185 | dir_cancel_async /* cancel_async */ |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 186 | }; |
| 187 | |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 188 | static struct list change_list = LIST_INIT(change_list); |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 189 | |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 190 | static void dnotify_adjust_changes( struct dir *dir ) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 191 | { |
Alexandre Julliard | e8a339c | 2004-03-01 21:32:02 +0000 | [diff] [blame] | 192 | #if defined(F_SETSIG) && defined(F_NOTIFY) |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 193 | int fd = get_unix_fd( dir->fd ); |
| 194 | unsigned int filter = dir->filter; |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 195 | unsigned int val; |
| 196 | if ( 0 > fcntl( fd, F_SETSIG, SIGIO) ) |
| 197 | return; |
| 198 | |
| 199 | val = DN_MULTISHOT; |
Robert Shearman | 3795709 | 2005-06-10 19:54:46 +0000 | [diff] [blame] | 200 | if (filter & FILE_NOTIFY_CHANGE_FILE_NAME) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 201 | val |= DN_RENAME | DN_DELETE | DN_CREATE; |
Robert Shearman | 3795709 | 2005-06-10 19:54:46 +0000 | [diff] [blame] | 202 | if (filter & FILE_NOTIFY_CHANGE_DIR_NAME) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 203 | val |= DN_RENAME | DN_DELETE | DN_CREATE; |
Robert Shearman | 3795709 | 2005-06-10 19:54:46 +0000 | [diff] [blame] | 204 | if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 205 | val |= DN_ATTRIB; |
Robert Shearman | 3795709 | 2005-06-10 19:54:46 +0000 | [diff] [blame] | 206 | if (filter & FILE_NOTIFY_CHANGE_SIZE) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 207 | val |= DN_MODIFY; |
Robert Shearman | 3795709 | 2005-06-10 19:54:46 +0000 | [diff] [blame] | 208 | if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 209 | val |= DN_MODIFY; |
Robert Shearman | 3795709 | 2005-06-10 19:54:46 +0000 | [diff] [blame] | 210 | if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS) |
Hans Leidekker | ff49652 | 2004-02-05 01:45:58 +0000 | [diff] [blame] | 211 | val |= DN_ACCESS; |
Robert Shearman | 3795709 | 2005-06-10 19:54:46 +0000 | [diff] [blame] | 212 | if (filter & FILE_NOTIFY_CHANGE_CREATION) |
Hans Leidekker | ff49652 | 2004-02-05 01:45:58 +0000 | [diff] [blame] | 213 | val |= DN_CREATE; |
Robert Shearman | 3795709 | 2005-06-10 19:54:46 +0000 | [diff] [blame] | 214 | if (filter & FILE_NOTIFY_CHANGE_SECURITY) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 215 | val |= DN_ATTRIB; |
| 216 | fcntl( fd, F_NOTIFY, val ); |
| 217 | #endif |
| 218 | } |
| 219 | |
| 220 | /* insert change in the global list */ |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 221 | static inline void insert_change( struct dir *dir ) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 222 | { |
| 223 | sigset_t sigset; |
| 224 | |
| 225 | sigemptyset( &sigset ); |
| 226 | sigaddset( &sigset, SIGIO ); |
| 227 | sigprocmask( SIG_BLOCK, &sigset, NULL ); |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 228 | list_add_head( &change_list, &dir->entry ); |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 229 | sigprocmask( SIG_UNBLOCK, &sigset, NULL ); |
| 230 | } |
| 231 | |
| 232 | /* remove change from the global list */ |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 233 | static inline void remove_change( struct dir *dir ) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 234 | { |
| 235 | sigset_t sigset; |
| 236 | |
| 237 | sigemptyset( &sigset ); |
| 238 | sigaddset( &sigset, SIGIO ); |
| 239 | sigprocmask( SIG_BLOCK, &sigset, NULL ); |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 240 | list_remove( &dir->entry ); |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 241 | sigprocmask( SIG_UNBLOCK, &sigset, NULL ); |
| 242 | } |
| 243 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 244 | struct object *create_dir_obj( struct fd *fd ) |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 245 | { |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 246 | struct dir *dir; |
Hans Leidekker | ff49652 | 2004-02-05 01:45:58 +0000 | [diff] [blame] | 247 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 248 | dir = alloc_object( &dir_ops ); |
| 249 | if (!dir) |
Hans Leidekker | ff49652 | 2004-02-05 01:45:58 +0000 | [diff] [blame] | 250 | return NULL; |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 251 | |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 252 | list_init( &dir->change_q ); |
| 253 | list_init( &dir->change_records ); |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 254 | dir->event = NULL; |
| 255 | dir->filter = 0; |
| 256 | dir->notified = 0; |
| 257 | dir->signaled = 0; |
Mike McCormack | 0790f95 | 2006-02-07 16:50:36 +0100 | [diff] [blame^] | 258 | dir->want_data = 0; |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 259 | dir->inotify_fd = NULL; |
| 260 | dir->wd = -1; |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 261 | grab_object( fd ); |
| 262 | dir->fd = fd; |
| 263 | set_fd_user( fd, &dir_fd_ops, &dir->obj ); |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 264 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 265 | return &dir->obj; |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 266 | } |
| 267 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 268 | static void dir_dump( struct object *obj, int verbose ) |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 269 | { |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 270 | struct dir *dir = (struct dir *)obj; |
| 271 | assert( obj->ops == &dir_ops ); |
| 272 | fprintf( stderr, "Dirfile fd=%p event=%p filter=%08x\n", |
| 273 | dir->fd, dir->event, dir->filter ); |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 274 | } |
| 275 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 276 | static int dir_signaled( struct object *obj, struct thread *thread ) |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 277 | { |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 278 | struct dir *dir = (struct dir *)obj; |
| 279 | assert (obj->ops == &dir_ops); |
| 280 | return (dir->event == NULL) && dir->signaled; |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 281 | } |
| 282 | |
| 283 | /* enter here directly from SIGIO signal handler */ |
| 284 | void do_change_notify( int unix_fd ) |
| 285 | { |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 286 | struct dir *dir; |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 287 | |
| 288 | /* FIXME: this is O(n) ... probably can be improved */ |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 289 | LIST_FOR_EACH_ENTRY( dir, &change_list, struct dir, entry ) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 290 | { |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 291 | if (get_unix_fd( dir->fd ) != unix_fd) continue; |
| 292 | interlocked_xchg_add( &dir->notified, 1 ); |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 293 | break; |
| 294 | } |
| 295 | } |
| 296 | |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 297 | static void dir_signal_changed( struct dir *dir ) |
| 298 | { |
| 299 | if (dir->event) |
| 300 | set_event( dir->event ); |
| 301 | else |
| 302 | wake_up( &dir->obj, 0 ); |
| 303 | } |
| 304 | |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 305 | /* SIGIO callback, called synchronously with the poll loop */ |
| 306 | void sigio_callback(void) |
| 307 | { |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 308 | struct dir *dir; |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 309 | |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 310 | LIST_FOR_EACH_ENTRY( dir, &change_list, struct dir, entry ) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 311 | { |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 312 | long count = interlocked_xchg( &dir->notified, 0 ); |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 313 | if (count) |
| 314 | { |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 315 | dir->signaled += count; |
| 316 | if (dir->signaled == count) /* was it 0? */ |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 317 | dir_signal_changed( dir ); |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 318 | } |
| 319 | } |
Alexandre Julliard | 63cb0f8 | 1998-12-31 15:43:48 +0000 | [diff] [blame] | 320 | } |
| 321 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 322 | static struct fd *dir_get_fd( struct object *obj ) |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 323 | { |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 324 | struct dir *dir = (struct dir *)obj; |
| 325 | assert( obj->ops == &dir_ops ); |
| 326 | return (struct fd *)grab_object( dir->fd ); |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 327 | } |
| 328 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 329 | static unsigned int dir_map_access( struct object *obj, unsigned int access ) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 330 | { |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 331 | if (access & GENERIC_READ) access |= FILE_GENERIC_READ; |
| 332 | if (access & GENERIC_WRITE) access |= FILE_GENERIC_WRITE; |
| 333 | if (access & GENERIC_EXECUTE) access |= FILE_GENERIC_EXECUTE; |
| 334 | if (access & GENERIC_ALL) access |= FILE_ALL_ACCESS; |
| 335 | return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); |
| 336 | } |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 337 | |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 338 | static struct change_record *get_first_change_record( struct dir *dir ) |
| 339 | { |
| 340 | struct list *ptr = list_head( &dir->change_records ); |
| 341 | if (!ptr) return NULL; |
| 342 | list_remove( ptr ); |
| 343 | return LIST_ENTRY( ptr, struct change_record, entry ); |
| 344 | } |
| 345 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 346 | static void dir_destroy( struct object *obj ) |
| 347 | { |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 348 | struct change_record *record; |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 349 | struct dir *dir = (struct dir *)obj; |
| 350 | assert (obj->ops == &dir_ops); |
| 351 | |
| 352 | if (dir->filter) |
| 353 | remove_change( dir ); |
| 354 | |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 355 | if (dir->inotify_fd) |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 356 | release_object( dir->inotify_fd ); |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 357 | |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 358 | async_terminate_queue( &dir->change_q, STATUS_CANCELLED ); |
| 359 | while ((record = get_first_change_record( dir ))) free( record ); |
| 360 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 361 | if (dir->event) |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 362 | { |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 363 | set_event( dir->event ); |
| 364 | release_object( dir->event ); |
Alexandre Julliard | 3e588e3 | 2003-03-26 23:41:43 +0000 | [diff] [blame] | 365 | } |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 366 | release_object( dir->fd ); |
| 367 | } |
| 368 | |
| 369 | static struct dir * |
| 370 | get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access ) |
| 371 | { |
| 372 | return (struct dir *)get_handle_obj( process, handle, access, &dir_ops ); |
| 373 | } |
| 374 | |
| 375 | static int dir_get_poll_events( struct fd *fd ) |
| 376 | { |
| 377 | return 0; |
| 378 | } |
| 379 | |
| 380 | static int dir_get_info( struct fd *fd ) |
| 381 | { |
| 382 | return 0; |
| 383 | } |
| 384 | |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 385 | static void dir_cancel_async( struct fd *fd ) |
| 386 | { |
| 387 | struct dir *dir = (struct dir *) get_fd_user( fd ); |
| 388 | async_terminate_queue( &dir->change_q, STATUS_CANCELLED ); |
| 389 | } |
| 390 | |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 391 | |
Alexandre Julliard | cf9ced5 | 2006-02-05 12:19:56 +0100 | [diff] [blame] | 392 | #ifdef USE_INOTIFY |
| 393 | |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 394 | static int inotify_get_poll_events( struct fd *fd ); |
| 395 | static void inotify_poll_event( struct fd *fd, int event ); |
| 396 | static int inotify_get_info( struct fd *fd ); |
| 397 | |
| 398 | static const struct fd_ops inotify_fd_ops = |
| 399 | { |
| 400 | inotify_get_poll_events, /* get_poll_events */ |
| 401 | inotify_poll_event, /* poll_event */ |
| 402 | no_flush, /* flush */ |
| 403 | inotify_get_info, /* get_file_info */ |
| 404 | default_fd_queue_async, /* queue_async */ |
| 405 | default_fd_cancel_async, /* cancel_async */ |
| 406 | }; |
| 407 | |
| 408 | static int inotify_get_poll_events( struct fd *fd ) |
| 409 | { |
| 410 | return POLLIN; |
| 411 | } |
| 412 | |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 413 | static void inotify_do_change_notify( struct dir *dir, struct inotify_event *ie ) |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 414 | { |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 415 | struct change_record *record; |
| 416 | |
Mike McCormack | 0790f95 | 2006-02-07 16:50:36 +0100 | [diff] [blame^] | 417 | if (dir->want_data) |
| 418 | { |
| 419 | record = malloc( sizeof (*record) + ie->len - 1 ) ; |
| 420 | if (!record) |
| 421 | return; |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 422 | |
Mike McCormack | 0790f95 | 2006-02-07 16:50:36 +0100 | [diff] [blame^] | 423 | if( ie->mask & IN_CREATE ) |
| 424 | record->action = FILE_ACTION_ADDED; |
| 425 | else if( ie->mask & IN_DELETE ) |
| 426 | record->action = FILE_ACTION_REMOVED; |
| 427 | else |
| 428 | record->action = FILE_ACTION_MODIFIED; |
| 429 | memcpy( record->name, ie->name, ie->len ); |
| 430 | record->len = strlen( ie->name ); |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 431 | |
Mike McCormack | 0790f95 | 2006-02-07 16:50:36 +0100 | [diff] [blame^] | 432 | list_add_tail( &dir->change_records, &record->entry ); |
| 433 | } |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 434 | |
| 435 | if (!list_empty( &dir->change_q )) |
| 436 | async_terminate_head( &dir->change_q, STATUS_ALERTED ); |
| 437 | else |
| 438 | { |
| 439 | dir->signaled++; |
| 440 | dir_signal_changed( dir ); |
| 441 | } |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 442 | } |
| 443 | |
| 444 | static void inotify_poll_event( struct fd *fd, int event ) |
| 445 | { |
| 446 | int r, ofs, unix_fd; |
| 447 | char buffer[0x1000]; |
| 448 | struct inotify_event *ie; |
| 449 | struct dir *dir = get_fd_user( fd ); |
| 450 | |
| 451 | unix_fd = get_unix_fd( fd ); |
| 452 | r = read( unix_fd, buffer, sizeof buffer ); |
| 453 | if (r < 0) |
| 454 | { |
| 455 | fprintf(stderr,"inotify_poll_event(): inotify read failed!\n"); |
| 456 | return; |
| 457 | } |
| 458 | |
| 459 | for( ofs = 0; ofs < r; ) |
| 460 | { |
| 461 | ie = (struct inotify_event*) &buffer[ofs]; |
| 462 | if (!ie->len) |
| 463 | break; |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 464 | inotify_do_change_notify( dir, ie ); |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 465 | ofs += (sizeof (*ie) + ie->len - 1); |
| 466 | } |
| 467 | } |
| 468 | |
| 469 | static int inotify_get_info( struct fd *fd ) |
| 470 | { |
| 471 | return 0; |
| 472 | } |
| 473 | |
Alexandre Julliard | cf9ced5 | 2006-02-05 12:19:56 +0100 | [diff] [blame] | 474 | static inline struct fd *create_inotify_fd( struct dir *dir ) |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 475 | { |
Alexandre Julliard | cf9ced5 | 2006-02-05 12:19:56 +0100 | [diff] [blame] | 476 | int unix_fd; |
| 477 | |
| 478 | unix_fd = inotify_init(); |
| 479 | if (unix_fd<0) |
| 480 | return NULL; |
| 481 | return create_anonymous_fd( &inotify_fd_ops, unix_fd, &dir->obj ); |
| 482 | } |
| 483 | |
| 484 | static int inotify_adjust_changes( struct dir *dir ) |
| 485 | { |
| 486 | unsigned int filter = dir->filter; |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 487 | unsigned int mask = 0; |
| 488 | char link[32]; |
| 489 | |
Alexandre Julliard | cf9ced5 | 2006-02-05 12:19:56 +0100 | [diff] [blame] | 490 | if (!dir->inotify_fd) |
| 491 | { |
| 492 | if (!(dir->inotify_fd = create_inotify_fd( dir ))) return 0; |
| 493 | } |
| 494 | |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 495 | if (filter & FILE_NOTIFY_CHANGE_FILE_NAME) |
| 496 | mask |= (IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_CREATE); |
| 497 | if (filter & FILE_NOTIFY_CHANGE_DIR_NAME) |
| 498 | mask |= (IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF); |
| 499 | if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) |
| 500 | mask |= IN_ATTRIB; |
| 501 | if (filter & FILE_NOTIFY_CHANGE_SIZE) |
| 502 | mask |= IN_MODIFY; |
| 503 | if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE) |
| 504 | mask |= IN_MODIFY; |
| 505 | if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS) |
| 506 | mask |= IN_ACCESS; |
| 507 | if (filter & FILE_NOTIFY_CHANGE_CREATION) |
| 508 | mask |= IN_CREATE; |
| 509 | if (filter & FILE_NOTIFY_CHANGE_SECURITY) |
| 510 | mask |= IN_ATTRIB; |
| 511 | |
| 512 | sprintf( link, "/proc/self/fd/%u", get_unix_fd( dir->fd ) ); |
| 513 | dir->wd = inotify_add_watch( get_unix_fd( dir->inotify_fd ), link, mask ); |
| 514 | if (dir->wd != -1) |
| 515 | set_fd_events( dir->inotify_fd, POLLIN ); |
Alexandre Julliard | cf9ced5 | 2006-02-05 12:19:56 +0100 | [diff] [blame] | 516 | return 1; |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 517 | } |
| 518 | |
Alexandre Julliard | cf9ced5 | 2006-02-05 12:19:56 +0100 | [diff] [blame] | 519 | #endif /* USE_INOTIFY */ |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 520 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 521 | /* enable change notifications for a directory */ |
| 522 | DECL_HANDLER(read_directory_changes) |
| 523 | { |
| 524 | struct event *event = NULL; |
| 525 | struct dir *dir; |
| 526 | |
| 527 | if (!req->filter) |
| 528 | { |
| 529 | set_error(STATUS_INVALID_PARAMETER); |
| 530 | return; |
| 531 | } |
| 532 | |
| 533 | dir = get_dir_obj( current->process, req->handle, 0 ); |
| 534 | if (!dir) |
| 535 | return; |
| 536 | |
| 537 | /* possibly send changes through an event flag */ |
| 538 | if (req->event) |
| 539 | { |
| 540 | event = get_event_obj( current->process, req->event, EVENT_MODIFY_STATE ); |
| 541 | if (!event) |
| 542 | goto end; |
| 543 | } |
| 544 | |
| 545 | /* discard the current data, and move onto the next event */ |
| 546 | if (dir->event) release_object( dir->event ); |
| 547 | dir->event = event; |
| 548 | |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 549 | /* requests don't timeout */ |
| 550 | if ( req->io_apc && !create_async( current, NULL, &dir->change_q, |
| 551 | req->io_apc, req->io_user, req->io_sb )) |
| 552 | return; |
| 553 | |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 554 | /* assign it once */ |
| 555 | if (!dir->filter) |
| 556 | { |
| 557 | insert_change( dir ); |
| 558 | dir->filter = req->filter; |
Mike McCormack | 0790f95 | 2006-02-07 16:50:36 +0100 | [diff] [blame^] | 559 | dir->want_data = req->want_data; |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 560 | } |
| 561 | |
| 562 | /* remove any notifications */ |
| 563 | if (dir->signaled>0) |
| 564 | dir->signaled--; |
| 565 | |
Mike McCormack | 0790f95 | 2006-02-07 16:50:36 +0100 | [diff] [blame^] | 566 | /* clear the event */ |
| 567 | if (event) |
| 568 | reset_event( event ); |
| 569 | |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 570 | /* setup the real notification */ |
Alexandre Julliard | cf9ced5 | 2006-02-05 12:19:56 +0100 | [diff] [blame] | 571 | #ifdef USE_INOTIFY |
| 572 | if (!inotify_adjust_changes( dir )) |
| 573 | #endif |
Mike McCormack | fdf0c68 | 2006-01-30 18:15:31 +0100 | [diff] [blame] | 574 | dnotify_adjust_changes( dir ); |
Mike McCormack | 0835107 | 2006-01-27 12:13:56 +0100 | [diff] [blame] | 575 | |
| 576 | set_error(STATUS_PENDING); |
| 577 | |
| 578 | end: |
| 579 | release_object( dir ); |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 580 | } |
Mike McCormack | 0193211 | 2006-02-06 11:58:55 +0100 | [diff] [blame] | 581 | |
| 582 | DECL_HANDLER(read_change) |
| 583 | { |
| 584 | struct change_record *record; |
| 585 | struct dir *dir; |
| 586 | |
| 587 | dir = get_dir_obj( current->process, req->handle, 0 ); |
| 588 | if (!dir) |
| 589 | return; |
| 590 | |
| 591 | if ((record = get_first_change_record( dir )) != NULL) |
| 592 | { |
| 593 | reply->action = record->action; |
| 594 | set_reply_data( record->name, record->len ); |
| 595 | free( record ); |
| 596 | } |
| 597 | else |
| 598 | set_error( STATUS_NO_DATA_DETECTED ); |
| 599 | |
| 600 | /* now signal it */ |
| 601 | dir->signaled++; |
| 602 | dir_signal_changed( dir ); |
| 603 | |
| 604 | release_object( dir ); |
| 605 | } |