#include "includes.h"
struct change_data {
time_t last_check_time;
time_t modify_time;
time_t status_time;
time_t total_time;
unsigned int num_entries;
unsigned int mode_sum;
unsigned char name_hash[16];
};
static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
struct change_data *data, struct change_data *old_data)
{
SMB_STRUCT_STAT st;
pstring full_name;
char *p;
const char *fname;
size_t remaining_len;
size_t fullname_len;
void *dp;
ZERO_STRUCTP(data);
if(SMB_VFS_STAT(conn,path, &st) == -1)
return False;
data->modify_time = st.st_mtime;
data->status_time = st.st_ctime;
if (old_data) {
if (old_data->modify_time != data->modify_time ||
old_data->status_time != data->status_time ) {
return True;
}
}
dp = OpenDir(conn, path, True);
if (dp == NULL)
return False;
data->num_entries = 0;
pstrcpy(full_name, path);
pstrcat(full_name, "/");
fullname_len = strlen(full_name);
remaining_len = sizeof(full_name) - fullname_len - 1;
p = &full_name[fullname_len];
while ((fname = ReadDirName(dp))) {
if(strequal(fname, ".") || strequal(fname, ".."))
continue;
data->num_entries++;
safe_strcpy(p, fname, remaining_len);
ZERO_STRUCT(st);
SMB_VFS_STAT(conn,full_name, &st);
data->total_time += (st.st_mtime + st.st_ctime);
if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_FILE)) {
int i;
unsigned char tmp_hash[16];
mdfour(tmp_hash, (const unsigned char *)fname, strlen(fname));
for (i=0;i<16;i++)
data->name_hash[i] ^= tmp_hash[i];
}
if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SECURITY))
data->mode_sum += st.st_mode;
}
CloseDir(dp);
return True;
}
static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags)
{
struct change_data data;
if (!notify_hash(conn, path, flags, &data, NULL))
return NULL;
data.last_check_time = time(NULL);
return (void *)memdup(&data, sizeof(data));
}
static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
{
struct change_data *data = (struct change_data *)datap;
struct change_data data2;
if (t && t < data->last_check_time + lp_change_notify_timeout())
return False;
if (!change_to_user(conn,vuid))
return True;
if (!set_current_service(conn,FLAG_CASELESS_PATHNAMES,True)) {
change_to_root_user();
return True;
}
if (!notify_hash(conn, path, flags, &data2, data) ||
data2.modify_time != data->modify_time ||
data2.status_time != data->status_time ||
data2.total_time != data->total_time ||
data2.num_entries != data->num_entries ||
data2.mode_sum != data->mode_sum ||
memcmp(data2.name_hash, data->name_hash, sizeof(data2.name_hash))) {
change_to_root_user();
return True;
}
if (t)
data->last_check_time = t;
change_to_root_user();
return False;
}
static void hash_remove_notify(void *datap)
{
free(datap);
}
struct cnotify_fns *hash_notify_init(void)
{
static struct cnotify_fns cnotify;
cnotify.register_notify = hash_register_notify;
cnotify.check_notify = hash_check_notify;
cnotify.remove_notify = hash_remove_notify;
cnotify.select_time = lp_change_notify_timeout();
return &cnotify;
}