blob: 4349d7e3876e5f9c8f4a37b210c9a57fd3f439af [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 McCormackfbc00db2006-02-17 11:40:38 +010031#include <sys/types.h>
32#include <limits.h>
33#include <dirent.h>
Mike McCormackfdf0c682006-01-30 18:15:31 +010034#include <errno.h>
35#ifdef HAVE_SYS_ERRNO_H
36#include <sys/errno.h>
37#endif
Alexandre Julliard63cb0f81998-12-31 15:43:48 +000038
Ge van Geldorp1a1583a2005-11-28 17:32:54 +010039#include "ntstatus.h"
40#define WIN32_NO_STATUS
Alexandre Julliard435e2e62002-12-10 22:56:43 +000041#include "windef.h"
Alexandre Julliard43c190e1999-05-15 10:48:19 +000042
Alexandre Julliard3e588e32003-03-26 23:41:43 +000043#include "file.h"
Alexandre Julliard43c190e1999-05-15 10:48:19 +000044#include "handle.h"
45#include "thread.h"
Alexandre Julliard5bc78081999-06-22 17:26:53 +000046#include "request.h"
Ge van Geldorp1a1583a2005-11-28 17:32:54 +010047#include "winternl.h"
Alexandre Julliard63cb0f81998-12-31 15:43:48 +000048
Mike McCormackfdf0c682006-01-30 18:15:31 +010049/* dnotify support */
50
Huw Daviesbdb63ec2004-02-10 20:08:24 +000051#ifdef linux
52#ifndef F_NOTIFY
53#define F_NOTIFY 1026
54#define DN_ACCESS 0x00000001 /* File accessed */
55#define DN_MODIFY 0x00000002 /* File modified */
56#define DN_CREATE 0x00000004 /* File created */
57#define DN_DELETE 0x00000008 /* File removed */
58#define DN_RENAME 0x00000010 /* File renamed */
59#define DN_ATTRIB 0x00000020 /* File changed attibutes */
60#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
61#endif
62#endif
63
Mike McCormackfdf0c682006-01-30 18:15:31 +010064/* inotify support */
65
66#if defined(__linux__) && defined(__i386__)
67
68#define SYS_inotify_init 291
69#define SYS_inotify_add_watch 292
70#define SYS_inotify_rm_watch 293
71
72struct inotify_event {
73 int wd;
74 unsigned int mask;
75 unsigned int cookie;
76 unsigned int len;
77 char name[1];
78};
79
80#define IN_ACCESS 0x00000001
81#define IN_MODIFY 0x00000002
82#define IN_ATTRIB 0x00000004
83#define IN_CLOSE_WRITE 0x00000008
84#define IN_CLOSE_NOWRITE 0x00000010
85#define IN_OPEN 0x00000020
86#define IN_MOVED_FROM 0x00000040
87#define IN_MOVED_TO 0x00000080
88#define IN_CREATE 0x00000100
89#define IN_DELETE 0x00000200
90#define IN_DELETE_SELF 0x00000400
91
92static inline int inotify_init( void )
93{
94 int ret;
95 __asm__ __volatile__( "int $0x80"
96 : "=a" (ret)
97 : "0" (SYS_inotify_init));
98 if (ret<0) { errno = -ret; ret = -1; }
99 return ret;
100}
101
102static inline int inotify_add_watch( int fd, const char *name, unsigned int mask )
103{
104 int ret;
105 __asm__ __volatile__( "pushl %%ebx;\n\t"
106 "movl %2,%%ebx;\n\t"
107 "int $0x80;\n\t"
108 "popl %%ebx"
109 : "=a" (ret) : "0" (SYS_inotify_add_watch),
110 "r" (fd), "c" (name), "d" (mask) );
111 if (ret<0) { errno = -ret; ret = -1; }
112 return ret;
113}
114
115static inline int inotify_remove_watch( int fd, int wd )
116{
117 int ret;
118 __asm__ __volatile__( "pushl %%ebx;\n\t"
119 "movl %2,%%ebx;\n\t"
120 "int $0x80;\n\t"
121 "popl %%ebx"
122 : "=a" (ret) : "0" (SYS_inotify_rm_watch),
123 "r" (fd), "c" (wd) );
124 if (ret<0) { errno = -ret; ret = -1; }
125 return ret;
126}
127
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100128#define USE_INOTIFY
Mike McCormackfdf0c682006-01-30 18:15:31 +0100129
130#endif
131
Mike McCormackfbc00db2006-02-17 11:40:38 +0100132struct inode;
133
134static void free_inode( struct inode *inode );
135
136static struct fd *inotify_fd;
137
Mike McCormack01932112006-02-06 11:58:55 +0100138struct change_record {
139 struct list entry;
140 int action;
141 int len;
142 char name[1];
143};
144
Mike McCormack08351072006-01-27 12:13:56 +0100145struct dir
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000146{
147 struct object obj; /* object header */
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000148 struct fd *fd; /* file descriptor to the directory */
149 struct list entry; /* entry in global change notifications list */
Mike McCormack08351072006-01-27 12:13:56 +0100150 struct event *event;
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000151 unsigned int filter; /* notification filter */
Alexandre Julliard43c63962005-09-26 13:51:58 +0000152 int notified; /* SIGIO counter */
Mike McCormack0790f952006-02-07 16:50:36 +0100153 int want_data; /* return change data */
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000154 long signaled; /* the file changed */
Mike McCormacke4faabf2006-02-21 16:58:19 +0900155 int subtree; /* do we want to watch subdirectories? */
Mike McCormack01932112006-02-06 11:58:55 +0100156 struct list change_q; /* change readers */
157 struct list change_records; /* data for the change */
Mike McCormackfbc00db2006-02-17 11:40:38 +0100158 struct list in_entry; /* entry in the inode dirs list */
159 struct inode *inode; /* inode of the associated directory */
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000160};
161
Mike McCormack08351072006-01-27 12:13:56 +0100162static struct fd *dir_get_fd( struct object *obj );
163static unsigned int dir_map_access( struct object *obj, unsigned int access );
164static void dir_dump( struct object *obj, int verbose );
165static void dir_destroy( struct object *obj );
166static int dir_signaled( struct object *obj, struct thread *thread );
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000167
Mike McCormack08351072006-01-27 12:13:56 +0100168static const struct object_ops dir_ops =
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000169{
Mike McCormack08351072006-01-27 12:13:56 +0100170 sizeof(struct dir), /* size */
171 dir_dump, /* dump */
Alexandre Julliard1dca5e22000-01-01 00:56:27 +0000172 add_queue, /* add_queue */
173 remove_queue, /* remove_queue */
Mike McCormack08351072006-01-27 12:13:56 +0100174 dir_signaled, /* signaled */
Alexandre Julliard1dca5e22000-01-01 00:56:27 +0000175 no_satisfied, /* satisfied */
Mike McCormackf92fff62005-04-24 17:35:52 +0000176 no_signal, /* signal */
Mike McCormack08351072006-01-27 12:13:56 +0100177 dir_get_fd, /* get_fd */
178 dir_map_access, /* map_access */
Vitaliy Margolenbaffcb92005-11-22 14:55:42 +0000179 no_lookup_name, /* lookup_name */
Alexandre Julliardb9b1ea92005-06-09 15:39:52 +0000180 no_close_handle, /* close_handle */
Mike McCormack08351072006-01-27 12:13:56 +0100181 dir_destroy /* destroy */
182};
183
184static int dir_get_poll_events( struct fd *fd );
185static int dir_get_info( struct fd *fd );
Mike McCormack01932112006-02-06 11:58:55 +0100186static void dir_cancel_async( struct fd *fd );
Mike McCormack08351072006-01-27 12:13:56 +0100187
188static const struct fd_ops dir_fd_ops =
189{
190 dir_get_poll_events, /* get_poll_events */
191 default_poll_event, /* poll_event */
192 no_flush, /* flush */
193 dir_get_info, /* get_file_info */
194 default_fd_queue_async, /* queue_async */
Mike McCormack01932112006-02-06 11:58:55 +0100195 dir_cancel_async /* cancel_async */
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000196};
197
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000198static struct list change_list = LIST_INIT(change_list);
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000199
Mike McCormackfdf0c682006-01-30 18:15:31 +0100200static void dnotify_adjust_changes( struct dir *dir )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000201{
Alexandre Julliarde8a339c2004-03-01 21:32:02 +0000202#if defined(F_SETSIG) && defined(F_NOTIFY)
Mike McCormackfdf0c682006-01-30 18:15:31 +0100203 int fd = get_unix_fd( dir->fd );
204 unsigned int filter = dir->filter;
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000205 unsigned int val;
206 if ( 0 > fcntl( fd, F_SETSIG, SIGIO) )
207 return;
208
209 val = DN_MULTISHOT;
Robert Shearman37957092005-06-10 19:54:46 +0000210 if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000211 val |= DN_RENAME | DN_DELETE | DN_CREATE;
Robert Shearman37957092005-06-10 19:54:46 +0000212 if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000213 val |= DN_RENAME | DN_DELETE | DN_CREATE;
Robert Shearman37957092005-06-10 19:54:46 +0000214 if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000215 val |= DN_ATTRIB;
Robert Shearman37957092005-06-10 19:54:46 +0000216 if (filter & FILE_NOTIFY_CHANGE_SIZE)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000217 val |= DN_MODIFY;
Robert Shearman37957092005-06-10 19:54:46 +0000218 if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000219 val |= DN_MODIFY;
Robert Shearman37957092005-06-10 19:54:46 +0000220 if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
Hans Leidekkerff496522004-02-05 01:45:58 +0000221 val |= DN_ACCESS;
Robert Shearman37957092005-06-10 19:54:46 +0000222 if (filter & FILE_NOTIFY_CHANGE_CREATION)
Hans Leidekkerff496522004-02-05 01:45:58 +0000223 val |= DN_CREATE;
Robert Shearman37957092005-06-10 19:54:46 +0000224 if (filter & FILE_NOTIFY_CHANGE_SECURITY)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000225 val |= DN_ATTRIB;
226 fcntl( fd, F_NOTIFY, val );
227#endif
228}
229
230/* insert change in the global list */
Mike McCormack08351072006-01-27 12:13:56 +0100231static inline void insert_change( struct dir *dir )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000232{
233 sigset_t sigset;
234
235 sigemptyset( &sigset );
236 sigaddset( &sigset, SIGIO );
237 sigprocmask( SIG_BLOCK, &sigset, NULL );
Mike McCormack08351072006-01-27 12:13:56 +0100238 list_add_head( &change_list, &dir->entry );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000239 sigprocmask( SIG_UNBLOCK, &sigset, NULL );
240}
241
242/* remove change from the global list */
Mike McCormack08351072006-01-27 12:13:56 +0100243static inline void remove_change( struct dir *dir )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000244{
245 sigset_t sigset;
246
247 sigemptyset( &sigset );
248 sigaddset( &sigset, SIGIO );
249 sigprocmask( SIG_BLOCK, &sigset, NULL );
Mike McCormack08351072006-01-27 12:13:56 +0100250 list_remove( &dir->entry );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000251 sigprocmask( SIG_UNBLOCK, &sigset, NULL );
252}
253
Mike McCormack08351072006-01-27 12:13:56 +0100254static void dir_dump( struct object *obj, int verbose )
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000255{
Mike McCormack08351072006-01-27 12:13:56 +0100256 struct dir *dir = (struct dir *)obj;
257 assert( obj->ops == &dir_ops );
258 fprintf( stderr, "Dirfile fd=%p event=%p filter=%08x\n",
259 dir->fd, dir->event, dir->filter );
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000260}
261
Mike McCormack08351072006-01-27 12:13:56 +0100262static int dir_signaled( struct object *obj, struct thread *thread )
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000263{
Mike McCormack08351072006-01-27 12:13:56 +0100264 struct dir *dir = (struct dir *)obj;
265 assert (obj->ops == &dir_ops);
266 return (dir->event == NULL) && dir->signaled;
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000267}
268
269/* enter here directly from SIGIO signal handler */
270void do_change_notify( int unix_fd )
271{
Mike McCormackfdf0c682006-01-30 18:15:31 +0100272 struct dir *dir;
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000273
274 /* FIXME: this is O(n) ... probably can be improved */
Mike McCormackfdf0c682006-01-30 18:15:31 +0100275 LIST_FOR_EACH_ENTRY( dir, &change_list, struct dir, entry )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000276 {
Mike McCormack08351072006-01-27 12:13:56 +0100277 if (get_unix_fd( dir->fd ) != unix_fd) continue;
278 interlocked_xchg_add( &dir->notified, 1 );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000279 break;
280 }
281}
282
Mike McCormackfdf0c682006-01-30 18:15:31 +0100283static void dir_signal_changed( struct dir *dir )
284{
285 if (dir->event)
286 set_event( dir->event );
287 else
288 wake_up( &dir->obj, 0 );
289}
290
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000291/* SIGIO callback, called synchronously with the poll loop */
292void sigio_callback(void)
293{
Mike McCormackfdf0c682006-01-30 18:15:31 +0100294 struct dir *dir;
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000295
Mike McCormackfdf0c682006-01-30 18:15:31 +0100296 LIST_FOR_EACH_ENTRY( dir, &change_list, struct dir, entry )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000297 {
Mike McCormack08351072006-01-27 12:13:56 +0100298 long count = interlocked_xchg( &dir->notified, 0 );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000299 if (count)
300 {
Mike McCormack08351072006-01-27 12:13:56 +0100301 dir->signaled += count;
302 if (dir->signaled == count) /* was it 0? */
Mike McCormackfdf0c682006-01-30 18:15:31 +0100303 dir_signal_changed( dir );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000304 }
305 }
Alexandre Julliard63cb0f81998-12-31 15:43:48 +0000306}
307
Mike McCormack08351072006-01-27 12:13:56 +0100308static struct fd *dir_get_fd( struct object *obj )
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000309{
Mike McCormack08351072006-01-27 12:13:56 +0100310 struct dir *dir = (struct dir *)obj;
311 assert( obj->ops == &dir_ops );
312 return (struct fd *)grab_object( dir->fd );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000313}
314
Mike McCormack08351072006-01-27 12:13:56 +0100315static unsigned int dir_map_access( struct object *obj, unsigned int access )
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000316{
Mike McCormack08351072006-01-27 12:13:56 +0100317 if (access & GENERIC_READ) access |= FILE_GENERIC_READ;
318 if (access & GENERIC_WRITE) access |= FILE_GENERIC_WRITE;
319 if (access & GENERIC_EXECUTE) access |= FILE_GENERIC_EXECUTE;
320 if (access & GENERIC_ALL) access |= FILE_ALL_ACCESS;
321 return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
322}
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000323
Mike McCormack01932112006-02-06 11:58:55 +0100324static struct change_record *get_first_change_record( struct dir *dir )
325{
326 struct list *ptr = list_head( &dir->change_records );
327 if (!ptr) return NULL;
328 list_remove( ptr );
329 return LIST_ENTRY( ptr, struct change_record, entry );
330}
331
Mike McCormack08351072006-01-27 12:13:56 +0100332static void dir_destroy( struct object *obj )
333{
Mike McCormack01932112006-02-06 11:58:55 +0100334 struct change_record *record;
Mike McCormack08351072006-01-27 12:13:56 +0100335 struct dir *dir = (struct dir *)obj;
336 assert (obj->ops == &dir_ops);
337
338 if (dir->filter)
339 remove_change( dir );
340
Mike McCormackfbc00db2006-02-17 11:40:38 +0100341 if (dir->inode)
342 {
343 list_remove( &dir->in_entry );
344 free_inode( dir->inode );
345 }
Mike McCormackfdf0c682006-01-30 18:15:31 +0100346
Mike McCormack01932112006-02-06 11:58:55 +0100347 async_terminate_queue( &dir->change_q, STATUS_CANCELLED );
348 while ((record = get_first_change_record( dir ))) free( record );
349
Mike McCormack08351072006-01-27 12:13:56 +0100350 if (dir->event)
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000351 {
Mike McCormack08351072006-01-27 12:13:56 +0100352 set_event( dir->event );
353 release_object( dir->event );
Alexandre Julliard3e588e32003-03-26 23:41:43 +0000354 }
Mike McCormack08351072006-01-27 12:13:56 +0100355 release_object( dir->fd );
Mike McCormackfbc00db2006-02-17 11:40:38 +0100356
357 if (inotify_fd && list_empty( &change_list ))
358 {
359 release_object( inotify_fd );
360 inotify_fd = NULL;
361 }
Mike McCormack08351072006-01-27 12:13:56 +0100362}
363
364static struct dir *
365get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access )
366{
367 return (struct dir *)get_handle_obj( process, handle, access, &dir_ops );
368}
369
370static int dir_get_poll_events( struct fd *fd )
371{
372 return 0;
373}
374
375static int dir_get_info( struct fd *fd )
376{
377 return 0;
378}
379
Mike McCormack01932112006-02-06 11:58:55 +0100380static void dir_cancel_async( struct fd *fd )
381{
382 struct dir *dir = (struct dir *) get_fd_user( fd );
383 async_terminate_queue( &dir->change_q, STATUS_CANCELLED );
384}
385
Mike McCormackfdf0c682006-01-30 18:15:31 +0100386
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100387#ifdef USE_INOTIFY
388
Mike McCormackfbc00db2006-02-17 11:40:38 +0100389#define HASH_SIZE 31
390
391struct inode {
Mike McCormacke4faabf2006-02-21 16:58:19 +0900392 struct list ch_entry; /* entry in the children list */
393 struct list children; /* children of this inode */
394 struct inode *parent; /* parent of this inode */
Mike McCormackfbc00db2006-02-17 11:40:38 +0100395 struct list dirs; /* directory handles watching this inode */
396 struct list ino_entry; /* entry in the inode hash */
397 struct list wd_entry; /* entry in the watch descriptor hash */
398 dev_t dev; /* device number */
399 ino_t ino; /* device's inode number */
400 int wd; /* inotify's watch descriptor */
Mike McCormacke4faabf2006-02-21 16:58:19 +0900401 char *name; /* basename name of the inode */
Mike McCormackfbc00db2006-02-17 11:40:38 +0100402};
403
404struct list inode_hash[ HASH_SIZE ];
405struct list wd_hash[ HASH_SIZE ];
406
Mike McCormacke4faabf2006-02-21 16:58:19 +0900407static int inotify_add_dir( char *path, unsigned int filter );
408
Mike McCormackfbc00db2006-02-17 11:40:38 +0100409static struct inode *inode_from_wd( int wd )
410{
411 struct list *bucket = &wd_hash[ wd % HASH_SIZE ];
412 struct inode *inode;
413
414 LIST_FOR_EACH_ENTRY( inode, bucket, struct inode, wd_entry )
415 if (inode->wd == wd)
416 return inode;
417
418 return NULL;
419}
420
421static inline struct list *get_hash_list( dev_t dev, ino_t ino )
422{
423 return &inode_hash[ (ino ^ dev) % HASH_SIZE ];
424}
425
Mike McCormacke4faabf2006-02-21 16:58:19 +0900426static struct inode *find_inode( dev_t dev, ino_t ino )
Mike McCormackfbc00db2006-02-17 11:40:38 +0100427{
428 struct list *bucket = get_hash_list( dev, ino );
429 struct inode *inode;
430
431 LIST_FOR_EACH_ENTRY( inode, bucket, struct inode, ino_entry )
432 if (inode->ino == ino && inode->dev == dev)
433 return inode;
434
435 return NULL;
436}
437
438static struct inode *create_inode( dev_t dev, ino_t ino )
439{
440 struct inode *inode;
441
442 inode = malloc( sizeof *inode );
443 if (inode)
444 {
Mike McCormacke4faabf2006-02-21 16:58:19 +0900445 list_init( &inode->children );
Mike McCormackfbc00db2006-02-17 11:40:38 +0100446 list_init( &inode->dirs );
447 inode->ino = ino;
448 inode->dev = dev;
449 inode->wd = -1;
Mike McCormacke4faabf2006-02-21 16:58:19 +0900450 inode->parent = NULL;
451 inode->name = NULL;
Mike McCormackfbc00db2006-02-17 11:40:38 +0100452 list_add_tail( get_hash_list( dev, ino ), &inode->ino_entry );
453 }
454 return inode;
455}
456
Mike McCormacke4faabf2006-02-21 16:58:19 +0900457static struct inode *get_inode( dev_t dev, ino_t ino )
458{
459 struct inode *inode;
460
461 inode = find_inode( dev, ino );
462 if (inode)
463 return inode;
464 return create_inode( dev, ino );
465}
466
Mike McCormackfbc00db2006-02-17 11:40:38 +0100467static void inode_set_wd( struct inode *inode, int wd )
468{
469 if (inode->wd != -1)
470 list_remove( &inode->wd_entry );
471 inode->wd = wd;
472 list_add_tail( &wd_hash[ wd % HASH_SIZE ], &inode->wd_entry );
473}
474
Mike McCormacke4faabf2006-02-21 16:58:19 +0900475static void inode_set_name( struct inode *inode, const char *name )
476{
477 if (inode->name)
478 free (inode->name);
479 inode->name = name ? strdup( name ) : NULL;
480}
481
Mike McCormackfbc00db2006-02-17 11:40:38 +0100482static void free_inode( struct inode *inode )
483{
Mike McCormacke4faabf2006-02-21 16:58:19 +0900484 int subtree = 0, watches = 0;
485 struct dir *dir;
486
487 LIST_FOR_EACH_ENTRY( dir, &inode->dirs, struct dir, in_entry )
488 {
489 subtree |= dir->subtree;
490 watches++;
491 }
492
493 if (!subtree && !inode->parent)
494 {
Mike McCormack309a9bf2006-02-22 23:11:17 +0900495 struct inode *tmp, *next;
496 LIST_FOR_EACH_ENTRY_SAFE( tmp, next, &inode->children,
497 struct inode, ch_entry )
Mike McCormacke4faabf2006-02-21 16:58:19 +0900498 {
Mike McCormacke4faabf2006-02-21 16:58:19 +0900499 assert( tmp != inode );
500 assert( tmp->parent == inode );
501 free_inode( tmp );
502 }
503 }
504
505 if (watches)
Mike McCormackfbc00db2006-02-17 11:40:38 +0100506 return;
507
Mike McCormacke4faabf2006-02-21 16:58:19 +0900508 if (inode->parent)
509 list_remove( &inode->ch_entry );
510
Mike McCormackfbc00db2006-02-17 11:40:38 +0100511 if (inode->wd != -1)
512 {
513 inotify_remove_watch( get_unix_fd( inotify_fd ), inode->wd );
514 list_remove( &inode->wd_entry );
515 }
516 list_remove( &inode->ino_entry );
Mike McCormacke4faabf2006-02-21 16:58:19 +0900517
518 free( inode->name );
Mike McCormackfbc00db2006-02-17 11:40:38 +0100519 free( inode );
520}
521
Mike McCormacke4faabf2006-02-21 16:58:19 +0900522static struct inode *inode_add( struct inode *parent,
523 dev_t dev, ino_t ino, const char *name )
524{
525 struct inode *inode;
526
527 inode = get_inode( dev, ino );
528 if (!inode)
529 return NULL;
530
531 if (!inode->parent)
532 {
533 list_add_tail( &parent->children, &inode->ch_entry );
534 inode->parent = parent;
535 assert( inode != parent );
536 }
537 inode_set_name( inode, name );
538
539 return inode;
540}
541
542static struct inode *inode_from_name( struct inode *inode, const char *name )
543{
544 struct inode *i;
545
546 LIST_FOR_EACH_ENTRY( i, &inode->children, struct inode, ch_entry )
547 if (i->name && !strcmp( i->name, name ))
548 return i;
549 return NULL;
550}
551
Mike McCormackfdf0c682006-01-30 18:15:31 +0100552static int inotify_get_poll_events( struct fd *fd );
553static void inotify_poll_event( struct fd *fd, int event );
554static int inotify_get_info( struct fd *fd );
555
556static const struct fd_ops inotify_fd_ops =
557{
558 inotify_get_poll_events, /* get_poll_events */
559 inotify_poll_event, /* poll_event */
560 no_flush, /* flush */
561 inotify_get_info, /* get_file_info */
562 default_fd_queue_async, /* queue_async */
563 default_fd_cancel_async, /* cancel_async */
564};
565
566static int inotify_get_poll_events( struct fd *fd )
567{
568 return POLLIN;
569}
570
Mike McCormacka2813f72006-02-20 12:28:46 +0100571static void inotify_do_change_notify( struct dir *dir, unsigned int action,
572 const char *relpath )
Mike McCormackfdf0c682006-01-30 18:15:31 +0100573{
Mike McCormack01932112006-02-06 11:58:55 +0100574 struct change_record *record;
575
Mike McCormacke4faabf2006-02-21 16:58:19 +0900576 assert( dir->obj.ops == &dir_ops );
577
Mike McCormack0790f952006-02-07 16:50:36 +0100578 if (dir->want_data)
579 {
Mike McCormacka2813f72006-02-20 12:28:46 +0100580 size_t len = strlen(relpath);
Alexandre Julliarde9798322006-02-08 15:06:42 +0100581 record = malloc( offsetof(struct change_record, name[len]) );
Mike McCormack0790f952006-02-07 16:50:36 +0100582 if (!record)
583 return;
Mike McCormack01932112006-02-06 11:58:55 +0100584
Mike McCormacka2813f72006-02-20 12:28:46 +0100585 record->action = action;
586 memcpy( record->name, relpath, len );
Alexandre Julliarde9798322006-02-08 15:06:42 +0100587 record->len = len;
Mike McCormack01932112006-02-06 11:58:55 +0100588
Mike McCormack0790f952006-02-07 16:50:36 +0100589 list_add_tail( &dir->change_records, &record->entry );
590 }
Mike McCormack01932112006-02-06 11:58:55 +0100591
592 if (!list_empty( &dir->change_q ))
593 async_terminate_head( &dir->change_q, STATUS_ALERTED );
594 else
595 {
596 dir->signaled++;
597 dir_signal_changed( dir );
598 }
Mike McCormackfdf0c682006-01-30 18:15:31 +0100599}
600
Mike McCormackfbc00db2006-02-17 11:40:38 +0100601static unsigned int filter_from_event( struct inotify_event *ie )
602{
603 unsigned int filter = 0;
604
605 if (ie->mask & (IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_CREATE))
606 filter |= FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME;
607 if (ie->mask & IN_MODIFY)
608 filter |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
609 if (ie->mask & IN_ATTRIB)
610 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY;
611 if (ie->mask & IN_ACCESS)
612 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
613 if (ie->mask & IN_CREATE)
614 filter |= FILE_NOTIFY_CHANGE_CREATION;
615
616 return filter;
617}
618
Mike McCormacke4faabf2006-02-21 16:58:19 +0900619/* scan up the parent directories for watches */
620static unsigned int filter_from_inode( struct inode *inode, int is_parent )
Mike McCormacka2813f72006-02-20 12:28:46 +0100621{
Mike McCormacke4faabf2006-02-21 16:58:19 +0900622 unsigned int filter = 0;
623 struct dir *dir;
624
625 /* combine filters from parents watching subtrees */
626 while (inode)
627 {
628 LIST_FOR_EACH_ENTRY( dir, &inode->dirs, struct dir, in_entry )
629 if (dir->subtree || !is_parent)
630 filter |= dir->filter;
631 is_parent = 1;
632 inode = inode->parent;
633 }
634
635 return filter;
636}
637
638static char *inode_get_path( struct inode *inode, int sz )
639{
Mike McCormacka2813f72006-02-20 12:28:46 +0100640 struct list *head;
Mike McCormacke4faabf2006-02-21 16:58:19 +0900641 char *path;
642 int len;
643
644 if (!inode)
645 return NULL;
Mike McCormacka2813f72006-02-20 12:28:46 +0100646
647 head = list_head( &inode->dirs );
Mike McCormacke4faabf2006-02-21 16:58:19 +0900648 if (head)
649 {
650 int unix_fd = get_unix_fd( LIST_ENTRY( head, struct dir, in_entry )->fd );
651 path = malloc ( 32 + sz );
652 if (path)
653 sprintf( path, "/proc/self/fd/%u/", unix_fd );
654 return path;
655 }
Mike McCormacka2813f72006-02-20 12:28:46 +0100656
Mike McCormacke4faabf2006-02-21 16:58:19 +0900657 if (!inode->name)
658 return NULL;
659
660 len = strlen( inode->name );
661 path = inode_get_path( inode->parent, sz + len + 1 );
Mike McCormacka2813f72006-02-20 12:28:46 +0100662 if (!path)
Mike McCormacke4faabf2006-02-21 16:58:19 +0900663 return NULL;
664
665 strcat( path, inode->name );
666 strcat( path, "/" );
Mike McCormacka2813f72006-02-20 12:28:46 +0100667
Mike McCormacke4faabf2006-02-21 16:58:19 +0900668 return path;
669}
670
671static int inode_check_dir( struct inode *parent, const char *name )
672{
673 char *path;
674 unsigned int filter;
675 struct inode *inode;
676 struct stat st;
677 int wd = -1, r = -1;
678
679 path = inode_get_path( parent, strlen(name) );
680 if (!path)
681 return r;
682
683 strcat( path, name );
684
Mike McCormacka2813f72006-02-20 12:28:46 +0100685 r = stat( path, &st );
Mike McCormacke4faabf2006-02-21 16:58:19 +0900686 if (r < 0) goto end;
Mike McCormacka2813f72006-02-20 12:28:46 +0100687
Mike McCormacke4faabf2006-02-21 16:58:19 +0900688 if (!S_ISDIR(st.st_mode))
689 {
690 r = 0;
691 goto end;
692 }
693
694 r = 1;
695
696 filter = filter_from_inode( parent, 1 );
697 if (!filter)
698 goto end;
699
700 inode = inode_add( parent, st.st_dev, st.st_ino, name );
701 if (!inode || inode->wd != -1)
702 goto end;
703
704 wd = inotify_add_dir( path, filter );
705 if (wd != -1)
706 inode_set_wd( inode, wd );
707 else
708 free_inode( inode );
709
710end:
711 free( path );
712 return r;
713}
714
715static int prepend( char **path, const char *segment )
716{
717 int extra;
718 char *p;
719
720 extra = strlen( segment ) + 1;
721 if (*path)
722 {
723 int len = strlen( *path ) + 1;
724 p = realloc( *path, len + extra );
725 if (!p) return 0;
726 memmove( &p[ extra ], p, len );
727 p[ extra - 1 ] = '/';
728 memcpy( p, segment, extra - 1 );
729 }
730 else
731 {
732 p = malloc( extra );
733 if (!p) return 0;
734 memcpy( p, segment, extra );
735 }
736
737 *path = p;
738
739 return 1;
Mike McCormacka2813f72006-02-20 12:28:46 +0100740}
741
Mike McCormackfbc00db2006-02-17 11:40:38 +0100742static void inotify_notify_all( struct inotify_event *ie )
743{
Mike McCormacka2813f72006-02-20 12:28:46 +0100744 unsigned int filter, action;
Mike McCormacke4faabf2006-02-21 16:58:19 +0900745 struct inode *inode, *i;
746 char *path = NULL;
Mike McCormackfbc00db2006-02-17 11:40:38 +0100747 struct dir *dir;
Mike McCormackfbc00db2006-02-17 11:40:38 +0100748
749 inode = inode_from_wd( ie->wd );
750 if (!inode)
751 {
752 fprintf( stderr, "no inode matches %d\n", ie->wd);
753 return;
754 }
755
756 filter = filter_from_event( ie );
Mike McCormacka2813f72006-02-20 12:28:46 +0100757
758 if (ie->mask & IN_CREATE)
759 {
Mike McCormacke4faabf2006-02-21 16:58:19 +0900760 switch (inode_check_dir( inode, ie->name ))
Mike McCormacka2813f72006-02-20 12:28:46 +0100761 {
762 case 1:
763 filter &= ~FILE_NOTIFY_CHANGE_FILE_NAME;
764 break;
765 case 0:
766 filter &= ~FILE_NOTIFY_CHANGE_DIR_NAME;
767 break;
768 default:
769 break;
770 /* Maybe the file disappeared before we could check it? */
771 }
772 action = FILE_ACTION_ADDED;
773 }
774 else if (ie->mask & IN_DELETE)
775 action = FILE_ACTION_REMOVED;
776 else
777 action = FILE_ACTION_MODIFIED;
Mike McCormackfbc00db2006-02-17 11:40:38 +0100778
Mike McCormacke4faabf2006-02-21 16:58:19 +0900779 /*
Francois Gouget4aa64182006-02-24 16:11:36 +0100780 * Work our way up the inode hierarchy
Mike McCormacke4faabf2006-02-21 16:58:19 +0900781 * extending the relative path as we go
Francois Gouget4aa64182006-02-24 16:11:36 +0100782 * and notifying all recursive watches.
Mike McCormacke4faabf2006-02-21 16:58:19 +0900783 */
784 if (!prepend( &path, ie->name ))
785 return;
786
787 for (i = inode; i; i = i->parent)
788 {
789 LIST_FOR_EACH_ENTRY( dir, &i->dirs, struct dir, in_entry )
790 if ((filter & dir->filter) && (i==inode || dir->subtree))
791 inotify_do_change_notify( dir, action, path );
792
793 if (!i->name || !prepend( &path, i->name ))
794 break;
795 }
796
797 free( path );
798
799 if (ie->mask & IN_DELETE)
800 {
801 i = inode_from_name( inode, ie->name );
802 if (i)
803 free_inode( i );
804 }
Mike McCormackfbc00db2006-02-17 11:40:38 +0100805}
806
Mike McCormackfdf0c682006-01-30 18:15:31 +0100807static void inotify_poll_event( struct fd *fd, int event )
808{
809 int r, ofs, unix_fd;
810 char buffer[0x1000];
811 struct inotify_event *ie;
Mike McCormackfdf0c682006-01-30 18:15:31 +0100812
813 unix_fd = get_unix_fd( fd );
814 r = read( unix_fd, buffer, sizeof buffer );
815 if (r < 0)
816 {
817 fprintf(stderr,"inotify_poll_event(): inotify read failed!\n");
818 return;
819 }
820
Alexandre Julliarde9798322006-02-08 15:06:42 +0100821 for( ofs = 0; ofs < r - offsetof(struct inotify_event, name); )
Mike McCormackfdf0c682006-01-30 18:15:31 +0100822 {
823 ie = (struct inotify_event*) &buffer[ofs];
824 if (!ie->len)
825 break;
Alexandre Julliarde9798322006-02-08 15:06:42 +0100826 ofs += offsetof( struct inotify_event, name[ie->len] );
827 if (ofs > r) break;
Mike McCormackfbc00db2006-02-17 11:40:38 +0100828 inotify_notify_all( ie );
Mike McCormackfdf0c682006-01-30 18:15:31 +0100829 }
830}
831
832static int inotify_get_info( struct fd *fd )
833{
834 return 0;
835}
836
Mike McCormackfbc00db2006-02-17 11:40:38 +0100837static inline struct fd *create_inotify_fd( void )
Mike McCormackfdf0c682006-01-30 18:15:31 +0100838{
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100839 int unix_fd;
840
841 unix_fd = inotify_init();
842 if (unix_fd<0)
843 return NULL;
Mike McCormackfbc00db2006-02-17 11:40:38 +0100844 return create_anonymous_fd( &inotify_fd_ops, unix_fd, NULL );
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100845}
846
Mike McCormackfbc00db2006-02-17 11:40:38 +0100847static int map_flags( unsigned int filter )
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100848{
Mike McCormacke4faabf2006-02-21 16:58:19 +0900849 unsigned int mask;
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100850
Mike McCormacke4faabf2006-02-21 16:58:19 +0900851 /* always watch these so we can track subdirectories in recursive watches */
852 mask = (IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF);
853
Mike McCormackfdf0c682006-01-30 18:15:31 +0100854 if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
855 mask |= IN_ATTRIB;
856 if (filter & FILE_NOTIFY_CHANGE_SIZE)
857 mask |= IN_MODIFY;
858 if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
859 mask |= IN_MODIFY;
860 if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
861 mask |= IN_ACCESS;
Mike McCormackfdf0c682006-01-30 18:15:31 +0100862 if (filter & FILE_NOTIFY_CHANGE_SECURITY)
863 mask |= IN_ATTRIB;
864
Mike McCormackfbc00db2006-02-17 11:40:38 +0100865 return mask;
866}
867
868static int inotify_add_dir( char *path, unsigned int filter )
869{
870 int wd = inotify_add_watch( get_unix_fd( inotify_fd ),
871 path, map_flags( filter ) );
872 if (wd != -1)
873 set_fd_events( inotify_fd, POLLIN );
874 return wd;
875}
876
Mike McCormackfbc00db2006-02-17 11:40:38 +0100877static int init_inotify( void )
878{
879 int i;
880
881 if (inotify_fd)
882 return 1;
883
884 inotify_fd = create_inotify_fd();
885 if (!inotify_fd)
886 return 0;
887
888 for (i=0; i<HASH_SIZE; i++)
889 {
890 list_init( &inode_hash[i] );
891 list_init( &wd_hash[i] );
892 }
893
Alexandre Julliardcf9ced52006-02-05 12:19:56 +0100894 return 1;
Mike McCormackfdf0c682006-01-30 18:15:31 +0100895}
896
Mike McCormackfbc00db2006-02-17 11:40:38 +0100897static int inotify_adjust_changes( struct dir *dir )
898{
899 unsigned int filter;
900 struct inode *inode;
901 struct stat st;
902 char path[32];
903 int wd, unix_fd;
904
905 if (!inotify_fd)
906 return 0;
907
908 unix_fd = get_unix_fd( dir->fd );
909
910 inode = dir->inode;
911 if (!inode)
912 {
913 /* check if this fd is already being watched */
914 if (-1 == fstat( unix_fd, &st ))
915 return 0;
916
917 inode = get_inode( st.st_dev, st.st_ino );
918 if (!inode)
919 inode = create_inode( st.st_dev, st.st_ino );
920 if (!inode)
921 return 0;
922 list_add_tail( &inode->dirs, &dir->in_entry );
923 dir->inode = inode;
924 }
925
Mike McCormacke4faabf2006-02-21 16:58:19 +0900926 filter = filter_from_inode( inode, 0 );
Mike McCormackfbc00db2006-02-17 11:40:38 +0100927
928 sprintf( path, "/proc/self/fd/%u", unix_fd );
929 wd = inotify_add_dir( path, filter );
930 if (wd == -1) return 0;
931
932 inode_set_wd( inode, wd );
933
934 return 1;
935}
936
Mike McCormack42121082006-02-23 00:45:34 +0900937static char *get_basename( const char *link )
938{
939 char *buffer, *name = NULL;
940 int r, n = 0x100;
941
942 while (1)
943 {
944 buffer = malloc( n );
Alexandre Julliardc9cc7e32006-03-02 18:03:32 +0100945 if (!buffer) return NULL;
Mike McCormack42121082006-02-23 00:45:34 +0900946
947 r = readlink( link, buffer, n );
948 if (r < 0)
949 break;
950
951 if (r < n)
952 {
953 name = buffer;
954 break;
955 }
956 free( buffer );
957 n *= 2;
958 }
959
960 if (name)
961 {
962 while (r > 0 && name[ r - 1 ] == '/' )
963 r--;
964 name[ r ] = 0;
965
966 name = strrchr( name, '/' );
967 if (name)
968 name = strdup( &name[1] );
969 }
970
971 free( buffer );
972 return name;
973}
974
975static int dir_add_to_existing_notify( struct dir *dir )
976{
977 struct inode *inode, *parent;
978 unsigned int filter = 0;
979 struct stat st, st_new;
980 char link[35], *name;
981 int wd, unix_fd;
982
983 if (!inotify_fd)
984 return 0;
985
986 unix_fd = get_unix_fd( dir->fd );
987
988 /* check if it's in the list of inodes we want to watch */
989 if (-1 == fstat( unix_fd, &st_new ))
990 return 0;
991 inode = find_inode( st_new.st_dev, st_new.st_ino );
992 if (inode)
993 return 0;
994
995 /* lookup the parent */
996 sprintf( link, "/proc/self/fd/%u/..", unix_fd );
997 if (-1 == stat( link, &st ))
998 return 0;
999
1000 /*
1001 * If there's no parent, stop. We could keep going adding
1002 * ../ to the path until we hit the root of the tree or
1003 * find a recursively watched ancestor.
1004 * Assume it's too expensive to search up the tree for now.
1005 */
1006 parent = find_inode( st.st_dev, st.st_ino );
1007 if (!parent)
1008 return 0;
1009
1010 if (parent->wd == -1)
1011 return 0;
1012
1013 filter = filter_from_inode( parent, 1 );
1014 if (!filter)
1015 return 0;
1016
1017 sprintf( link, "/proc/self/fd/%u", unix_fd );
1018 name = get_basename( link );
1019 if (!name)
1020 return 0;
1021 inode = inode_add( parent, st_new.st_dev, st_new.st_ino, name );
1022 free( name );
1023 if (!inode)
1024 return 0;
1025
1026 /* Couldn't find this inode at the start of the function, must be new */
1027 assert( inode->wd == -1 );
1028
1029 wd = inotify_add_dir( link, filter );
1030 if (wd != -1)
1031 inode_set_wd( inode, wd );
1032
1033 return 1;
1034}
1035
Mike McCormackfbc00db2006-02-17 11:40:38 +01001036#else
1037
1038static int init_inotify( void )
1039{
1040 return 0;
1041}
1042
1043static int inotify_adjust_changes( struct dir *dir )
1044{
1045 return 0;
1046}
1047
1048static void free_inode( struct inode *inode )
1049{
1050 assert( 0 );
1051}
1052
Mike McCormack42121082006-02-23 00:45:34 +09001053static int dir_add_to_existing_notify( struct dir *dir )
1054{
1055 return 0;
1056}
1057
Alexandre Julliardcf9ced52006-02-05 12:19:56 +01001058#endif /* USE_INOTIFY */
Mike McCormackfdf0c682006-01-30 18:15:31 +01001059
Mike McCormackfbc00db2006-02-17 11:40:38 +01001060struct object *create_dir_obj( struct fd *fd )
1061{
1062 struct dir *dir;
1063
1064 dir = alloc_object( &dir_ops );
1065 if (!dir)
1066 return NULL;
1067
1068 list_init( &dir->change_q );
1069 list_init( &dir->change_records );
1070 dir->event = NULL;
1071 dir->filter = 0;
1072 dir->notified = 0;
1073 dir->signaled = 0;
1074 dir->want_data = 0;
1075 dir->inode = NULL;
1076 grab_object( fd );
1077 dir->fd = fd;
1078 set_fd_user( fd, &dir_fd_ops, &dir->obj );
1079
Mike McCormack42121082006-02-23 00:45:34 +09001080 dir_add_to_existing_notify( dir );
1081
Mike McCormackfbc00db2006-02-17 11:40:38 +01001082 return &dir->obj;
1083}
1084
Mike McCormack08351072006-01-27 12:13:56 +01001085/* enable change notifications for a directory */
1086DECL_HANDLER(read_directory_changes)
1087{
1088 struct event *event = NULL;
1089 struct dir *dir;
1090
1091 if (!req->filter)
1092 {
1093 set_error(STATUS_INVALID_PARAMETER);
1094 return;
1095 }
1096
1097 dir = get_dir_obj( current->process, req->handle, 0 );
1098 if (!dir)
1099 return;
1100
1101 /* possibly send changes through an event flag */
1102 if (req->event)
1103 {
1104 event = get_event_obj( current->process, req->event, EVENT_MODIFY_STATE );
1105 if (!event)
1106 goto end;
1107 }
1108
1109 /* discard the current data, and move onto the next event */
1110 if (dir->event) release_object( dir->event );
1111 dir->event = event;
1112
Mike McCormack01932112006-02-06 11:58:55 +01001113 /* requests don't timeout */
1114 if ( req->io_apc && !create_async( current, NULL, &dir->change_q,
1115 req->io_apc, req->io_user, req->io_sb ))
1116 return;
1117
Mike McCormack08351072006-01-27 12:13:56 +01001118 /* assign it once */
1119 if (!dir->filter)
1120 {
Mike McCormackfbc00db2006-02-17 11:40:38 +01001121 init_inotify();
Mike McCormack08351072006-01-27 12:13:56 +01001122 insert_change( dir );
1123 dir->filter = req->filter;
Mike McCormacke4faabf2006-02-21 16:58:19 +09001124 dir->subtree = req->subtree;
Mike McCormack0790f952006-02-07 16:50:36 +01001125 dir->want_data = req->want_data;
Mike McCormack08351072006-01-27 12:13:56 +01001126 }
1127
1128 /* remove any notifications */
1129 if (dir->signaled>0)
1130 dir->signaled--;
1131
Mike McCormack0790f952006-02-07 16:50:36 +01001132 /* clear the event */
1133 if (event)
1134 reset_event( event );
1135
Mike McCormackfdf0c682006-01-30 18:15:31 +01001136 /* setup the real notification */
Alexandre Julliardcf9ced52006-02-05 12:19:56 +01001137 if (!inotify_adjust_changes( dir ))
Mike McCormackfdf0c682006-01-30 18:15:31 +01001138 dnotify_adjust_changes( dir );
Mike McCormack08351072006-01-27 12:13:56 +01001139
1140 set_error(STATUS_PENDING);
1141
1142end:
1143 release_object( dir );
Alexandre Julliard43c190e1999-05-15 10:48:19 +00001144}
Mike McCormack01932112006-02-06 11:58:55 +01001145
1146DECL_HANDLER(read_change)
1147{
1148 struct change_record *record;
1149 struct dir *dir;
1150
1151 dir = get_dir_obj( current->process, req->handle, 0 );
1152 if (!dir)
1153 return;
1154
1155 if ((record = get_first_change_record( dir )) != NULL)
1156 {
1157 reply->action = record->action;
1158 set_reply_data( record->name, record->len );
1159 free( record );
1160 }
1161 else
1162 set_error( STATUS_NO_DATA_DETECTED );
1163
1164 /* now signal it */
1165 dir->signaled++;
1166 dir_signal_changed( dir );
1167
1168 release_object( dir );
1169}