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 Copyright (C) Volker Lendecke 2007
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 struct hash_change_data {
26 time_t last_check_time; /* time we last checked this entry */
27 struct timespec modify_time; /* Info from the directory we're
29 struct timespec status_time; /* Info from the directory we're
31 time_t total_time; /* Total time of all directory entries - don't care
33 unsigned int num_entries; /* Zero or the number of files in the
35 unsigned int mode_sum;
36 unsigned char name_hash[16];
39 struct hash_notify_ctx {
40 struct hash_change_data *data;
46 /* Compare struct timespec. */
47 #define TIMESTAMP_NEQ(x, y) (((x).tv_sec != (y).tv_sec) || ((x).tv_nsec != (y).tv_nsec))
49 /****************************************************************************
50 Create the hash we will use to determine if the contents changed.
51 *****************************************************************************/
53 static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
54 struct hash_change_data *data,
55 struct hash_change_data *old_data)
68 if(SMB_VFS_STAT(conn,path, &st) == -1)
71 data->modify_time = get_mtimespec(&st);
72 data->status_time = get_ctimespec(&st);
76 * Shortcut to avoid directory scan if the time
77 * has changed - we always must return true then.
79 if (TIMESTAMP_NEQ(old_data->modify_time, data->modify_time) ||
80 TIMESTAMP_NEQ(old_data->status_time, data->status_time) ) {
85 if (S_ISDIR(st.st_mode) &&
86 (flags & ~(FILE_NOTIFY_CHANGE_FILE_NAME
87 | FILE_NOTIFY_CHANGE_DIR_NAME)) == 0)
89 /* This is the case of a client wanting to know only when
90 * the contents of a directory changes. Since any file
91 * creation, rename or deletion will update the directory
92 * timestamps, we don't need to create a hash.
98 * If we are to watch for changes that are only stored
99 * in inodes of files, not in the directory inode, we must
100 * scan the directory and produce a unique identifier with
101 * which we can determine if anything changed. We use the
102 * modify and change times from all the files in the
103 * directory, added together (ignoring wrapping if it's
104 * larger than the max time_t value).
107 dp = OpenDir(conn, path, NULL, 0);
111 data->num_entries = 0;
113 pstrcpy(full_name, path);
114 pstrcat(full_name, "/");
116 fullname_len = strlen(full_name);
117 remaining_len = sizeof(full_name) - fullname_len - 1;
118 p = &full_name[fullname_len];
121 while ((fname = ReadDirName(dp, &offset))) {
122 SET_STAT_INVALID(st);
123 if(strequal(fname, ".") || strequal(fname, ".."))
126 if (!is_visible_file(conn, path, fname, &st, True))
130 safe_strcpy(p, fname, remaining_len);
133 * Do the stat - but ignore errors.
135 if (!VALID_STAT(st)) {
136 SMB_VFS_STAT(conn,full_name, &st);
140 * Always sum the times.
143 data->total_time += (st.st_mtime + st.st_ctime);
146 * If requested hash the names.
149 if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME
150 |FILE_NOTIFY_CHANGE_FILE_NAME)) {
152 unsigned char tmp_hash[16];
153 mdfour(tmp_hash, (const unsigned char *)fname,
156 data->name_hash[i] ^= tmp_hash[i];
160 * If requested sum the mode_t's.
163 if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES
164 |FILE_NOTIFY_CHANGE_SECURITY))
165 data->mode_sum += st.st_mode;
173 static void hash_change_notify_handler(struct event_context *event_ctx,
174 struct timed_event *te,
175 const struct timeval *now,
178 struct hash_change_data *new_data;
179 struct hash_notify_ctx *ctx =
180 talloc_get_type_abort(private_data,
181 struct hash_notify_ctx);
185 if (!(new_data = TALLOC_P(ctx, struct hash_change_data))) {
186 DEBUG(0, ("talloc failed\n"));
188 * No new timed event;
193 if (!notify_hash(ctx->fsp->conn, ctx->fsp->fsp_name,
194 ctx->filter, new_data, ctx->data)
195 || TIMESTAMP_NEQ(new_data->modify_time, ctx->data->modify_time)
196 || TIMESTAMP_NEQ(new_data->status_time, ctx->data->status_time)
197 || new_data->total_time != ctx->data->total_time
198 || new_data->num_entries != ctx->data->num_entries
199 || new_data->mode_sum != ctx->data->mode_sum
200 || (memcmp(new_data->name_hash, ctx->data->name_hash,
201 sizeof(new_data->name_hash)))) {
202 notify_fsp(ctx->fsp, 0, NULL);
205 TALLOC_FREE(ctx->data);
206 ctx->data = new_data;
211 lp_change_notify_timeout(SNUM(ctx->fsp->conn)), 0),
212 "hash_change_notify_handler",
213 hash_change_notify_handler, ctx);
218 static void *hash_notify_add(TALLOC_CTX *mem_ctx,
219 struct event_context *event_ctx,
223 struct hash_notify_ctx *ctx;
224 int timeout = lp_change_notify_timeout(SNUM(fsp->conn));
227 /* It change notify timeout has been disabled, never scan the
232 if (!(ctx = TALLOC_P(mem_ctx, struct hash_notify_ctx))) {
233 DEBUG(0, ("talloc failed\n"));
237 if (!(ctx->path = talloc_asprintf(ctx, "%s/%s", fsp->conn->connectpath,
239 DEBUG(0, ("talloc_asprintf failed\n"));
244 if (!(ctx->data = TALLOC_P(ctx, struct hash_change_data))) {
245 DEBUG(0, ("talloc failed\n"));
253 * Don't change the Samba filter, hash can only be a very bad attempt
256 ctx->filter = *filter;
258 notify_hash(fsp->conn, ctx->path, ctx->filter, ctx->data, NULL);
260 event_add_timed(event_ctx, ctx, timeval_current_ofs(timeout, 0),
261 "hash_change_notify_handler",
262 hash_change_notify_handler, ctx);
267 /****************************************************************************
268 Setup hash based change notify.
269 ****************************************************************************/
271 struct cnotify_fns *hash_notify_init(void)
273 static struct cnotify_fns cnotify;
275 cnotify.notify_add = hash_notify_add;