3 Unix SMB/Netbios implementation.
6 Copyright (C) Jeremy Allison 1994-1998
7 Copyright (C) Andrew Tridgell 2000
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 extern int DEBUGLEVEL;
28 /****************************************************************************
29 This is the structure to keep the information needed to
30 determine if a directory has changed.
31 *****************************************************************************/
34 time_t modify_time; /* Info from the directory we're monitoring. */
35 time_t status_time; /* Info from the directory we're monitoring. */
36 time_t total_time; /* Total time of all directory entries - don't care if it wraps. */
37 unsigned int num_entries; /* Zero or the number of files in the directory. */
40 /****************************************************************************
41 This is the structure to queue to implement NT change
42 notify. It consists of smb_size bytes stored from the
43 transact command (to keep the mid, tid etc around).
44 Plus the fid to examine and the time to check next.
45 *****************************************************************************/
50 connection_struct *conn;
52 time_t next_check_time;
53 change_hash_data change_data;
54 char request_buf[smb_size];
57 static ubi_slList change_notify_queue = { NULL, (ubi_slNodePtr)&change_notify_queue, 0};
59 /****************************************************************************
60 Setup the common parts of the return packet and send it.
61 *****************************************************************************/
63 static void change_notify_reply_packet(char *inbuf, int error_class, uint32 error_code)
65 char outbuf[smb_size+38];
67 memset(outbuf, '\0', sizeof(outbuf));
68 construct_reply_common(inbuf, outbuf);
71 * If we're returning a 'too much in the directory changed' we need to
72 * set this is an NT error status flags. If we don't then the (probably
73 * untested) code in the NT redirector has a bug in that it doesn't re-issue
74 * the change notify.... Ah - I *love* it when I get so deeply into this I
75 * can even determine how MS failed to test stuff and why.... :-). JRA.
78 if(error_class == 0) /* NT Error. */
79 SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
81 ERROR(error_class,error_code);
84 * Seems NT needs a transact command with an error code
85 * in it. This is a longer packet than a simple error.
87 set_message(outbuf,18,0,False);
89 send_smb(smbd_server_fd(),outbuf);
92 /****************************************************************************
93 Create the hash we will use to determine if the contents changed.
94 *****************************************************************************/
96 static BOOL create_directory_notify_hash( change_notify_buf *cnbp, change_hash_data *change_data)
99 files_struct *fsp = cnbp->fsp;
101 memset((char *)change_data, '\0', sizeof(change_data));
104 * Store the current timestamp on the directory we are monitoring.
107 if(dos_stat(fsp->fsp_name, &st) < 0) {
108 DEBUG(0,("create_directory_notify_hash: Unable to stat name = %s. \
109 Error was %s\n", fsp->fsp_name, strerror(errno) ));
113 change_data->modify_time = st.st_mtime;
114 change_data->status_time = st.st_ctime;
117 * If we are to watch for changes that are only stored
118 * in inodes of files, not in the directory inode, we must
119 * scan the directory and produce a unique identifier with
120 * which we can determine if anything changed. We use the
121 * modify and change times from all the files in the
122 * directory, added together (ignoring wrapping if it's
123 * larger than the max time_t value).
126 if(cnbp->flags & (FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE)) {
130 size_t remaining_len;
132 void *dp = OpenDir(cnbp->conn, fsp->fsp_name, True);
135 DEBUG(0,("create_directory_notify_hash: Unable to open directory = %s. \
136 Error was %s\n", fsp->fsp_name, strerror(errno) ));
140 change_data->num_entries = 0;
142 pstrcpy(full_name, fsp->fsp_name);
143 pstrcat(full_name, "/");
145 fullname_len = strlen(full_name);
146 remaining_len = sizeof(full_name) - fullname_len - 1;
147 p = &full_name[fullname_len];
149 while ((fname = ReadDirName(dp))) {
150 if(strequal(fname, ".") || strequal(fname, ".."))
153 change_data->num_entries++;
154 safe_strcpy( p, fname, remaining_len);
156 memset(&st, '\0', sizeof(st));
159 * Do the stat - but ignore errors.
162 if(dos_stat(full_name, &st) < 0) {
163 DEBUG(5,("create_directory_notify_hash: Unable to stat content file = %s. \
164 Error was %s\n", fsp->fsp_name, strerror(errno) ));
166 change_data->total_time += (st.st_mtime + st.st_ctime);
175 /****************************************************************************
176 Delete entries by fnum from the change notify pending queue.
177 *****************************************************************************/
179 void remove_pending_change_notify_requests_by_fid(files_struct *fsp)
181 change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
182 change_notify_buf *prev = NULL;
184 while(cnbp != NULL) {
185 if(cnbp->fsp->fnum == fsp->fnum) {
186 free((char *)ubi_slRemNext( &change_notify_queue, prev));
187 cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
192 cnbp = (change_notify_buf *)ubi_slNext(cnbp);
196 /****************************************************************************
197 Delete entries by mid from the change notify pending queue. Always send reply.
198 *****************************************************************************/
200 void remove_pending_change_notify_requests_by_mid(int mid)
202 change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
203 change_notify_buf *prev = NULL;
205 while(cnbp != NULL) {
206 if(SVAL(cnbp->request_buf,smb_mid) == mid) {
207 change_notify_reply_packet(cnbp->request_buf,0,0xC0000000 |NT_STATUS_CANCELLED);
208 free((char *)ubi_slRemNext( &change_notify_queue, prev));
209 cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
214 cnbp = (change_notify_buf *)ubi_slNext(cnbp);
218 /****************************************************************************
219 Delete entries by filename and cnum from the change notify pending queue.
221 *****************************************************************************/
223 void remove_pending_change_notify_requests_by_filename(files_struct *fsp)
225 change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
226 change_notify_buf *prev = NULL;
228 while(cnbp != NULL) {
230 * We know it refers to the same directory if the connection number and
231 * the filename are identical.
233 if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) {
234 change_notify_reply_packet(cnbp->request_buf,0,0xC0000000 |NT_STATUS_CANCELLED);
235 free((char *)ubi_slRemNext( &change_notify_queue, prev));
236 cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
241 cnbp = (change_notify_buf *)ubi_slNext(cnbp);
245 /****************************************************************************
246 Process the change notify queue. Note that this is only called as root.
247 Returns True if there are still outstanding change notify requests on the
249 *****************************************************************************/
251 BOOL process_pending_change_notify_queue(time_t t)
253 change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
254 change_notify_buf *prev = NULL;
259 if(cnbp->next_check_time >= t)
263 * It's time to check. Go through the queue and see if
264 * the timestamps changed.
267 while((cnbp != NULL) && (cnbp->next_check_time <= t)) {
268 change_hash_data change_data;
269 connection_struct *conn = cnbp->conn;
270 uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
271 SVAL(cnbp->request_buf,smb_uid);
273 ZERO_STRUCT(change_data);
276 * Ensure we don't have any old chain_fsp values
282 if(!become_user(conn,vuid)) {
283 DEBUG(0,("process_pending_change_notify_queue: Unable to become user vuid=%d.\n",
286 * Remove the entry and return an error to the client.
288 change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
289 free((char *)ubi_slRemNext( &change_notify_queue, prev));
290 cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
294 if(!become_service(conn,True)) {
295 DEBUG(0,("process_pending_change_notify_queue: Unable to become service Error was %s.\n", strerror(errno) ));
297 * Remove the entry and return an error to the client.
299 change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
300 free((char *)ubi_slRemNext( &change_notify_queue, prev));
301 cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
306 if(!create_directory_notify_hash( cnbp, &change_data)) {
307 DEBUG(0,("process_pending_change_notify_queue: Unable to create change data for \
308 directory %s\n", cnbp->fsp->fsp_name ));
310 * Remove the entry and return an error to the client.
312 change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
313 free((char *)ubi_slRemNext( &change_notify_queue, prev));
314 cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
319 if(memcmp( (char *)&cnbp->change_data, (char *)&change_data, sizeof(change_data))) {
321 * Remove the entry and return a change notify to the client.
323 DEBUG(5,("process_pending_change_notify_queue: directory name = %s changed.\n",
324 cnbp->fsp->fsp_name ));
325 change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
326 free((char *)ubi_slRemNext( &change_notify_queue, prev));
327 cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
335 * Move to the next in the list.
338 cnbp = (change_notify_buf *)ubi_slNext(cnbp);
341 return (cnbp != NULL);
344 /****************************************************************************
345 Return true if there are pending change notifies.
346 ****************************************************************************/
347 BOOL change_notifies_pending(void)
349 change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
350 return (cnbp != NULL);
353 /****************************************************************************
354 * Now queue an entry on the notify change stack. We timestamp
355 * the entry we are adding so that we know when to scan next.
356 * We only need to save smb_size bytes from this incoming packet
357 * as we will always by returning a 'read the directory yourself'
359 ****************************************************************************/
360 BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn, uint32 flags)
362 change_notify_buf *cnbp;
364 if((cnbp = (change_notify_buf *)malloc(sizeof(change_notify_buf))) == NULL) {
365 DEBUG(0,("call_nt_transact_notify_change: malloc fail !\n" ));
371 memcpy(cnbp->request_buf, inbuf, smb_size);
374 cnbp->next_check_time = time(NULL) + lp_change_notify_timeout();
377 if (!create_directory_notify_hash(cnbp, &cnbp->change_data)) {
383 * Adding to the tail enables us to check only
384 * the head when scanning for change, as this entry
385 * is forced to have the first timeout expiration.
388 ubi_slAddTail(&change_notify_queue, cnbp);