~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/server/change.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.