2 * Server-side change notification management
4 * Copyright (C) 1998 Alexandre Julliard
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.
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.
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
22 #include "wine/port.h"
32 #define WIN32_NO_STATUS
44 #define DN_ACCESS 0x00000001 /* File accessed */
45 #define DN_MODIFY 0x00000002 /* File modified */
46 #define DN_CREATE 0x00000004 /* File created */
47 #define DN_DELETE 0x00000008 /* File removed */
48 #define DN_RENAME 0x00000010 /* File renamed */
49 #define DN_ATTRIB 0x00000020 /* File changed attibutes */
50 #define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
56 struct object obj
; /* object header */
57 struct fd
*fd
; /* file descriptor to the directory */
58 struct list entry
; /* entry in global change notifications list */
59 int subtree
; /* watch all the subtree */
60 unsigned int filter
; /* notification filter */
61 int notified
; /* SIGIO counter */
62 long signaled
; /* the file changed */
65 static void change_dump( struct object
*obj
, int verbose
);
66 static int change_signaled( struct object
*obj
, struct thread
*thread
);
67 static void change_destroy( struct object
*obj
);
69 static const struct object_ops change_ops
=
71 sizeof(struct change
), /* size */
72 change_dump
, /* dump */
73 add_queue
, /* add_queue */
74 remove_queue
, /* remove_queue */
75 change_signaled
, /* signaled */
76 no_satisfied
, /* satisfied */
77 no_signal
, /* signal */
78 no_get_fd
, /* get_fd */
79 no_map_access
, /* map_access */
80 no_lookup_name
, /* lookup_name */
81 no_close_handle
, /* close_handle */
82 change_destroy
/* destroy */
85 static struct list change_list
= LIST_INIT(change_list
);
87 static void adjust_changes( int fd
, unsigned int filter
)
89 #if defined(F_SETSIG) && defined(F_NOTIFY)
91 if ( 0 > fcntl( fd
, F_SETSIG
, SIGIO
) )
95 if (filter
& FILE_NOTIFY_CHANGE_FILE_NAME
)
96 val
|= DN_RENAME
| DN_DELETE
| DN_CREATE
;
97 if (filter
& FILE_NOTIFY_CHANGE_DIR_NAME
)
98 val
|= DN_RENAME
| DN_DELETE
| DN_CREATE
;
99 if (filter
& FILE_NOTIFY_CHANGE_ATTRIBUTES
)
101 if (filter
& FILE_NOTIFY_CHANGE_SIZE
)
103 if (filter
& FILE_NOTIFY_CHANGE_LAST_WRITE
)
105 if (filter
& FILE_NOTIFY_CHANGE_LAST_ACCESS
)
107 if (filter
& FILE_NOTIFY_CHANGE_CREATION
)
109 if (filter
& FILE_NOTIFY_CHANGE_SECURITY
)
111 fcntl( fd
, F_NOTIFY
, val
);
115 /* insert change in the global list */
116 static inline void insert_change( struct change
*change
)
120 sigemptyset( &sigset
);
121 sigaddset( &sigset
, SIGIO
);
122 sigprocmask( SIG_BLOCK
, &sigset
, NULL
);
123 list_add_head( &change_list
, &change
->entry
);
124 sigprocmask( SIG_UNBLOCK
, &sigset
, NULL
);
127 /* remove change from the global list */
128 static inline void remove_change( struct change
*change
)
132 sigemptyset( &sigset
);
133 sigaddset( &sigset
, SIGIO
);
134 sigprocmask( SIG_BLOCK
, &sigset
, NULL
);
135 list_remove( &change
->entry
);
136 sigprocmask( SIG_UNBLOCK
, &sigset
, NULL
);
139 static struct change
*create_change_notification( struct fd
*fd
, int subtree
, unsigned int filter
)
141 struct change
*change
;
143 int unix_fd
= get_unix_fd( fd
);
145 if (unix_fd
== -1) return NULL
;
147 if (fstat( unix_fd
, &st
) == -1 || !S_ISDIR(st
.st_mode
))
149 set_error( STATUS_NOT_A_DIRECTORY
);
153 if ((change
= alloc_object( &change_ops
)))
155 change
->fd
= (struct fd
*)grab_object( fd
);
156 change
->subtree
= subtree
;
157 change
->filter
= filter
;
158 change
->notified
= 0;
159 change
->signaled
= 0;
160 insert_change( change
);
161 adjust_changes( unix_fd
, filter
);
166 static void change_dump( struct object
*obj
, int verbose
)
168 struct change
*change
= (struct change
*)obj
;
169 assert( obj
->ops
== &change_ops
);
170 fprintf( stderr
, "Change notification fd=%p sub=%d filter=%08x\n",
171 change
->fd
, change
->subtree
, change
->filter
);
174 static int change_signaled( struct object
*obj
, struct thread
*thread
)
176 struct change
*change
= (struct change
*)obj
;
178 return change
->signaled
!= 0;
181 static void change_destroy( struct object
*obj
)
183 struct change
*change
= (struct change
*)obj
;
185 release_object( change
->fd
);
186 remove_change( change
);
189 /* enter here directly from SIGIO signal handler */
190 void do_change_notify( int unix_fd
)
194 /* FIXME: this is O(n) ... probably can be improved */
195 LIST_FOR_EACH( ptr
, &change_list
)
197 struct change
*change
= LIST_ENTRY( ptr
, struct change
, entry
);
198 if (get_unix_fd( change
->fd
) != unix_fd
) continue;
199 interlocked_xchg_add( &change
->notified
, 1 );
204 /* SIGIO callback, called synchronously with the poll loop */
205 void sigio_callback(void)
209 LIST_FOR_EACH( ptr
, &change_list
)
211 struct change
*change
= LIST_ENTRY( ptr
, struct change
, entry
);
212 long count
= interlocked_xchg( &change
->notified
, 0 );
215 change
->signaled
+= count
;
216 if (change
->signaled
== count
) /* was it 0? */
217 wake_up( &change
->obj
, 0 );
222 /* create a change notification */
223 DECL_HANDLER(create_change_notification
)
225 struct change
*change
;
229 if (!(file
= get_file_obj( current
->process
, req
->handle
, 0 ))) return;
230 fd
= get_obj_fd( (struct object
*)file
);
231 release_object( file
);
234 if ((change
= create_change_notification( fd
, req
->subtree
, req
->filter
)))
236 reply
->handle
= alloc_handle( current
->process
, change
, req
->access
, req
->attributes
);
237 release_object( change
);
239 release_object( fd
);
242 /* move to the next change notification */
243 DECL_HANDLER(next_change_notification
)
245 struct change
*change
;
247 if ((change
= (struct change
*)get_handle_obj( current
->process
, req
->handle
,
250 if (change
->signaled
> 0) change
->signaled
--;
251 release_object( change
);