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
6
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.
11
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.
16
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.
20*/
21
22#include "includes.h"
23
24struct 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];
32};
33
34/****************************************************************************
35 Create the hash we will use to determine if the contents changed.
36*****************************************************************************/
37
38static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
39			struct change_data *data, struct change_data *old_data)
40{
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	struct smb_Dir *dp;
48	long offset;
49
50	ZERO_STRUCTP(data);
51
52	if(SMB_VFS_STAT(conn,path, &st) == -1)
53		return False;
54
55	data->modify_time = st.st_mtime;
56	data->status_time = st.st_ctime;
57
58	if (old_data) {
59		/*
60		 * Shortcut to avoid directory scan if the time
61		 * has changed - we always must return true then.
62		 */
63		if (old_data->modify_time != data->modify_time ||
64			old_data->status_time != data->status_time ) {
65				return True;
66		}
67	}
68
69	/*
70	 * If we are to watch for changes that are only stored
71	 * in inodes of files, not in the directory inode, we must
72	 * scan the directory and produce a unique identifier with
73	 * which we can determine if anything changed. We use the
74	 * modify and change times from all the files in the
75	 * directory, added together (ignoring wrapping if it's
76	 * larger than the max time_t value).
77	 */
78
79	dp = OpenDir(conn, path);
80	if (dp == NULL)
81		return False;
82
83	data->num_entries = 0;
84
85	pstrcpy(full_name, path);
86	pstrcat(full_name, "/");
87
88	fullname_len = strlen(full_name);
89	remaining_len = sizeof(full_name) - fullname_len - 1;
90	p = &full_name[fullname_len];
91
92	offset = 0;
93	while ((fname = ReadDirName(dp, &offset))) {
94		ZERO_STRUCT(st);
95		if(strequal(fname, ".") || strequal(fname, ".."))
96			continue;
97
98		if (!is_visible_file(conn, path, fname, &st, True))
99			continue;
100
101		data->num_entries++;
102		safe_strcpy(p, fname, remaining_len);
103
104		/*
105		 * Do the stat - but ignore errors.
106		 */
107		if (!VALID_STAT(st)) {
108			SMB_VFS_STAT(conn,full_name, &st);
109		}
110
111		/*
112		 * Always sum the times.
113		 */
114
115		data->total_time += (st.st_mtime + st.st_ctime);
116
117		/*
118		 * If requested hash the names.
119		 */
120
121		if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_FILE)) {
122			int i;
123			unsigned char tmp_hash[16];
124			mdfour(tmp_hash, (const unsigned char *)fname, strlen(fname));
125			for (i=0;i<16;i++)
126				data->name_hash[i] ^= tmp_hash[i];
127		}
128
129		/*
130		 * If requested sum the mode_t's.
131		 */
132
133		if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SECURITY))
134			data->mode_sum += st.st_mode;
135	}
136
137	CloseDir(dp);
138
139	return True;
140}
141
142/****************************************************************************
143 Register a change notify request.
144*****************************************************************************/
145
146static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags)
147{
148	struct change_data data;
149
150	if (!notify_hash(conn, path, flags, &data, NULL))
151		return NULL;
152
153	data.last_check_time = time(NULL);
154
155	return (void *)memdup(&data, sizeof(data));
156}
157
158/****************************************************************************
159 Check if a change notify should be issued.
160 A time of zero means instantaneous check - don't modify the last check time.
161*****************************************************************************/
162
163static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
164{
165	struct change_data *data = (struct change_data *)datap;
166	struct change_data data2;
167
168	if (t && t < data->last_check_time + lp_change_notify_timeout())
169		return False;
170
171	if (!change_to_user(conn,vuid))
172		return True;
173	if (!set_current_service(conn,FLAG_CASELESS_PATHNAMES,True)) {
174		change_to_root_user();
175		return True;
176	}
177
178	if (!notify_hash(conn, path, flags, &data2, data) ||
179	    data2.modify_time != data->modify_time ||
180	    data2.status_time != data->status_time ||
181	    data2.total_time != data->total_time ||
182	    data2.num_entries != data->num_entries ||
183		data2.mode_sum != data->mode_sum ||
184		memcmp(data2.name_hash, data->name_hash, sizeof(data2.name_hash))) {
185		change_to_root_user();
186		return True;
187	}
188
189	if (t)
190		data->last_check_time = t;
191
192	change_to_root_user();
193
194	return False;
195}
196
197/****************************************************************************
198 Remove a change notify data structure.
199*****************************************************************************/
200
201static void hash_remove_notify(void *datap)
202{
203	free(datap);
204}
205
206/****************************************************************************
207 Setup hash based change notify.
208****************************************************************************/
209
210struct cnotify_fns *hash_notify_init(void)
211{
212	static struct cnotify_fns cnotify;
213
214	cnotify.register_notify = hash_register_notify;
215	cnotify.check_notify = hash_check_notify;
216	cnotify.remove_notify = hash_remove_notify;
217	cnotify.select_time = lp_change_notify_timeout();
218
219	return &cnotify;
220}
221
222/*
223  change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
224  change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
225
226  chain_size = 0;
227  file_chain_reset();
228
229  uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
230  SVAL(cnbp->request_buf,smb_uid);
231*/
232