server: Fix the file notification interface to use directory handles.
[wine/gsoc_dplay.git] / server / change.c
blobca78093437ac38740e3c8778fd1d0d821a59c81a
1 /*
2 * Server-side change notification management
4 * Copyright (C) 1998 Alexandre Julliard
5 * Copyright (C) 2006 Mike McCormack
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.
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.
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <assert.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <signal.h>
30 #include <sys/stat.h>
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "windef.h"
36 #include "file.h"
37 #include "handle.h"
38 #include "thread.h"
39 #include "request.h"
40 #include "winternl.h"
42 #ifdef linux
43 #ifndef F_NOTIFY
44 #define F_NOTIFY 1026
45 #define DN_ACCESS 0x00000001 /* File accessed */
46 #define DN_MODIFY 0x00000002 /* File modified */
47 #define DN_CREATE 0x00000004 /* File created */
48 #define DN_DELETE 0x00000008 /* File removed */
49 #define DN_RENAME 0x00000010 /* File renamed */
50 #define DN_ATTRIB 0x00000020 /* File changed attibutes */
51 #define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
52 #endif
53 #endif
55 struct dir
57 struct object obj; /* object header */
58 struct fd *fd; /* file descriptor to the directory */
59 struct list entry; /* entry in global change notifications list */
60 struct event *event;
61 unsigned int filter; /* notification filter */
62 int notified; /* SIGIO counter */
63 long signaled; /* the file changed */
66 static struct fd *dir_get_fd( struct object *obj );
67 static unsigned int dir_map_access( struct object *obj, unsigned int access );
68 static void dir_dump( struct object *obj, int verbose );
69 static void dir_destroy( struct object *obj );
70 static int dir_signaled( struct object *obj, struct thread *thread );
72 static const struct object_ops dir_ops =
74 sizeof(struct dir), /* size */
75 dir_dump, /* dump */
76 add_queue, /* add_queue */
77 remove_queue, /* remove_queue */
78 dir_signaled, /* signaled */
79 no_satisfied, /* satisfied */
80 no_signal, /* signal */
81 dir_get_fd, /* get_fd */
82 dir_map_access, /* map_access */
83 no_lookup_name, /* lookup_name */
84 no_close_handle, /* close_handle */
85 dir_destroy /* destroy */
88 static int dir_get_poll_events( struct fd *fd );
89 static int dir_get_info( struct fd *fd );
91 static const struct fd_ops dir_fd_ops =
93 dir_get_poll_events, /* get_poll_events */
94 default_poll_event, /* poll_event */
95 no_flush, /* flush */
96 dir_get_info, /* get_file_info */
97 default_fd_queue_async, /* queue_async */
98 default_fd_cancel_async /* cancel_async */
101 static struct list change_list = LIST_INIT(change_list);
103 static void adjust_changes( int fd, unsigned int filter )
105 #if defined(F_SETSIG) && defined(F_NOTIFY)
106 unsigned int val;
107 if ( 0 > fcntl( fd, F_SETSIG, SIGIO) )
108 return;
110 val = DN_MULTISHOT;
111 if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
112 val |= DN_RENAME | DN_DELETE | DN_CREATE;
113 if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
114 val |= DN_RENAME | DN_DELETE | DN_CREATE;
115 if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
116 val |= DN_ATTRIB;
117 if (filter & FILE_NOTIFY_CHANGE_SIZE)
118 val |= DN_MODIFY;
119 if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
120 val |= DN_MODIFY;
121 if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
122 val |= DN_ACCESS;
123 if (filter & FILE_NOTIFY_CHANGE_CREATION)
124 val |= DN_CREATE;
125 if (filter & FILE_NOTIFY_CHANGE_SECURITY)
126 val |= DN_ATTRIB;
127 fcntl( fd, F_NOTIFY, val );
128 #endif
131 /* insert change in the global list */
132 static inline void insert_change( struct dir *dir )
134 sigset_t sigset;
136 sigemptyset( &sigset );
137 sigaddset( &sigset, SIGIO );
138 sigprocmask( SIG_BLOCK, &sigset, NULL );
139 list_add_head( &change_list, &dir->entry );
140 sigprocmask( SIG_UNBLOCK, &sigset, NULL );
143 /* remove change from the global list */
144 static inline void remove_change( struct dir *dir )
146 sigset_t sigset;
148 sigemptyset( &sigset );
149 sigaddset( &sigset, SIGIO );
150 sigprocmask( SIG_BLOCK, &sigset, NULL );
151 list_remove( &dir->entry );
152 sigprocmask( SIG_UNBLOCK, &sigset, NULL );
155 struct object *create_dir_obj( struct fd *fd )
157 struct dir *dir;
159 dir = alloc_object( &dir_ops );
160 if (!dir)
161 return NULL;
163 dir->event = NULL;
164 dir->filter = 0;
165 dir->notified = 0;
166 dir->signaled = 0;
167 grab_object( fd );
168 dir->fd = fd;
169 set_fd_user( fd, &dir_fd_ops, &dir->obj );
171 return &dir->obj;
174 static void dir_dump( struct object *obj, int verbose )
176 struct dir *dir = (struct dir *)obj;
177 assert( obj->ops == &dir_ops );
178 fprintf( stderr, "Dirfile fd=%p event=%p filter=%08x\n",
179 dir->fd, dir->event, dir->filter );
182 static int dir_signaled( struct object *obj, struct thread *thread )
184 struct dir *dir = (struct dir *)obj;
185 assert (obj->ops == &dir_ops);
186 return (dir->event == NULL) && dir->signaled;
189 /* enter here directly from SIGIO signal handler */
190 void do_change_notify( int unix_fd )
192 struct list *ptr;
194 /* FIXME: this is O(n) ... probably can be improved */
195 LIST_FOR_EACH( ptr, &change_list )
197 struct dir *dir = LIST_ENTRY( ptr, struct dir, entry );
198 if (get_unix_fd( dir->fd ) != unix_fd) continue;
199 interlocked_xchg_add( &dir->notified, 1 );
200 break;
204 /* SIGIO callback, called synchronously with the poll loop */
205 void sigio_callback(void)
207 struct list *ptr;
209 LIST_FOR_EACH( ptr, &change_list )
211 struct dir *dir = LIST_ENTRY( ptr, struct dir, entry );
212 long count = interlocked_xchg( &dir->notified, 0 );
213 if (count)
215 dir->signaled += count;
216 if (dir->signaled == count) /* was it 0? */
218 if (dir->event)
219 set_event( dir->event );
220 else
221 wake_up( &dir->obj, 0 );
227 static struct fd *dir_get_fd( struct object *obj )
229 struct dir *dir = (struct dir *)obj;
230 assert( obj->ops == &dir_ops );
231 return (struct fd *)grab_object( dir->fd );
234 static unsigned int dir_map_access( struct object *obj, unsigned int access )
236 if (access & GENERIC_READ) access |= FILE_GENERIC_READ;
237 if (access & GENERIC_WRITE) access |= FILE_GENERIC_WRITE;
238 if (access & GENERIC_EXECUTE) access |= FILE_GENERIC_EXECUTE;
239 if (access & GENERIC_ALL) access |= FILE_ALL_ACCESS;
240 return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
243 static void dir_destroy( struct object *obj )
245 struct dir *dir = (struct dir *)obj;
246 assert (obj->ops == &dir_ops);
248 if (dir->filter)
249 remove_change( dir );
251 if (dir->event)
253 set_event( dir->event );
254 release_object( dir->event );
256 release_object( dir->fd );
259 static struct dir *
260 get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access )
262 return (struct dir *)get_handle_obj( process, handle, access, &dir_ops );
265 static int dir_get_poll_events( struct fd *fd )
267 return 0;
270 static int dir_get_info( struct fd *fd )
272 return 0;
275 /* enable change notifications for a directory */
276 DECL_HANDLER(read_directory_changes)
278 struct event *event = NULL;
279 struct dir *dir;
281 if (!req->filter)
283 set_error(STATUS_INVALID_PARAMETER);
284 return;
287 dir = get_dir_obj( current->process, req->handle, 0 );
288 if (!dir)
289 return;
291 /* possibly send changes through an event flag */
292 if (req->event)
294 event = get_event_obj( current->process, req->event, EVENT_MODIFY_STATE );
295 if (!event)
296 goto end;
299 /* discard the current data, and move onto the next event */
300 if (dir->event) release_object( dir->event );
301 dir->event = event;
303 /* assign it once */
304 if (!dir->filter)
306 insert_change( dir );
307 dir->filter = req->filter;
310 /* remove any notifications */
311 if (dir->signaled>0)
312 dir->signaled--;
314 adjust_changes( get_unix_fd( dir->fd ), dir->filter );
316 set_error(STATUS_PENDING);
318 end:
319 release_object( dir );