r20837: Use real type name, to fix compilation with -WC++-compat
[Samba.git] / source / smbd / notify_hash.c
blob0787a3eec5e2a7e68acf0bcb4aa2f8077bbf5869
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 struct timespec modify_time; /* Info from the directory we're monitoring. */
27 struct timespec 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];
35 /* Compare struct timespec. */
36 #define TIMESTAMP_NEQ(x, y) (((x).tv_sec != (y).tv_sec) || ((x).tv_nsec != (y).tv_nsec))
38 /****************************************************************************
39 Create the hash we will use to determine if the contents changed.
40 *****************************************************************************/
42 static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
43 struct change_data *data, struct change_data *old_data)
45 SMB_STRUCT_STAT st;
46 pstring full_name;
47 char *p;
48 const char *fname;
49 size_t remaining_len;
50 size_t fullname_len;
51 struct smb_Dir *dp;
52 long offset;
54 ZERO_STRUCTP(data);
56 if(SMB_VFS_STAT(conn,path, &st) == -1)
57 return False;
59 data->modify_time = get_mtimespec(&st);
60 data->status_time = get_ctimespec(&st);
62 if (old_data) {
64 * Shortcut to avoid directory scan if the time
65 * has changed - we always must return true then.
67 if (TIMESTAMP_NEQ(old_data->modify_time, data->modify_time) ||
68 TIMESTAMP_NEQ(old_data->status_time, data->status_time) ) {
69 return True;
73 if (S_ISDIR(st.st_mode) &&
74 (flags & ~(FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME)) == 0)
76 /* This is the case of a client wanting to know only when
77 * the contents of a directory changes. Since any file
78 * creation, rename or deletion will update the directory
79 * timestamps, we don't need to create a hash.
81 return True;
84 if (lp_change_notify_timeout(SNUM(conn)) <= 0) {
85 /* It change notify timeout has been disabled, never scan the directory. */
86 return True;
90 * If we are to watch for changes that are only stored
91 * in inodes of files, not in the directory inode, we must
92 * scan the directory and produce a unique identifier with
93 * which we can determine if anything changed. We use the
94 * modify and change times from all the files in the
95 * directory, added together (ignoring wrapping if it's
96 * larger than the max time_t value).
99 dp = OpenDir(conn, path, NULL, 0);
100 if (dp == NULL)
101 return False;
103 data->num_entries = 0;
105 pstrcpy(full_name, path);
106 pstrcat(full_name, "/");
108 fullname_len = strlen(full_name);
109 remaining_len = sizeof(full_name) - fullname_len - 1;
110 p = &full_name[fullname_len];
112 offset = 0;
113 while ((fname = ReadDirName(dp, &offset))) {
114 SET_STAT_INVALID(st);
115 if(strequal(fname, ".") || strequal(fname, ".."))
116 continue;
118 if (!is_visible_file(conn, path, fname, &st, True))
119 continue;
121 data->num_entries++;
122 safe_strcpy(p, fname, remaining_len);
125 * Do the stat - but ignore errors.
127 if (!VALID_STAT(st)) {
128 SMB_VFS_STAT(conn,full_name, &st);
132 * Always sum the times.
135 data->total_time += (st.st_mtime + st.st_ctime);
138 * If requested hash the names.
141 if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_FILE)) {
142 int i;
143 unsigned char tmp_hash[16];
144 mdfour(tmp_hash, (const unsigned char *)fname, strlen(fname));
145 for (i=0;i<16;i++)
146 data->name_hash[i] ^= tmp_hash[i];
150 * If requested sum the mode_t's.
153 if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SECURITY))
154 data->mode_sum += st.st_mode;
157 CloseDir(dp);
159 return True;
162 /****************************************************************************
163 Register a change notify request.
164 *****************************************************************************/
166 static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags)
168 struct change_data data;
170 if (!notify_hash(conn, path, flags, &data, NULL))
171 return NULL;
173 data.last_check_time = time(NULL);
175 return (void *)memdup(&data, sizeof(data));
178 /****************************************************************************
179 Check if a change notify should be issued.
180 A time of zero means instantaneous check - don't modify the last check time.
181 *****************************************************************************/
183 static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
185 struct change_data *data = (struct change_data *)datap;
186 struct change_data data2;
187 int cnto = lp_change_notify_timeout(SNUM(conn));
189 if (t && cnto <= 0) {
190 /* Change notify turned off on this share.
191 * Only scan when (t==0) - we think something changed. */
192 return False;
195 if (t && t < data->last_check_time + cnto) {
196 return False;
199 if (!change_to_user(conn,vuid))
200 return True;
201 if (!set_current_service(conn,FLAG_CASELESS_PATHNAMES,True)) {
202 change_to_root_user();
203 return True;
206 if (!notify_hash(conn, path, flags, &data2, data) ||
207 TIMESTAMP_NEQ(data2.modify_time, data->modify_time) ||
208 TIMESTAMP_NEQ(data2.status_time, data->status_time) ||
209 data2.total_time != data->total_time ||
210 data2.num_entries != data->num_entries ||
211 data2.mode_sum != data->mode_sum ||
212 memcmp(data2.name_hash, data->name_hash, sizeof(data2.name_hash))) {
213 change_to_root_user();
214 return True;
217 if (t) {
218 data->last_check_time = t;
221 change_to_root_user();
223 return False;
226 /****************************************************************************
227 Remove a change notify data structure.
228 *****************************************************************************/
230 static void hash_remove_notify(void *datap)
232 free(datap);
235 /****************************************************************************
236 Setup hash based change notify.
237 ****************************************************************************/
239 struct cnotify_fns *hash_notify_init(void)
241 static struct cnotify_fns cnotify;
243 cnotify.register_notify = hash_register_notify;
244 cnotify.check_notify = hash_check_notify;
245 cnotify.remove_notify = hash_remove_notify;
246 cnotify.select_time = 60; /* Start with 1 minute default. */
247 cnotify.notification_fd = -1;
249 return &cnotify;
253 change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
254 change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
256 chain_size = 0;
257 file_chain_reset();
259 uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
260 SVAL(cnbp->request_buf,smb_uid);