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.
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
)
56 if(SMB_VFS_STAT(conn
,path
, &st
) == -1)
59 data
->modify_time
= get_mtimespec(&st
);
60 data
->status_time
= get_ctimespec(&st
);
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
) ) {
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.
84 if (lp_change_notify_timeout(SNUM(conn
)) <= 0) {
85 /* It change notify timeout has been disabled, never scan the directory. */
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);
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
];
113 while ((fname
= ReadDirName(dp
, &offset
))) {
114 SET_STAT_INVALID(st
);
115 if(strequal(fname
, ".") || strequal(fname
, ".."))
118 if (!is_visible_file(conn
, path
, fname
, &st
, True
))
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
)) {
143 unsigned char tmp_hash
[16];
144 mdfour(tmp_hash
, (const unsigned char *)fname
, strlen(fname
));
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
;
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
))
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. */
195 if (t
&& t
< data
->last_check_time
+ cnto
) {
199 if (!change_to_user(conn
,vuid
))
201 if (!set_current_service(conn
,FLAG_CASELESS_PATHNAMES
,True
)) {
202 change_to_root_user();
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();
218 data
->last_check_time
= t
;
221 change_to_root_user();
226 /****************************************************************************
227 Remove a change notify data structure.
228 *****************************************************************************/
230 static void hash_remove_notify(void *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;
253 change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
254 change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
259 uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
260 SVAL(cnbp->request_buf,smb_uid);