change.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. * Server-side change notification management
  3. *
  4. * Copyright (C) 1998 Alexandre Julliard
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. */
  20. #include "config.h"
  21. #include "wine/port.h"
  22. #include <assert.h>
  23. #include <fcntl.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <signal.h>
  27. #include <sys/stat.h>
  28. #include "windef.h"
  29. #include "file.h"
  30. #include "handle.h"
  31. #include "thread.h"
  32. #include "request.h"
  33. #ifdef linux
  34. #ifndef F_NOTIFY
  35. #define F_NOTIFY 1026
  36. #define DN_ACCESS 0x00000001 /* File accessed */
  37. #define DN_MODIFY 0x00000002 /* File modified */
  38. #define DN_CREATE 0x00000004 /* File created */
  39. #define DN_DELETE 0x00000008 /* File removed */
  40. #define DN_RENAME 0x00000010 /* File renamed */
  41. #define DN_ATTRIB 0x00000020 /* File changed attibutes */
  42. #define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
  43. #endif
  44. #endif
  45. struct change
  46. {
  47. struct object obj; /* object header */
  48. struct fd *fd; /* file descriptor to the directory */
  49. struct list entry; /* entry in global change notifications list */
  50. int subtree; /* watch all the subtree */
  51. unsigned int filter; /* notification filter */
  52. long notified; /* SIGIO counter */
  53. long signaled; /* the file changed */
  54. };
  55. static void change_dump( struct object *obj, int verbose );
  56. static int change_signaled( struct object *obj, struct thread *thread );
  57. static void change_destroy( struct object *obj );
  58. static const struct object_ops change_ops =
  59. {
  60. sizeof(struct change), /* size */
  61. change_dump, /* dump */
  62. add_queue, /* add_queue */
  63. remove_queue, /* remove_queue */
  64. change_signaled, /* signaled */
  65. no_satisfied, /* satisfied */
  66. no_get_fd, /* get_fd */
  67. change_destroy /* destroy */
  68. };
  69. static struct list change_list = LIST_INIT(change_list);
  70. static void adjust_changes( int fd, unsigned int filter )
  71. {
  72. #if defined(F_SETSIG) && defined(F_NOTIFY)
  73. unsigned int val;
  74. if ( 0 > fcntl( fd, F_SETSIG, SIGIO) )
  75. return;
  76. val = DN_MULTISHOT;
  77. if( filter & FILE_NOTIFY_CHANGE_FILE_NAME )
  78. val |= DN_RENAME | DN_DELETE | DN_CREATE;
  79. if( filter & FILE_NOTIFY_CHANGE_DIR_NAME )
  80. val |= DN_RENAME | DN_DELETE | DN_CREATE;
  81. if( filter & FILE_NOTIFY_CHANGE_ATTRIBUTES )
  82. val |= DN_ATTRIB;
  83. if( filter & FILE_NOTIFY_CHANGE_SIZE )
  84. val |= DN_MODIFY;
  85. if( filter & FILE_NOTIFY_CHANGE_LAST_WRITE )
  86. val |= DN_MODIFY;
  87. if( filter & FILE_NOTIFY_CHANGE_LAST_ACCESS )
  88. val |= DN_ACCESS;
  89. if( filter & FILE_NOTIFY_CHANGE_CREATION )
  90. val |= DN_CREATE;
  91. if( filter & FILE_NOTIFY_CHANGE_SECURITY )
  92. val |= DN_ATTRIB;
  93. fcntl( fd, F_NOTIFY, val );
  94. #endif
  95. }
  96. /* insert change in the global list */
  97. static inline void insert_change( struct change *change )
  98. {
  99. sigset_t sigset;
  100. sigemptyset( &sigset );
  101. sigaddset( &sigset, SIGIO );
  102. sigprocmask( SIG_BLOCK, &sigset, NULL );
  103. list_add_head( &change_list, &change->entry );
  104. sigprocmask( SIG_UNBLOCK, &sigset, NULL );
  105. }
  106. /* remove change from the global list */
  107. static inline void remove_change( struct change *change )
  108. {
  109. sigset_t sigset;
  110. sigemptyset( &sigset );
  111. sigaddset( &sigset, SIGIO );
  112. sigprocmask( SIG_BLOCK, &sigset, NULL );
  113. list_remove( &change->entry );
  114. sigprocmask( SIG_UNBLOCK, &sigset, NULL );
  115. }
  116. static struct change *create_change_notification( struct fd *fd, int subtree, unsigned int filter )
  117. {
  118. struct change *change;
  119. struct stat st;
  120. int unix_fd = get_unix_fd( fd );
  121. if (fstat( unix_fd, &st ) == -1 || !S_ISDIR(st.st_mode))
  122. {
  123. set_error( STATUS_NOT_A_DIRECTORY );
  124. return NULL;
  125. }
  126. if ((change = alloc_object( &change_ops )))
  127. {
  128. change->fd = (struct fd *)grab_object( fd );
  129. change->subtree = subtree;
  130. change->filter = filter;
  131. change->notified = 0;
  132. change->signaled = 0;
  133. insert_change( change );
  134. adjust_changes( unix_fd, filter );
  135. }
  136. return change;
  137. }
  138. static void change_dump( struct object *obj, int verbose )
  139. {
  140. struct change *change = (struct change *)obj;
  141. assert( obj->ops == &change_ops );
  142. fprintf( stderr, "Change notification fd=%p sub=%d filter=%08x\n",
  143. change->fd, change->subtree, change->filter );
  144. }
  145. static int change_signaled( struct object *obj, struct thread *thread )
  146. {
  147. struct change *change = (struct change *)obj;
  148. return change->signaled != 0;
  149. }
  150. static void change_destroy( struct object *obj )
  151. {
  152. struct change *change = (struct change *)obj;
  153. release_object( change->fd );
  154. remove_change( change );
  155. }
  156. /* enter here directly from SIGIO signal handler */
  157. void do_change_notify( int unix_fd )
  158. {
  159. struct list *ptr;
  160. /* FIXME: this is O(n) ... probably can be improved */
  161. LIST_FOR_EACH( ptr, &change_list )
  162. {
  163. struct change *change = LIST_ENTRY( ptr, struct change, entry );
  164. if (get_unix_fd( change->fd ) != unix_fd) continue;
  165. interlocked_xchg_add( &change->notified, 1 );
  166. break;
  167. }
  168. }
  169. /* SIGIO callback, called synchronously with the poll loop */
  170. void sigio_callback(void)
  171. {
  172. struct list *ptr;
  173. LIST_FOR_EACH( ptr, &change_list )
  174. {
  175. struct change *change = LIST_ENTRY( ptr, struct change, entry );
  176. long count = interlocked_xchg( &change->notified, 0 );
  177. if (count)
  178. {
  179. change->signaled += count;
  180. if (change->signaled == count) /* was it 0? */
  181. wake_up( &change->obj, 0 );
  182. }
  183. }
  184. }
  185. /* create a change notification */
  186. DECL_HANDLER(create_change_notification)
  187. {
  188. struct change *change;
  189. struct file *file;
  190. struct fd *fd;
  191. if (!(file = get_file_obj( current->process, req->handle, 0 ))) return;
  192. fd = get_obj_fd( (struct object *)file );
  193. release_object( file );
  194. if (!fd) return;
  195. if ((change = create_change_notification( fd, req->subtree, req->filter )))
  196. {
  197. reply->handle = alloc_handle( current->process, change,
  198. STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE, 0 );
  199. release_object( change );
  200. }
  201. release_object( fd );
  202. }
  203. /* move to the next change notification */
  204. DECL_HANDLER(next_change_notification)
  205. {
  206. struct change *change;
  207. if ((change = (struct change *)get_handle_obj( current->process, req->handle,
  208. 0, &change_ops )))
  209. {
  210. if (change->signaled > 0) change->signaled--;
  211. release_object( change );
  212. }
  213. }