d3dx9: Implement ID3DXBaseEffect::SetValue().
[wine] / server / change.c
1 /*
2  * Server-side change notification management
3  *
4  * Copyright (C) 1998 Alexandre Julliard
5  * Copyright (C) 2006 Mike McCormack
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
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>
31 #include <sys/types.h>
32 #include <limits.h>
33 #include <dirent.h>
34 #include <errno.h>
35 #ifdef HAVE_SYS_ERRNO_H
36 #include <sys/errno.h>
37 #endif
38
39 #include "ntstatus.h"
40 #define WIN32_NO_STATUS
41 #include "windef.h"
42
43 #include "file.h"
44 #include "handle.h"
45 #include "thread.h"
46 #include "request.h"
47 #include "process.h"
48 #include "security.h"
49 #include "winternl.h"
50
51 /* dnotify support */
52
53 #ifdef linux
54 #ifndef F_NOTIFY
55 #define F_NOTIFY 1026
56 #define DN_ACCESS       0x00000001      /* File accessed */
57 #define DN_MODIFY       0x00000002      /* File modified */
58 #define DN_CREATE       0x00000004      /* File created */
59 #define DN_DELETE       0x00000008      /* File removed */
60 #define DN_RENAME       0x00000010      /* File renamed */
61 #define DN_ATTRIB       0x00000020      /* File changed attributes */
62 #define DN_MULTISHOT    0x80000000      /* Don't remove notifier */
63 #endif
64 #endif
65
66 /* inotify support */
67
68 #ifdef HAVE_SYS_INOTIFY_H
69 #include <sys/inotify.h>
70 #define USE_INOTIFY
71 #elif defined(__linux__) && defined(__i386__)
72
73 #define SYS_inotify_init        291
74 #define SYS_inotify_add_watch   292
75 #define SYS_inotify_rm_watch    293
76
77 struct inotify_event {
78     int           wd;
79     unsigned int  mask;
80     unsigned int  cookie;
81     unsigned int  len;
82     char          name[1];
83 };
84
85 #define IN_ACCESS        0x00000001
86 #define IN_MODIFY        0x00000002
87 #define IN_ATTRIB        0x00000004
88 #define IN_CLOSE_WRITE   0x00000008
89 #define IN_CLOSE_NOWRITE 0x00000010
90 #define IN_OPEN          0x00000020
91 #define IN_MOVED_FROM    0x00000040
92 #define IN_MOVED_TO      0x00000080
93 #define IN_CREATE        0x00000100
94 #define IN_DELETE        0x00000200
95 #define IN_DELETE_SELF   0x00000400
96
97 #define IN_ISDIR         0x40000000
98
99 static inline int inotify_init( void )
100 {
101     return syscall( SYS_inotify_init );
102 }
103
104 static inline int inotify_add_watch( int fd, const char *name, unsigned int mask )
105 {
106     return syscall( SYS_inotify_add_watch, fd, name, mask );
107 }
108
109 static inline int inotify_rm_watch( int fd, int wd )
110 {
111     return syscall( SYS_inotify_rm_watch, fd, wd );
112 }
113
114 #define USE_INOTIFY
115
116 #endif
117
118 struct inode;
119
120 static void free_inode( struct inode *inode );
121
122 static struct fd *inotify_fd;
123
124 struct change_record {
125     struct list entry;
126     unsigned int cookie;
127     struct filesystem_event event;
128 };
129
130 struct dir
131 {
132     struct object  obj;      /* object header */
133     struct fd     *fd;       /* file descriptor to the directory */
134     mode_t         mode;     /* file stat.st_mode */
135     uid_t          uid;      /* file stat.st_uid */
136     struct list    entry;    /* entry in global change notifications list */
137     unsigned int   filter;   /* notification filter */
138     int            notified; /* SIGIO counter */
139     int            want_data; /* return change data */
140     int            subtree;  /* do we want to watch subdirectories? */
141     struct list    change_records;   /* data for the change */
142     struct list    in_entry; /* entry in the inode dirs list */
143     struct inode  *inode;    /* inode of the associated directory */
144 };
145
146 static struct fd *dir_get_fd( struct object *obj );
147 static struct security_descriptor *dir_get_sd( struct object *obj );
148 static int dir_set_sd( struct object *obj, const struct security_descriptor *sd,
149                        unsigned int set_info );
150 static void dir_dump( struct object *obj, int verbose );
151 static void dir_destroy( struct object *obj );
152
153 static const struct object_ops dir_ops =
154 {
155     sizeof(struct dir),       /* size */
156     dir_dump,                 /* dump */
157     no_get_type,              /* get_type */
158     add_queue,                /* add_queue */
159     remove_queue,             /* remove_queue */
160     default_fd_signaled,      /* signaled */
161     no_satisfied,             /* satisfied */
162     no_signal,                /* signal */
163     dir_get_fd,               /* get_fd */
164     default_fd_map_access,    /* map_access */
165     dir_get_sd,               /* get_sd */
166     dir_set_sd,               /* set_sd */
167     no_lookup_name,           /* lookup_name */
168     no_open_file,             /* open_file */
169     fd_close_handle,          /* close_handle */
170     dir_destroy               /* destroy */
171 };
172
173 static int dir_get_poll_events( struct fd *fd );
174 static enum server_fd_type dir_get_fd_type( struct fd *fd );
175
176 static const struct fd_ops dir_fd_ops =
177 {
178     dir_get_poll_events,         /* get_poll_events */
179     default_poll_event,          /* poll_event */
180     no_flush,                    /* flush */
181     dir_get_fd_type,             /* get_fd_type */
182     default_fd_ioctl,            /* ioctl */
183     default_fd_queue_async,      /* queue_async */
184     default_fd_reselect_async,   /* reselect_async */
185     default_fd_cancel_async      /* cancel_async */
186 };
187
188 static struct list change_list = LIST_INIT(change_list);
189
190 static void dnotify_adjust_changes( struct dir *dir )
191 {
192 #if defined(F_SETSIG) && defined(F_NOTIFY)
193     int fd = get_unix_fd( dir->fd );
194     unsigned int filter = dir->filter;
195     unsigned int val;
196     if ( 0 > fcntl( fd, F_SETSIG, SIGIO) )
197         return;
198
199     val = DN_MULTISHOT;
200     if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
201         val |= DN_RENAME | DN_DELETE | DN_CREATE;
202     if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
203         val |= DN_RENAME | DN_DELETE | DN_CREATE;
204     if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
205         val |= DN_ATTRIB;
206     if (filter & FILE_NOTIFY_CHANGE_SIZE)
207         val |= DN_MODIFY;
208     if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
209         val |= DN_MODIFY;
210     if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
211         val |= DN_ACCESS;
212     if (filter & FILE_NOTIFY_CHANGE_CREATION)
213         val |= DN_CREATE;
214     if (filter & FILE_NOTIFY_CHANGE_SECURITY)
215         val |= DN_ATTRIB;
216     fcntl( fd, F_NOTIFY, val );
217 #endif
218 }
219
220 /* insert change in the global list */
221 static inline void insert_change( struct dir *dir )
222 {
223     sigset_t sigset;
224
225     sigemptyset( &sigset );
226     sigaddset( &sigset, SIGIO );
227     sigprocmask( SIG_BLOCK, &sigset, NULL );
228     list_add_head( &change_list, &dir->entry );
229     sigprocmask( SIG_UNBLOCK, &sigset, NULL );
230 }
231
232 /* remove change from the global list */
233 static inline void remove_change( struct dir *dir )
234 {
235     sigset_t sigset;
236
237     sigemptyset( &sigset );
238     sigaddset( &sigset, SIGIO );
239     sigprocmask( SIG_BLOCK, &sigset, NULL );
240     list_remove( &dir->entry );
241     sigprocmask( SIG_UNBLOCK, &sigset, NULL );
242 }
243
244 static void dir_dump( struct object *obj, int verbose )
245 {
246     struct dir *dir = (struct dir *)obj;
247     assert( obj->ops == &dir_ops );
248     fprintf( stderr, "Dirfile fd=%p filter=%08x\n", dir->fd, dir->filter );
249 }
250
251 /* enter here directly from SIGIO signal handler */
252 void do_change_notify( int unix_fd )
253 {
254     struct dir *dir;
255
256     /* FIXME: this is O(n) ... probably can be improved */
257     LIST_FOR_EACH_ENTRY( dir, &change_list, struct dir, entry )
258     {
259         if (get_unix_fd( dir->fd ) != unix_fd) continue;
260         interlocked_xchg_add( &dir->notified, 1 );
261         break;
262     }
263 }
264
265 /* SIGIO callback, called synchronously with the poll loop */
266 void sigio_callback(void)
267 {
268     struct dir *dir;
269
270     LIST_FOR_EACH_ENTRY( dir, &change_list, struct dir, entry )
271     {
272         if (interlocked_xchg( &dir->notified, 0 ))
273             fd_async_wake_up( dir->fd, ASYNC_TYPE_WAIT, STATUS_NOTIFY_ENUM_DIR );
274     }
275 }
276
277 static struct fd *dir_get_fd( struct object *obj )
278 {
279     struct dir *dir = (struct dir *)obj;
280     assert( obj->ops == &dir_ops );
281     return (struct fd *)grab_object( dir->fd );
282 }
283
284 static int get_dir_unix_fd( struct dir *dir )
285 {
286     return get_unix_fd( dir->fd );
287 }
288
289 static struct security_descriptor *dir_get_sd( struct object *obj )
290 {
291     struct dir *dir = (struct dir *)obj;
292     int unix_fd;
293     struct stat st;
294     struct security_descriptor *sd;
295     assert( obj->ops == &dir_ops );
296
297     unix_fd = get_dir_unix_fd( dir );
298
299     if (unix_fd == -1 || fstat( unix_fd, &st ) == -1)
300         return obj->sd;
301
302     /* mode and uid the same? if so, no need to re-generate security descriptor */
303     if (obj->sd &&
304         (st.st_mode & (S_IRWXU|S_IRWXO)) == (dir->mode & (S_IRWXU|S_IRWXO)) &&
305         (st.st_uid == dir->uid))
306         return obj->sd;
307
308     sd = mode_to_sd( st.st_mode,
309                      security_unix_uid_to_sid( st.st_uid ),
310                      token_get_primary_group( current->process->token ));
311     if (!sd) return obj->sd;
312
313     dir->mode = st.st_mode;
314     dir->uid = st.st_uid;
315     free( obj->sd );
316     obj->sd = sd;
317     return sd;
318 }
319
320 static int dir_set_sd( struct object *obj, const struct security_descriptor *sd,
321                        unsigned int set_info )
322 {
323     struct dir *dir = (struct dir *)obj;
324     const SID *owner;
325     struct stat st;
326     mode_t mode;
327     int unix_fd;
328
329     assert( obj->ops == &dir_ops );
330
331     unix_fd = get_dir_unix_fd( dir );
332
333     if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return 1;
334
335     if (set_info & OWNER_SECURITY_INFORMATION)
336     {
337         owner = sd_get_owner( sd );
338         if (!owner)
339         {
340             set_error( STATUS_INVALID_SECURITY_DESCR );
341             return 0;
342         }
343         if (!obj->sd || !security_equal_sid( owner, sd_get_owner( obj->sd ) ))
344         {
345             /* FIXME: get Unix uid and call fchown */
346         }
347     }
348     else if (obj->sd)
349         owner = sd_get_owner( obj->sd );
350     else
351         owner = token_get_user( current->process->token );
352
353     if (set_info & DACL_SECURITY_INFORMATION)
354     {
355         /* keep the bits that we don't map to access rights in the ACL */
356         mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX);
357         mode |= sd_to_mode( sd, owner );
358
359         if (((st.st_mode ^ mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, mode ) == -1)
360         {
361             file_set_error();
362             return 0;
363         }
364     }
365     return 1;
366 }
367
368 static struct change_record *get_first_change_record( struct dir *dir )
369 {
370     struct list *ptr = list_head( &dir->change_records );
371     if (!ptr) return NULL;
372     list_remove( ptr );
373     return LIST_ENTRY( ptr, struct change_record, entry );
374 }
375
376 static void dir_destroy( struct object *obj )
377 {
378     struct change_record *record;
379     struct dir *dir = (struct dir *)obj;
380     assert (obj->ops == &dir_ops);
381
382     if (dir->filter)
383         remove_change( dir );
384
385     if (dir->inode)
386     {
387         list_remove( &dir->in_entry );
388         free_inode( dir->inode );
389     }
390
391     while ((record = get_first_change_record( dir ))) free( record );
392
393     release_object( dir->fd );
394
395     if (inotify_fd && list_empty( &change_list ))
396     {
397         release_object( inotify_fd );
398         inotify_fd = NULL;
399     }
400 }
401
402 struct dir *get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access )
403 {
404     return (struct dir *)get_handle_obj( process, handle, access, &dir_ops );
405 }
406
407 static int dir_get_poll_events( struct fd *fd )
408 {
409     return 0;
410 }
411
412 static enum server_fd_type dir_get_fd_type( struct fd *fd )
413 {
414     return FD_TYPE_DIR;
415 }
416
417 #ifdef USE_INOTIFY
418
419 #define HASH_SIZE 31
420
421 struct inode {
422     struct list ch_entry;    /* entry in the children list */
423     struct list children;    /* children of this inode */
424     struct inode *parent;    /* parent of this inode */
425     struct list dirs;        /* directory handles watching this inode */
426     struct list ino_entry;   /* entry in the inode hash */
427     struct list wd_entry;    /* entry in the watch descriptor hash */
428     dev_t dev;               /* device number */
429     ino_t ino;               /* device's inode number */
430     int wd;                  /* inotify's watch descriptor */
431     char *name;              /* basename name of the inode */
432 };
433
434 static struct list inode_hash[ HASH_SIZE ];
435 static struct list wd_hash[ HASH_SIZE ];
436
437 static int inotify_add_dir( char *path, unsigned int filter );
438
439 static struct inode *inode_from_wd( int wd )
440 {
441     struct list *bucket = &wd_hash[ wd % HASH_SIZE ];
442     struct inode *inode;
443
444     LIST_FOR_EACH_ENTRY( inode, bucket, struct inode, wd_entry )
445         if (inode->wd == wd)
446             return inode;
447
448     return NULL;
449 }
450
451 static inline struct list *get_hash_list( dev_t dev, ino_t ino )
452 {
453     return &inode_hash[ (ino ^ dev) % HASH_SIZE ];
454 }
455
456 static struct inode *find_inode( dev_t dev, ino_t ino )
457 {
458     struct list *bucket = get_hash_list( dev, ino );
459     struct inode *inode;
460
461     LIST_FOR_EACH_ENTRY( inode, bucket, struct inode, ino_entry )
462         if (inode->ino == ino && inode->dev == dev)
463              return inode;
464
465     return NULL;
466 }
467
468 static struct inode *create_inode( dev_t dev, ino_t ino )
469 {
470     struct inode *inode;
471
472     inode = malloc( sizeof *inode );
473     if (inode)
474     {
475         list_init( &inode->children );
476         list_init( &inode->dirs );
477         inode->ino = ino;
478         inode->dev = dev;
479         inode->wd = -1;
480         inode->parent = NULL;
481         inode->name = NULL;
482         list_add_tail( get_hash_list( dev, ino ), &inode->ino_entry );
483     }
484     return inode;
485 }
486
487 static struct inode *get_inode( dev_t dev, ino_t ino )
488 {
489     struct inode *inode;
490
491     inode = find_inode( dev, ino );
492     if (inode)
493         return inode;
494     return create_inode( dev, ino );
495 }
496
497 static void inode_set_wd( struct inode *inode, int wd )
498 {
499     if (inode->wd != -1)
500         list_remove( &inode->wd_entry );
501     inode->wd = wd;
502     list_add_tail( &wd_hash[ wd % HASH_SIZE ], &inode->wd_entry );
503 }
504
505 static void inode_set_name( struct inode *inode, const char *name )
506 {
507     free (inode->name);
508     inode->name = name ? strdup( name ) : NULL;
509 }
510
511 static void free_inode( struct inode *inode )
512 {
513     int subtree = 0, watches = 0;
514     struct inode *tmp, *next;
515     struct dir *dir;
516
517     LIST_FOR_EACH_ENTRY( dir, &inode->dirs, struct dir, in_entry )
518     {
519         subtree |= dir->subtree;
520         watches++;
521     }
522
523     if (!subtree && !inode->parent)
524     {
525         LIST_FOR_EACH_ENTRY_SAFE( tmp, next, &inode->children,
526                                   struct inode, ch_entry )
527         {
528             assert( tmp != inode );
529             assert( tmp->parent == inode );
530             free_inode( tmp );
531         }
532     }
533
534     if (watches)
535         return;
536
537     if (inode->parent)
538         list_remove( &inode->ch_entry );
539
540     /* disconnect remaining children from the parent */
541     LIST_FOR_EACH_ENTRY_SAFE( tmp, next, &inode->children, struct inode, ch_entry )
542     {
543         list_remove( &tmp->ch_entry );
544         tmp->parent = NULL;
545     }
546
547     if (inode->wd != -1)
548     {
549         inotify_rm_watch( get_unix_fd( inotify_fd ), inode->wd );
550         list_remove( &inode->wd_entry );
551     }
552     list_remove( &inode->ino_entry );
553
554     free( inode->name );
555     free( inode );
556 }
557
558 static struct inode *inode_add( struct inode *parent,
559                                 dev_t dev, ino_t ino, const char *name )
560 {
561     struct inode *inode;
562  
563     inode = get_inode( dev, ino );
564     if (!inode)
565         return NULL;
566  
567     if (!inode->parent)
568     {
569         list_add_tail( &parent->children, &inode->ch_entry );
570         inode->parent = parent;
571         assert( inode != parent );
572     }
573     inode_set_name( inode, name );
574
575     return inode;
576 }
577
578 static struct inode *inode_from_name( struct inode *inode, const char *name )
579 {
580     struct inode *i;
581
582     LIST_FOR_EACH_ENTRY( i, &inode->children, struct inode, ch_entry )
583         if (i->name && !strcmp( i->name, name ))
584             return i;
585     return NULL;
586 }
587
588 static int inotify_get_poll_events( struct fd *fd );
589 static void inotify_poll_event( struct fd *fd, int event );
590
591 static const struct fd_ops inotify_fd_ops =
592 {
593     inotify_get_poll_events,     /* get_poll_events */
594     inotify_poll_event,          /* poll_event */
595     NULL,                        /* flush */
596     NULL,                        /* get_fd_type */
597     NULL,                        /* ioctl */
598     NULL,                        /* queue_async */
599     NULL,                        /* reselect_async */
600     NULL,                        /* cancel_async */
601 };
602
603 static int inotify_get_poll_events( struct fd *fd )
604 {
605     return POLLIN;
606 }
607
608 static void inotify_do_change_notify( struct dir *dir, unsigned int action,
609                                       unsigned int cookie, const char *relpath )
610 {
611     struct change_record *record;
612
613     assert( dir->obj.ops == &dir_ops );
614
615     if (dir->want_data)
616     {
617         size_t len = strlen(relpath);
618         record = malloc( offsetof(struct change_record, event.name[len]) );
619         if (!record)
620             return;
621
622         record->cookie = cookie;
623         record->event.action = action;
624         memcpy( record->event.name, relpath, len );
625         record->event.len = len;
626
627         list_add_tail( &dir->change_records, &record->entry );
628     }
629
630     fd_async_wake_up( dir->fd, ASYNC_TYPE_WAIT, STATUS_ALERTED );
631 }
632
633 static unsigned int filter_from_event( struct inotify_event *ie )
634 {
635     unsigned int filter = 0;
636
637     if (ie->mask & (IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_CREATE))
638         filter |= FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME;
639     if (ie->mask & IN_MODIFY)
640         filter |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
641     if (ie->mask & IN_ATTRIB)
642         filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY;
643     if (ie->mask & IN_ACCESS)
644         filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
645     if (ie->mask & IN_CREATE)
646         filter |= FILE_NOTIFY_CHANGE_CREATION;
647
648     if (ie->mask & IN_ISDIR)
649         filter &= ~FILE_NOTIFY_CHANGE_FILE_NAME;
650     else
651         filter &= ~FILE_NOTIFY_CHANGE_DIR_NAME;
652
653     return filter;
654 }
655
656 /* scan up the parent directories for watches */
657 static unsigned int filter_from_inode( struct inode *inode, int is_parent )
658 {
659     unsigned int filter = 0;
660     struct dir *dir;
661
662     /* combine filters from parents watching subtrees */
663     while (inode)
664     {
665         LIST_FOR_EACH_ENTRY( dir, &inode->dirs, struct dir, in_entry )
666             if (dir->subtree || !is_parent)
667                 filter |= dir->filter;
668         is_parent = 1;
669         inode = inode->parent;
670     }
671
672     return filter;
673 }
674
675 static char *inode_get_path( struct inode *inode, int sz )
676 {
677     struct list *head;
678     char *path;
679     int len;
680
681     if (!inode)
682         return NULL;
683
684     head = list_head( &inode->dirs );
685     if (head)
686     {
687         int unix_fd = get_unix_fd( LIST_ENTRY( head, struct dir, in_entry )->fd );
688         path = malloc ( 32 + sz );
689         if (path)
690             sprintf( path, "/proc/self/fd/%u/", unix_fd );
691         return path;
692     }
693
694     if (!inode->name)
695         return NULL;
696
697     len = strlen( inode->name );
698     path = inode_get_path( inode->parent, sz + len + 1 );
699     if (!path)
700         return NULL;
701     
702     strcat( path, inode->name );
703     strcat( path, "/" );
704
705     return path;
706 }
707
708 static void inode_check_dir( struct inode *parent, const char *name )
709 {
710     char *path;
711     unsigned int filter;
712     struct inode *inode;
713     struct stat st;
714     int wd = -1;
715
716     path = inode_get_path( parent, strlen(name) );
717     if (!path)
718         return;
719
720     strcat( path, name );
721
722     if (stat( path, &st ) < 0)
723         goto end;
724
725     filter = filter_from_inode( parent, 1 );
726     if (!filter)
727         goto end;
728
729     inode = inode_add( parent, st.st_dev, st.st_ino, name );
730     if (!inode || inode->wd != -1)
731         goto end;
732
733     wd = inotify_add_dir( path, filter );
734     if (wd != -1)
735         inode_set_wd( inode, wd );
736     else
737         free_inode( inode );
738
739 end:
740     free( path );
741 }
742
743 static int prepend( char **path, const char *segment )
744 {
745     int extra;
746     char *p;
747
748     extra = strlen( segment ) + 1;
749     if (*path)
750     {
751         int len = strlen( *path ) + 1;
752         p = realloc( *path, len + extra );
753         if (!p) return 0;
754         memmove( &p[ extra ], p, len );
755         p[ extra - 1 ] = '/';
756         memcpy( p, segment, extra - 1 );
757     }
758     else
759     {
760         p = malloc( extra );
761         if (!p) return 0;
762         memcpy( p, segment, extra );
763     }
764
765     *path = p;
766
767     return 1;
768 }
769
770 static void inotify_notify_all( struct inotify_event *ie )
771 {
772     unsigned int filter, action;
773     struct inode *inode, *i;
774     char *path = NULL;
775     struct dir *dir;
776
777     inode = inode_from_wd( ie->wd );
778     if (!inode)
779     {
780         fprintf( stderr, "no inode matches %d\n", ie->wd);
781         return;
782     }
783
784     filter = filter_from_event( ie );
785     
786     if (ie->mask & IN_CREATE)
787     {
788         if (ie->mask & IN_ISDIR)
789             inode_check_dir( inode, ie->name );
790
791         action = FILE_ACTION_ADDED;
792     }
793     else if (ie->mask & IN_DELETE)
794         action = FILE_ACTION_REMOVED;
795     else if (ie->mask & IN_MOVED_FROM)
796         action = FILE_ACTION_RENAMED_OLD_NAME;
797     else if (ie->mask & IN_MOVED_TO)
798         action = FILE_ACTION_RENAMED_NEW_NAME;
799     else
800         action = FILE_ACTION_MODIFIED;
801
802     /*
803      * Work our way up the inode hierarchy
804      *  extending the relative path as we go
805      *  and notifying all recursive watches.
806      */
807     if (!prepend( &path, ie->name ))
808         return;
809
810     for (i = inode; i; i = i->parent)
811     {
812         LIST_FOR_EACH_ENTRY( dir, &i->dirs, struct dir, in_entry )
813             if ((filter & dir->filter) && (i==inode || dir->subtree))
814                 inotify_do_change_notify( dir, action, ie->cookie, path );
815
816         if (!i->name || !prepend( &path, i->name ))
817             break;
818     }
819
820     free( path );
821
822     if (ie->mask & IN_DELETE)
823     {
824         i = inode_from_name( inode, ie->name );
825         if (i)
826             free_inode( i );
827     }
828 }
829
830 static void inotify_poll_event( struct fd *fd, int event )
831 {
832     int r, ofs, unix_fd;
833     char buffer[0x1000];
834     struct inotify_event *ie;
835
836     unix_fd = get_unix_fd( fd );
837     r = read( unix_fd, buffer, sizeof buffer );
838     if (r < 0)
839     {
840         fprintf(stderr,"inotify_poll_event(): inotify read failed!\n");
841         return;
842     }
843
844     for( ofs = 0; ofs < r - offsetof(struct inotify_event, name); )
845     {
846         ie = (struct inotify_event*) &buffer[ofs];
847         if (!ie->len)
848             break;
849         ofs += offsetof( struct inotify_event, name[ie->len] );
850         if (ofs > r) break;
851         inotify_notify_all( ie );
852     }
853 }
854
855 static inline struct fd *create_inotify_fd( void )
856 {
857     int unix_fd;
858
859     unix_fd = inotify_init();
860     if (unix_fd<0)
861         return NULL;
862     return create_anonymous_fd( &inotify_fd_ops, unix_fd, NULL, 0 );
863 }
864
865 static int map_flags( unsigned int filter )
866 {
867     unsigned int mask;
868
869     /* always watch these so we can track subdirectories in recursive watches */
870     mask = (IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF);
871
872     if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
873         mask |= IN_ATTRIB;
874     if (filter & FILE_NOTIFY_CHANGE_SIZE)
875         mask |= IN_MODIFY;
876     if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
877         mask |= IN_MODIFY;
878     if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
879         mask |= IN_ACCESS;
880     if (filter & FILE_NOTIFY_CHANGE_SECURITY)
881         mask |= IN_ATTRIB;
882
883     return mask;
884 }
885
886 static int inotify_add_dir( char *path, unsigned int filter )
887 {
888     int wd = inotify_add_watch( get_unix_fd( inotify_fd ),
889                                 path, map_flags( filter ) );
890     if (wd != -1)
891         set_fd_events( inotify_fd, POLLIN );
892     return wd;
893 }
894
895 static int init_inotify( void )
896 {
897     int i;
898
899     if (inotify_fd)
900         return 1;
901
902     inotify_fd = create_inotify_fd();
903     if (!inotify_fd)
904         return 0;
905
906     for (i=0; i<HASH_SIZE; i++)
907     {
908         list_init( &inode_hash[i] );
909         list_init( &wd_hash[i] );
910     }
911
912     return 1;
913 }
914
915 static int inotify_adjust_changes( struct dir *dir )
916 {
917     unsigned int filter;
918     struct inode *inode;
919     struct stat st;
920     char path[32];
921     int wd, unix_fd;
922
923     if (!inotify_fd)
924         return 0;
925
926     unix_fd = get_unix_fd( dir->fd );
927
928     inode = dir->inode;
929     if (!inode)
930     {
931         /* check if this fd is already being watched */
932         if (-1 == fstat( unix_fd, &st ))
933             return 0;
934
935         inode = get_inode( st.st_dev, st.st_ino );
936         if (!inode)
937             inode = create_inode( st.st_dev, st.st_ino );
938         if (!inode)
939             return 0;
940         list_add_tail( &inode->dirs, &dir->in_entry );
941         dir->inode = inode;
942     }
943
944     filter = filter_from_inode( inode, 0 );
945
946     sprintf( path, "/proc/self/fd/%u", unix_fd );
947     wd = inotify_add_dir( path, filter );
948     if (wd == -1) return 0;
949
950     inode_set_wd( inode, wd );
951
952     return 1;
953 }
954
955 static char *get_basename( const char *link )
956 {
957     char *buffer, *name = NULL;
958     int r, n = 0x100;
959
960     while (1)
961     {
962         buffer = malloc( n );
963         if (!buffer) return NULL;
964
965         r = readlink( link, buffer, n );
966         if (r < 0)
967             break;
968
969         if (r < n)
970         {
971             name = buffer;
972             break;
973         }
974         free( buffer );
975         n *= 2;
976     }
977
978     if (name)
979     {
980         while (r > 0 && name[ r - 1 ] == '/' )
981             r--;
982         name[ r ] = 0;
983
984         name = strrchr( name, '/' );
985         if (name)
986             name = strdup( &name[1] );
987     }
988
989     free( buffer );
990     return name;
991 }
992
993 static int dir_add_to_existing_notify( struct dir *dir )
994 {
995     struct inode *inode, *parent;
996     unsigned int filter = 0;
997     struct stat st, st_new;
998     char link[35], *name;
999     int wd, unix_fd;
1000
1001     if (!inotify_fd)
1002         return 0;
1003
1004     unix_fd = get_unix_fd( dir->fd );
1005
1006     /* check if it's in the list of inodes we want to watch */
1007     if (-1 == fstat( unix_fd, &st_new ))
1008         return 0;
1009     inode = find_inode( st_new.st_dev, st_new.st_ino );
1010     if (inode)
1011         return 0;
1012
1013     /* lookup the parent */
1014     sprintf( link, "/proc/self/fd/%u/..", unix_fd );
1015     if (-1 == stat( link, &st ))
1016         return 0;
1017
1018     /*
1019      * If there's no parent, stop.  We could keep going adding
1020      *  ../ to the path until we hit the root of the tree or
1021      *  find a recursively watched ancestor.
1022      * Assume it's too expensive to search up the tree for now.
1023      */
1024     parent = find_inode( st.st_dev, st.st_ino );
1025     if (!parent)
1026         return 0;
1027
1028     if (parent->wd == -1)
1029         return 0;
1030
1031     filter = filter_from_inode( parent, 1 );
1032     if (!filter)
1033         return 0;
1034
1035     sprintf( link, "/proc/self/fd/%u", unix_fd );
1036     name = get_basename( link );
1037     if (!name)
1038         return 0;
1039     inode = inode_add( parent, st_new.st_dev, st_new.st_ino, name );
1040     free( name );
1041     if (!inode)
1042         return 0;
1043
1044     /* Couldn't find this inode at the start of the function, must be new */
1045     assert( inode->wd == -1 );
1046
1047     wd = inotify_add_dir( link, filter );
1048     if (wd != -1)
1049         inode_set_wd( inode, wd );
1050
1051     return 1;
1052 }
1053
1054 #else
1055
1056 static int init_inotify( void )
1057 {
1058     return 0;
1059 }
1060
1061 static int inotify_adjust_changes( struct dir *dir )
1062 {
1063     return 0;
1064 }
1065
1066 static void free_inode( struct inode *inode )
1067 {
1068     assert( 0 );
1069 }
1070
1071 static int dir_add_to_existing_notify( struct dir *dir )
1072 {
1073     return 0;
1074 }
1075
1076 #endif  /* USE_INOTIFY */
1077
1078 struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode )
1079 {
1080     struct dir *dir;
1081
1082     dir = alloc_object( &dir_ops );
1083     if (!dir)
1084         return NULL;
1085
1086     list_init( &dir->change_records );
1087     dir->filter = 0;
1088     dir->notified = 0;
1089     dir->want_data = 0;
1090     dir->inode = NULL;
1091     grab_object( fd );
1092     dir->fd = fd;
1093     dir->mode = mode;
1094     dir->uid  = ~(uid_t)0;
1095     set_fd_user( fd, &dir_fd_ops, &dir->obj );
1096
1097     dir_add_to_existing_notify( dir );
1098
1099     return &dir->obj;
1100 }
1101
1102 /* enable change notifications for a directory */
1103 DECL_HANDLER(read_directory_changes)
1104 {
1105     struct dir *dir;
1106     struct async *async;
1107
1108     if (!req->filter)
1109     {
1110         set_error(STATUS_INVALID_PARAMETER);
1111         return;
1112     }
1113
1114     dir = get_dir_obj( current->process, req->async.handle, 0 );
1115     if (!dir)
1116         return;
1117
1118     /* requests don't timeout */
1119     if (!(async = fd_queue_async( dir->fd, &req->async, ASYNC_TYPE_WAIT ))) goto end;
1120
1121     /* assign it once */
1122     if (!dir->filter)
1123     {
1124         init_inotify();
1125         insert_change( dir );
1126         dir->filter = req->filter;
1127         dir->subtree = req->subtree;
1128         dir->want_data = req->want_data;
1129     }
1130
1131     /* if there's already a change in the queue, send it */
1132     if (!list_empty( &dir->change_records ))
1133         fd_async_wake_up( dir->fd, ASYNC_TYPE_WAIT, STATUS_ALERTED );
1134
1135     /* setup the real notification */
1136     if (!inotify_adjust_changes( dir ))
1137         dnotify_adjust_changes( dir );
1138
1139     release_object( async );
1140     set_error(STATUS_PENDING);
1141
1142 end:
1143     release_object( dir );
1144 }
1145
1146 DECL_HANDLER(read_change)
1147 {
1148     struct change_record *record, *next;
1149     struct dir *dir;
1150     struct list events;
1151     char *data, *event;
1152     int size = 0;
1153
1154     dir = get_dir_obj( current->process, req->handle, 0 );
1155     if (!dir)
1156         return;
1157
1158     list_init( &events );
1159     list_move_tail( &events, &dir->change_records );
1160     release_object( dir );
1161
1162     if (list_empty( &events ))
1163     {
1164         set_error( STATUS_NO_DATA_DETECTED );
1165         return;
1166     }
1167
1168     LIST_FOR_EACH_ENTRY( record, &events, struct change_record, entry )
1169     {
1170         size += (offsetof(struct filesystem_event, name[record->event.len])
1171                 + sizeof(int)-1) / sizeof(int) * sizeof(int);
1172     }
1173
1174     if (size > get_reply_max_size())
1175         set_error( STATUS_BUFFER_TOO_SMALL );
1176     else if ((data = mem_alloc( size )) != NULL)
1177     {
1178         event = data;
1179         LIST_FOR_EACH_ENTRY( record, &events, struct change_record, entry )
1180         {
1181             data_size_t len = offsetof( struct filesystem_event, name[record->event.len] );
1182
1183             /* FIXME: rename events are sometimes reported as delete/create */
1184             if (record->event.action == FILE_ACTION_RENAMED_OLD_NAME)
1185             {
1186                 struct list *elem = list_next( &events, &record->entry );
1187                 if (elem)
1188                     next = LIST_ENTRY(elem, struct change_record, entry);
1189
1190                 if (elem && next->cookie == record->cookie)
1191                     next->cookie = 0;
1192                 else
1193                     record->event.action = FILE_ACTION_REMOVED;
1194             }
1195             else if (record->event.action == FILE_ACTION_RENAMED_NEW_NAME && record->cookie)
1196                 record->event.action = FILE_ACTION_ADDED;
1197
1198             memcpy( event, &record->event, len );
1199             event += len;
1200             if (len % sizeof(int))
1201             {
1202                 memset( event, 0, sizeof(int) - len % sizeof(int) );
1203                 event += sizeof(int) - len % sizeof(int);
1204             }
1205         }
1206         set_reply_data_ptr( data, size );
1207     }
1208
1209     LIST_FOR_EACH_ENTRY_SAFE( record, next, &events, struct change_record, entry )
1210     {
1211         list_remove( &record->entry );
1212         free( record );
1213     }
1214 }