Make sure we don't clobber the stack when response consists of the empty
[Samba/gebeck_regimport.git] / source3 / smbd / notify_hash.c
blobec414454f9eac09acb168229e24301bcdae9b68c
1 /*
2 Unix SMB/CIFS implementation.
3 change notify handling - hash based implementation
4 Copyright (C) Jeremy Allison 1994-1998
5 Copyright (C) Andrew Tridgell 2000
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program 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
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "includes.h"
24 struct change_data {
25 time_t last_check_time; /* time we last checked this entry */
26 time_t modify_time; /* Info from the directory we're monitoring. */
27 time_t status_time; /* Info from the directory we're monitoring. */
28 time_t total_time; /* Total time of all directory entries - don't care if it wraps. */
29 unsigned int num_entries; /* Zero or the number of files in the directory. */
30 unsigned int mode_sum;
31 unsigned char name_hash[16];
34 /****************************************************************************
35 Create the hash we will use to determine if the contents changed.
36 *****************************************************************************/
38 static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
39 struct change_data *data, struct change_data *old_data)
41 SMB_STRUCT_STAT st;
42 pstring full_name;
43 char *p;
44 const char *fname;
45 size_t remaining_len;
46 size_t fullname_len;
47 void *dp;
49 ZERO_STRUCTP(data);
51 if(SMB_VFS_STAT(conn,path, &st) == -1)
52 return False;
54 data->modify_time = st.st_mtime;
55 data->status_time = st.st_ctime;
57 if (old_data) {
59 * Shortcut to avoid directory scan if the time
60 * has changed - we always must return true then.
62 if (old_data->modify_time != data->modify_time ||
63 old_data->status_time != data->status_time ) {
64 return True;
69 * If we are to watch for changes that are only stored
70 * in inodes of files, not in the directory inode, we must
71 * scan the directory and produce a unique identifier with
72 * which we can determine if anything changed. We use the
73 * modify and change times from all the files in the
74 * directory, added together (ignoring wrapping if it's
75 * larger than the max time_t value).
78 dp = OpenDir(conn, path, True);
79 if (dp == NULL)
80 return False;
82 data->num_entries = 0;
84 pstrcpy(full_name, path);
85 pstrcat(full_name, "/");
87 fullname_len = strlen(full_name);
88 remaining_len = sizeof(full_name) - fullname_len - 1;
89 p = &full_name[fullname_len];
91 while ((fname = ReadDirName(dp))) {
92 if(strequal(fname, ".") || strequal(fname, ".."))
93 continue;
95 data->num_entries++;
96 safe_strcpy(p, fname, remaining_len);
98 ZERO_STRUCT(st);
101 * Do the stat - but ignore errors.
103 SMB_VFS_STAT(conn,full_name, &st);
106 * Always sum the times.
109 data->total_time += (st.st_mtime + st.st_ctime);
112 * If requested hash the names.
115 if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_FILE)) {
116 int i;
117 unsigned char tmp_hash[16];
118 mdfour(tmp_hash, (const unsigned char *)fname, strlen(fname));
119 for (i=0;i<16;i++)
120 data->name_hash[i] ^= tmp_hash[i];
124 * If requested sum the mode_t's.
127 if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SECURITY))
128 data->mode_sum += st.st_mode;
131 CloseDir(dp);
133 return True;
136 /****************************************************************************
137 Register a change notify request.
138 *****************************************************************************/
140 static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags)
142 struct change_data data;
144 if (!notify_hash(conn, path, flags, &data, NULL))
145 return NULL;
147 data.last_check_time = time(NULL);
149 return (void *)memdup(&data, sizeof(data));
152 /****************************************************************************
153 Check if a change notify should be issued.
154 A time of zero means instantaneous check - don't modify the last check time.
155 *****************************************************************************/
157 static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
159 struct change_data *data = (struct change_data *)datap;
160 struct change_data data2;
162 if (t && t < data->last_check_time + lp_change_notify_timeout())
163 return False;
165 if (!change_to_user(conn,vuid))
166 return True;
167 if (!set_current_service(conn,True)) {
168 change_to_root_user();
169 return True;
172 if (!notify_hash(conn, path, flags, &data2, data) ||
173 data2.modify_time != data->modify_time ||
174 data2.status_time != data->status_time ||
175 data2.total_time != data->total_time ||
176 data2.num_entries != data->num_entries ||
177 data2.mode_sum != data->mode_sum ||
178 memcmp(data2.name_hash, data->name_hash, sizeof(data2.name_hash))) {
179 change_to_root_user();
180 return True;
183 if (t)
184 data->last_check_time = t;
186 change_to_root_user();
188 return False;
191 /****************************************************************************
192 Remove a change notify data structure.
193 *****************************************************************************/
195 static void hash_remove_notify(void *datap)
197 free(datap);
200 /****************************************************************************
201 Setup hash based change notify.
202 ****************************************************************************/
204 struct cnotify_fns *hash_notify_init(void)
206 static struct cnotify_fns cnotify;
208 cnotify.register_notify = hash_register_notify;
209 cnotify.check_notify = hash_check_notify;
210 cnotify.remove_notify = hash_remove_notify;
211 cnotify.select_time = lp_change_notify_timeout();
213 return &cnotify;
217 change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
218 change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
220 chain_size = 0;
221 file_chain_reset();
223 uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
224 SVAL(cnbp->request_buf,smb_uid);