Removed 'extern int DEBUGLEVEL' as it is now in the smb.h header.
[samba.git] / source3 / smbd / notify_hash.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    change notify handling - hash based implementation
5    Copyright (C) Jeremy Allison 1994-1998
6    Copyright (C) Andrew Tridgell 2000
7
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.
12
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.
17
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.
21 */
22
23 #include "includes.h"
24
25 struct change_data {
26         time_t last_check_time; /* time we last checked this entry */
27         time_t modify_time; /* Info from the directory we're monitoring. */ 
28         time_t status_time; /* Info from the directory we're monitoring. */
29         time_t total_time; /* Total time of all directory entries - don't care if it wraps. */
30         unsigned int num_entries; /* Zero or the number of files in the directory. */
31 };
32
33
34 /****************************************************************************
35  Create the hash we will use to determine if the contents changed.
36 *****************************************************************************/
37 static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, 
38                         struct change_data *data, struct change_data *old_data)
39 {
40         SMB_STRUCT_STAT st;
41         pstring full_name;
42         char *p;
43         char *fname;
44         size_t remaining_len;
45         size_t fullname_len;
46         void *dp;
47
48         ZERO_STRUCTP(data);
49
50         if(vfs_stat(conn,path, &st) == -1) return False;
51
52         data->modify_time = st.st_mtime;
53         data->status_time = st.st_ctime;
54
55         if (old_data) {
56                 /*
57                  * Shortcut to avoid directory scan if the time
58                  * has changed - we always must return true then.
59                  */
60                 if (old_data->modify_time != data->modify_time ||
61                         old_data->status_time != data->status_time ) {
62                                 return True;
63                 }
64         }
65  
66         /*
67          * If we are to watch for changes that are only stored
68          * in inodes of files, not in the directory inode, we must
69          * scan the directory and produce a unique identifier with
70          * which we can determine if anything changed. We use the
71          * modify and change times from all the files in the
72          * directory, added together (ignoring wrapping if it's
73          * larger than the max time_t value).
74          */
75
76         if (!(flags & (FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE))) return True;
77
78         dp = OpenDir(conn, path, True);
79         if (dp == NULL) return False;
80
81         data->num_entries = 0;
82         
83         pstrcpy(full_name, path);
84         pstrcat(full_name, "/");
85         
86         fullname_len = strlen(full_name);
87         remaining_len = sizeof(full_name) - fullname_len - 1;
88         p = &full_name[fullname_len];
89         
90         while ((fname = ReadDirName(dp))) {
91                 if(strequal(fname, ".") || strequal(fname, "..")) continue;             
92
93                 data->num_entries++;
94                 safe_strcpy(p, fname, remaining_len);
95
96                 ZERO_STRUCT(st);
97
98                 /*
99                  * Do the stat - but ignore errors.
100                  */             
101                 vfs_stat(conn,full_name, &st);
102                 data->total_time += (st.st_mtime + st.st_ctime);
103         }
104         
105         CloseDir(dp);
106         
107         return True;
108 }
109
110
111 /****************************************************************************
112 register a change notify request
113 *****************************************************************************/
114 static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags)
115 {
116         struct change_data data;
117
118         if (!notify_hash(conn, path, flags, &data, NULL)) return NULL;
119
120         data.last_check_time = time(NULL);
121
122         return (void *)memdup(&data, sizeof(data));
123 }
124
125 /****************************************************************************
126  Check if a change notify should be issued.
127  A time of zero means instantaneous check - don't modify the last check time.
128 *****************************************************************************/
129 static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
130 {
131         struct change_data *data = (struct change_data *)datap;
132         struct change_data data2;
133
134         if (t && t < data->last_check_time + lp_change_notify_timeout()) return False;
135
136         if (!become_user(conn,vuid)) return True;
137         if (!become_service(conn,True)) {
138                 unbecome_user();
139                 return True;
140         }
141
142         if (!notify_hash(conn, path, flags, &data2, data) ||
143             data2.modify_time != data->modify_time ||
144             data2.status_time != data->status_time ||
145             data2.total_time != data->total_time ||
146             data2.num_entries != data->num_entries) {
147                 unbecome_user();
148                 return True;
149         }
150
151         if (t)
152                 data->last_check_time = t;
153
154         unbecome_user();
155
156         return False;
157 }
158
159 /****************************************************************************
160 remove a change notify data structure
161 *****************************************************************************/
162 static void hash_remove_notify(void *datap)
163 {
164         SAFE_FREE(datap);
165 }
166
167
168 /****************************************************************************
169 setup hash based change notify
170 ****************************************************************************/
171 struct cnotify_fns *hash_notify_init(void) 
172 {
173         static struct cnotify_fns cnotify;
174
175         cnotify.register_notify = hash_register_notify;
176         cnotify.check_notify = hash_check_notify;
177         cnotify.remove_notify = hash_remove_notify;
178         cnotify.select_time = lp_change_notify_timeout();
179
180         return &cnotify;
181 }
182
183
184 /*
185   change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
186   change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
187
188   chain_size = 0;
189   file_chain_reset();
190
191   uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : 
192   SVAL(cnbp->request_buf,smb_uid);
193 */