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

Wine Cross Reference
wine/server/change.c

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

~ [ 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.