0464eaa2eb3ea364acae3cb4ec76e390f9d14a44
[abartlet/samba.git/.git] / source3 / smbd / notify_hash.c
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
24 struct 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
38 static 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                 SET_STAT_INVALID(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
146 static 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
163 static 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
201 static void hash_remove_notify(void *datap)
202 {
203         free(datap);
204 }
205
206 /****************************************************************************
207  Setup hash based change notify.
208 ****************************************************************************/
209
210 struct 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 */