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 24struct 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 38static 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 ZERO_STRUCT(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 146static 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 163static 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 201static void hash_remove_notify(void *datap) 202{ 203 free(datap); 204} 205 206/**************************************************************************** 207 Setup hash based change notify. 208****************************************************************************/ 209 210struct 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*/ 232