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 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
)
51 if(SMB_VFS_STAT(conn
,path
, &st
) == -1)
54 data
->modify_time
= st
.st_mtime
;
55 data
->status_time
= st
.st_ctime
;
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
) {
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
);
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
, ".."))
96 safe_strcpy(p
, fname
, remaining_len
);
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
)) {
117 unsigned char tmp_hash
[16];
118 mdfour(tmp_hash
, (unsigned char *)fname
, strlen(fname
));
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
;
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
))
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())
165 if (!change_to_user(conn
,vuid
))
167 if (!set_current_service(conn
,True
)) {
168 change_to_root_user();
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();
184 data
->last_check_time
= t
;
186 change_to_root_user();
191 /****************************************************************************
192 Remove a change notify data structure.
193 *****************************************************************************/
195 static void hash_remove_notify(void *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();
217 change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
218 change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
223 uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
224 SVAL(cnbp->request_buf,smb_uid);