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