blob: c35a82cf67e61cad85f02459a626fd909b7a59d1 [file] [log] [blame]
Alexandre Julliard63cb0f81998-12-31 15:43:48 +00001/*
2 * Server-side change notification management
3 *
4 * Copyright (C) 1998 Alexandre Julliard
Mike McCormack08351072006-01-27 12:13:56 +01005 * Copyright (C) 2006 Mike McCormack
Alexandre Julliard0799c1a2002-03-09 23:29:33 +00006 *
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 Julliard63cb0f81998-12-31 15:43:48 +000020 */
21
Alexandre Julliard3e588e32003-03-26 23:41:43 +000022#include "config.h"
23#include "wine/port.h"
24
Alexandre Julliard63cb0f81998-12-31 15:43:48 +000025#include <assert.h>
Alexandre Julliard3e588e32003-03-26 23:41:43 +000026#include <fcntl.h>
Alexandre Julliard63cb0f81998-12-31 15:43:48 +000027#include <stdio.h>
28#include <stdlib.h>
Alexandre Julliard3e588e32003-03-26 23:41:43 +000029#include <signal.h>
Hans Leidekkerff496522004-02-05 01:45:58 +000030#include <sys/stat.h>
Mike McCormackfdf0c682006-01-30 18:15:31 +010031#include <errno.h>
32#ifdef HAVE_SYS_ERRNO_H
33#include <sys/errno.h>
34#endif
Alexandre Julliard63cb0f81998-12-31 15:43:48 +000035
Ge van Geldorp1a1583a2005-11-28 17:32:54 +010036#include "ntstatus.h"
37#define WIN32_NO_STATUS
Alexandre Julliard435e2e62002-12-10 22:56:43 +000038#include "windef.h"
Alexandre Julliard43c190e1999-05-15 10:48:19 +000039
Alexandre Julliard3e588e32003-03-26 23:41:43 +000040#include "file.h"
Alexandre Julliard43c190e1999-05-15 10:48:19 +000041#include "handle.h"
42#include "thread.h"
Alexandre Julliard5bc78081999-06-22 17:26:53 +000043#include "request.h"
Ge van Geldorp1a1583a2005-11-28 17:32:54 +010044#include "winternl.h"
Alexandre Julliard63cb0f81998-12-31 15:43:48 +000045
Mike McCormackfdf0c682006-01-30 18:15:31 +010046/* dnotify support */
47
Huw Daviesbdb63ec2004-02-10 20:08:24 +000048#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 McCormackfdf0c682006-01-30 18:15:31 +010061/* 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
69struct 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
89static 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
99static 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
112static 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 Julliardcf9ced52006-02-05 12:19:56 +0100125#define USE_INOTIFY
Mike McCormackfdf0c682006-01-30 18:15:31 +0100126
127#endif
128
Mike McCormack01932112006-02-06 11:58:55 +0100129struct change_record {
130 struct list entry;
131 int action;
132 int len;
133 char name[1];
134};
135
Mike McCormack08351072006-01-27 12:13:56 +0100136struct dir
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000137{
138 struct object obj; /* object header */
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000139 struct fd *fd; /* file descriptor to the directory */
140 struct list entry; /* entry in global change notifications list */
Mike McCormack08351072006-01-27 12:13:56 +0100141 struct event *event;
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000142 unsigned int filter; /* notification filter */
Alexandre Julliard43c63962005-09-26 13:51:58 +0000143 int notified; /* SIGIO counter */
Mike McCormack0790f952006-02-07 16:50:36 +0100144 int want_data; /* return change data */
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000145 long signaled; /* the file changed */
Mike McCormackfdf0c682006-01-30 18:15:31 +0100146 struct fd *inotify_fd; /* inotify file descriptor */
147 int wd; /* inotify watch descriptor */
Mike McCormack01932112006-02-06 11:58:55 +0100148 struct list change_q; /* change readers */
149 struct list change_records; /* data for the change */
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000150};
151
Mike McCormack08351072006-01-27 12:13:56 +0100152static struct fd *dir_get_fd( struct object *obj );
153static unsigned int dir_map_access( struct object *obj, unsigned int access );
154static void dir_dump( struct object *obj, int verbose );
155static void dir_destroy( struct object *obj );
156static int dir_signaled( struct object *obj, struct thread *thread );
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000157
Mike McCormack08351072006-01-27 12:13:56 +0100158static const struct object_ops dir_ops =
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000159{
Mike McCormack08351072006-01-27 12:13:56 +0100160 sizeof(struct dir), /* size */
161 dir_dump, /* dump */
Alexandre Julliard1dca5e22000-01-01 00:56:27 +0000162 add_queue, /* add_queue */
163 remove_queue, /* remove_queue */
Mike McCormack08351072006-01-27 12:13:56 +0100164 dir_signaled, /* signaled */
Alexandre Julliard1dca5e22000-01-01 00:56:27 +0000165 no_satisfied, /* satisfied */
Mike McCormackf92fff62005-04-24 17:35:52 +0000166 no_signal, /* signal */
Mike McCormack08351072006-01-27 12:13:56 +0100167 dir_get_fd, /* get_fd */
168 dir_map_access, /* map_access */
Vitaliy Margolenbaffcb92005-11-22 14:55:42 +0000169 no_lookup_name, /* lookup_name */
Alexandre Julliardb9b1ea92005-06-09 15:39:52 +0000170 no_close_handle, /* close_handle */
Mike McCormack08351072006-01-27 12:13:56 +0100171 dir_destroy /* destroy */
172};
173
174static int dir_get_poll_events( struct fd *fd );
175static int dir_get_info( struct fd *fd );
Mike McCormack01932112006-02-06 11:58:55 +0100176static void dir_cancel_async( struct fd *fd );
Mike McCormack08351072006-01-27 12:13:56 +0100177
178static 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 McCormack01932112006-02-06 11:58:55 +0100185 dir_cancel_async /* cancel_async */
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000186};
187
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000188static struct list change_list = LIST_INIT(change_list);
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000189
Mike McCormackfdf0c682006-01-30 18:15:31 +0100190static void dnotify_adjust_changes( struct dir *dir )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000191{
Alexandre Julliarde8a339c2004-03-01 21:32:02 +0000192#if defined(F_SETSIG) && defined(F_NOTIFY)
Mike McCormackfdf0c682006-01-30 18:15:31 +0100193 int fd = get_unix_fd( dir->fd );
194 unsigned int filter = dir->filter;
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000195 unsigned int val;
196 if ( 0 > fcntl( fd, F_SETSIG, SIGIO) )
197 return;
198
199 val = DN_MULTISHOT;
Robert Shearman37957092005-06-10 19:54:46 +0000200 if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000201 val |= DN_RENAME | DN_DELETE | DN_CREATE;
Robert Shearman37957092005-06-10 19:54:46 +0000202 if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000203 val |= DN_RENAME | DN_DELETE | DN_CREATE;
Robert Shearman37957092005-06-10 19:54:46 +0000204 if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000205 val |= DN_ATTRIB;
Robert Shearman37957092005-06-10 19:54:46 +0000206 if (filter & FILE_NOTIFY_CHANGE_SIZE)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000207 val |= DN_MODIFY;
Robert Shearman37957092005-06-10 19:54:46 +0000208 if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000209 val |= DN_MODIFY;
Robert Shearman37957092005-06-10 19:54:46 +0000210 if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
Hans Leidekkerff496522004-02-05 01:45:58 +0000211 val |= DN_ACCESS;
Robert Shearman37957092005-06-10 19:54:46 +0000212 if (filter & FILE_NOTIFY_CHANGE_CREATION)
Hans Leidekkerff496522004-02-05 01:45:58 +0000213 val |= DN_CREATE;
Robert Shearman37957092005-06-10 19:54:46 +0000214 if (filter & FILE_NOTIFY_CHANGE_SECURITY)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000215 val |= DN_ATTRIB;
216 fcntl( fd, F_NOTIFY, val );
217#endif
218}
219
220/* insert change in the global list */
Mike McCormack08351072006-01-27 12:13:56 +0100221static inline void insert_change( struct dir *dir )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000222{
223 sigset_t sigset;
224
225 sigemptyset( &sigset );
226 sigaddset( &sigset, SIGIO );
227 sigprocmask( SIG_BLOCK, &sigset, NULL );
Mike McCormack08351072006-01-27 12:13:56 +0100228 list_add_head( &change_list, &dir->entry );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000229 sigprocmask( SIG_UNBLOCK, &sigset, NULL );
230}
231
232/* remove change from the global list */
Mike McCormack08351072006-01-27 12:13:56 +0100233static inline void remove_change( struct dir *dir )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000234{
235 sigset_t sigset;
236
237 sigemptyset( &sigset );
238 sigaddset( &sigset, SIGIO );
239 sigprocmask( SIG_BLOCK, &sigset, NULL );
Mike McCormack08351072006-01-27 12:13:56 +0100240 list_remove( &dir->entry );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000241 sigprocmask( SIG_UNBLOCK, &sigset, NULL );
242}
243
Mike McCormack08351072006-01-27 12:13:56 +0100244struct object *create_dir_obj( struct fd *fd )
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000245{
Mike McCormack08351072006-01-27 12:13:56 +0100246 struct dir *dir;
Hans Leidekkerff496522004-02-05 01:45:58 +0000247
Mike McCormack08351072006-01-27 12:13:56 +0100248 dir = alloc_object( &dir_ops );
249 if (!dir)
Hans Leidekkerff496522004-02-05 01:45:58 +0000250 return NULL;
Mike McCormackfdf0c682006-01-30 18:15:31 +0100251
Mike McCormack01932112006-02-06 11:58:55 +0100252 list_init( &dir->change_q );
253 list_init( &dir->change_records );
Mike McCormack08351072006-01-27 12:13:56 +0100254 dir->event = NULL;
255 dir->filter = 0;
256 dir->notified = 0;
257 dir->signaled = 0;
Mike McCormack0790f952006-02-07 16:50:36 +0100258 dir->want_data = 0;
Mike McCormackfdf0c682006-01-30 18:15:31 +0100259 dir->inotify_fd = NULL;
260 dir->wd = -1;
Mike McCormack08351072006-01-27 12:13:56 +0100261 grab_object( fd );
262 dir->fd = fd;
263 set_fd_user( fd, &dir_fd_ops, &dir->obj );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000264
Mike McCormack08351072006-01-27 12:13:56 +0100265 return &dir->obj;
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000266}
267
Mike McCormack08351072006-01-27 12:13:56 +0100268static void dir_dump( struct object *obj, int verbose )
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000269{
Mike McCormack08351072006-01-27 12:13:56 +0100270 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 Julliard63cb0f81998-12-31 15:43:48 +0000274}
275
Mike McCormack08351072006-01-27 12:13:56 +0100276static int dir_signaled( struct object *obj, struct thread *thread )
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000277{
Mike McCormack08351072006-01-27 12:13:56 +0100278 struct dir *dir = (struct dir *)obj;
279 assert (obj->ops == &dir_ops);
280 return (dir->event == NULL) && dir->signaled;
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000281}
282
283/* enter here directly from SIGIO signal handler */
284void do_change_notify( int unix_fd )
285{
Mike McCormackfdf0c682006-01-30 18:15:31 +0100286 struct dir *dir;
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000287
288 /* FIXME: this is O(n) ... probably can be improved */
Mike McCormackfdf0c682006-01-30 18:15:31 +0100289 LIST_FOR_EACH_ENTRY( dir, &change_list, struct dir, entry )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000290 {
Mike McCormack08351072006-01-27 12:13:56 +0100291 if (get_unix_fd( dir->fd ) != unix_fd) continue;
292 interlocked_xchg_add( &dir->notified, 1 );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000293 break;
294 }
295}
296
Mike McCormackfdf0c682006-01-30 18:15:31 +0100297static 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 Julliard3e588e32003-03-26 23:41:43 +0000305/* SIGIO callback, called synchronously with the poll loop */
306void sigio_callback(void)
307{
Mike McCormackfdf0c682006-01-30 18:15:31 +0100308 struct dir *dir;
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000309
Mike McCormackfdf0c682006-01-30 18:15:31 +0100310 LIST_FOR_EACH_ENTRY( dir, &change_list, struct dir, entry )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000311 {
Mike McCormack08351072006-01-27 12:13:56 +0100312 long count = interlocked_xchg( &dir->notified, 0 );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000313 if (count)
314 {
Mike McCormack08351072006-01-27 12:13:56 +0100315 dir->signaled += count;
316 if (dir->signaled == count) /* was it 0? */
Mike McCormackfdf0c682006-01-30 18:15:31 +0100317 dir_signal_changed( dir );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000318 }
319 }
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000320}
321
Mike McCormack08351072006-01-27 12:13:56 +0100322static struct fd *dir_get_fd( struct object *obj )
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000323{
Mike McCormack08351072006-01-27 12:13:56 +0100324 struct dir *dir = (struct dir *)obj;
325 assert( obj->ops == &dir_ops );
326 return (struct fd *)grab_object( dir->fd );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000327}
328
Mike McCormack08351072006-01-27 12:13:56 +0100329static unsigned int dir_map_access( struct object *obj, unsigned int access )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000330{
Mike McCormack08351072006-01-27 12:13:56 +0100331 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 Julliard3e588e32003-03-26 23:41:43 +0000337
Mike McCormack01932112006-02-06 11:58:55 +0100338static 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 McCormack08351072006-01-27 12:13:56 +0100346static void dir_destroy( struct object *obj )
347{
Mike McCormack01932112006-02-06 11:58:55 +0100348 struct change_record *record;
Mike McCormack08351072006-01-27 12:13:56 +0100349 struct dir *dir = (struct dir *)obj;
350 assert (obj->ops == &dir_ops);
351
352 if (dir->filter)
353 remove_change( dir );
354
Mike McCormackfdf0c682006-01-30 18:15:31 +0100355 if (dir->inotify_fd)
Mike McCormackfdf0c682006-01-30 18:15:31 +0100356 release_object( dir->inotify_fd );
Mike McCormackfdf0c682006-01-30 18:15:31 +0100357
Mike McCormack01932112006-02-06 11:58:55 +0100358 async_terminate_queue( &dir->change_q, STATUS_CANCELLED );
359 while ((record = get_first_change_record( dir ))) free( record );
360
Mike McCormack08351072006-01-27 12:13:56 +0100361 if (dir->event)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000362 {
Mike McCormack08351072006-01-27 12:13:56 +0100363 set_event( dir->event );
364 release_object( dir->event );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000365 }
Mike McCormack08351072006-01-27 12:13:56 +0100366 release_object( dir->fd );
367}
368
369static struct dir *
370get_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
375static int dir_get_poll_events( struct fd *fd )
376{
377 return 0;
378}
379
380static int dir_get_info( struct fd *fd )
381{
382 return 0;
383}
384
Mike McCormack01932112006-02-06 11:58:55 +0100385static 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 McCormackfdf0c682006-01-30 18:15:31 +0100391
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100392#ifdef USE_INOTIFY
393
Mike McCormackfdf0c682006-01-30 18:15:31 +0100394static int inotify_get_poll_events( struct fd *fd );
395static void inotify_poll_event( struct fd *fd, int event );
396static int inotify_get_info( struct fd *fd );
397
398static 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
408static int inotify_get_poll_events( struct fd *fd )
409{
410 return POLLIN;
411}
412
Mike McCormack01932112006-02-06 11:58:55 +0100413static void inotify_do_change_notify( struct dir *dir, struct inotify_event *ie )
Mike McCormackfdf0c682006-01-30 18:15:31 +0100414{
Mike McCormack01932112006-02-06 11:58:55 +0100415 struct change_record *record;
416
Mike McCormack0790f952006-02-07 16:50:36 +0100417 if (dir->want_data)
418 {
419 record = malloc( sizeof (*record) + ie->len - 1 ) ;
420 if (!record)
421 return;
Mike McCormack01932112006-02-06 11:58:55 +0100422
Mike McCormack0790f952006-02-07 16:50:36 +0100423 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 McCormack01932112006-02-06 11:58:55 +0100431
Mike McCormack0790f952006-02-07 16:50:36 +0100432 list_add_tail( &dir->change_records, &record->entry );
433 }
Mike McCormack01932112006-02-06 11:58:55 +0100434
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 McCormackfdf0c682006-01-30 18:15:31 +0100442}
443
444static 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 McCormack01932112006-02-06 11:58:55 +0100464 inotify_do_change_notify( dir, ie );
Mike McCormackfdf0c682006-01-30 18:15:31 +0100465 ofs += (sizeof (*ie) + ie->len - 1);
466 }
467}
468
469static int inotify_get_info( struct fd *fd )
470{
471 return 0;
472}
473
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100474static inline struct fd *create_inotify_fd( struct dir *dir )
Mike McCormackfdf0c682006-01-30 18:15:31 +0100475{
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100476 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
484static int inotify_adjust_changes( struct dir *dir )
485{
486 unsigned int filter = dir->filter;
Mike McCormackfdf0c682006-01-30 18:15:31 +0100487 unsigned int mask = 0;
488 char link[32];
489
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100490 if (!dir->inotify_fd)
491 {
492 if (!(dir->inotify_fd = create_inotify_fd( dir ))) return 0;
493 }
494
Mike McCormackfdf0c682006-01-30 18:15:31 +0100495 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 Julliardcf9ced52006-02-05 12:19:56 +0100516 return 1;
Mike McCormackfdf0c682006-01-30 18:15:31 +0100517}
518
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100519#endif /* USE_INOTIFY */
Mike McCormackfdf0c682006-01-30 18:15:31 +0100520
Mike McCormack08351072006-01-27 12:13:56 +0100521/* enable change notifications for a directory */
522DECL_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 McCormack01932112006-02-06 11:58:55 +0100549 /* 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 McCormack08351072006-01-27 12:13:56 +0100554 /* assign it once */
555 if (!dir->filter)
556 {
557 insert_change( dir );
558 dir->filter = req->filter;
Mike McCormack0790f952006-02-07 16:50:36 +0100559 dir->want_data = req->want_data;
Mike McCormack08351072006-01-27 12:13:56 +0100560 }
561
562 /* remove any notifications */
563 if (dir->signaled>0)
564 dir->signaled--;
565
Mike McCormack0790f952006-02-07 16:50:36 +0100566 /* clear the event */
567 if (event)
568 reset_event( event );
569
Mike McCormackfdf0c682006-01-30 18:15:31 +0100570 /* setup the real notification */
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100571#ifdef USE_INOTIFY
572 if (!inotify_adjust_changes( dir ))
573#endif
Mike McCormackfdf0c682006-01-30 18:15:31 +0100574 dnotify_adjust_changes( dir );
Mike McCormack08351072006-01-27 12:13:56 +0100575
576 set_error(STATUS_PENDING);
577
578end:
579 release_object( dir );
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000580}
Mike McCormack01932112006-02-06 11:58:55 +0100581
582DECL_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}