~vcs-imports/samba/main

« back to all changes in this revision

Viewing changes to source/smbd/notify_hash.c

  • Committer: jerry
  • Date: 2006-07-14 21:48:39 UTC
  • Revision ID: vcs-imports@canonical.com-20060714214839-586d8c489a8fcead
gutting trunk to move to svn:externals

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
        struct timespec modify_time; /* Info from the directory we're monitoring. */
27
 
        struct timespec 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
 
/* Compare struct timespec. */
36
 
#define TIMESTAMP_NEQ(x, y) (((x).tv_sec != (y).tv_sec) || ((x).tv_nsec != (y).tv_nsec))
37
 
 
38
 
/****************************************************************************
39
 
 Create the hash we will use to determine if the contents changed.
40
 
*****************************************************************************/
41
 
 
42
 
static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, 
43
 
                        struct change_data *data, struct change_data *old_data)
44
 
{
45
 
        SMB_STRUCT_STAT st;
46
 
        pstring full_name;
47
 
        char *p;
48
 
        const char *fname;
49
 
        size_t remaining_len;
50
 
        size_t fullname_len;
51
 
        struct smb_Dir *dp;
52
 
        long offset;
53
 
 
54
 
        ZERO_STRUCTP(data);
55
 
 
56
 
        if(SMB_VFS_STAT(conn,path, &st) == -1)
57
 
                return False;
58
 
 
59
 
        data->modify_time = get_mtimespec(&st);
60
 
        data->status_time = get_ctimespec(&st);
61
 
 
62
 
        if (old_data) {
63
 
                /*
64
 
                 * Shortcut to avoid directory scan if the time
65
 
                 * has changed - we always must return true then.
66
 
                 */
67
 
                if (TIMESTAMP_NEQ(old_data->modify_time, data->modify_time) ||
68
 
                    TIMESTAMP_NEQ(old_data->status_time, data->status_time) ) {
69
 
                                return True;
70
 
                }
71
 
        }
72
 
 
73
 
        if (S_ISDIR(st.st_mode) && 
74
 
            (flags & ~(FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME)) == 0)
75
 
        {
76
 
                /* This is the case of a client wanting to know only when
77
 
                 * the contents of a directory changes. Since any file
78
 
                 * creation, rename or deletion will update the directory
79
 
                 * timestamps, we don't need to create a hash.
80
 
                 */
81
 
                return True;
82
 
        }
83
 
 
84
 
        if (lp_change_notify_timeout(SNUM(conn)) <= 0) {
85
 
                /* It change notify timeout has been disabled, never scan the directory. */
86
 
                return True;
87
 
        }
88
 
 
89
 
        /*
90
 
         * If we are to watch for changes that are only stored
91
 
         * in inodes of files, not in the directory inode, we must
92
 
         * scan the directory and produce a unique identifier with
93
 
         * which we can determine if anything changed. We use the
94
 
         * modify and change times from all the files in the
95
 
         * directory, added together (ignoring wrapping if it's
96
 
         * larger than the max time_t value).
97
 
         */
98
 
 
99
 
        dp = OpenDir(conn, path, NULL, 0);
100
 
        if (dp == NULL)
101
 
                return False;
102
 
 
103
 
        data->num_entries = 0;
104
 
        
105
 
        pstrcpy(full_name, path);
106
 
        pstrcat(full_name, "/");
107
 
        
108
 
        fullname_len = strlen(full_name);
109
 
        remaining_len = sizeof(full_name) - fullname_len - 1;
110
 
        p = &full_name[fullname_len];
111
 
        
112
 
        offset = 0;
113
 
        while ((fname = ReadDirName(dp, &offset))) {
114
 
                SET_STAT_INVALID(st);
115
 
                if(strequal(fname, ".") || strequal(fname, ".."))
116
 
                        continue;               
117
 
 
118
 
                if (!is_visible_file(conn, path, fname, &st, True))
119
 
                        continue;
120
 
 
121
 
                data->num_entries++;
122
 
                safe_strcpy(p, fname, remaining_len);
123
 
 
124
 
                /*
125
 
                 * Do the stat - but ignore errors.
126
 
                 */             
127
 
                if (!VALID_STAT(st)) {
128
 
                        SMB_VFS_STAT(conn,full_name, &st);
129
 
                }
130
 
 
131
 
                /*
132
 
                 * Always sum the times.
133
 
                 */
134
 
 
135
 
                data->total_time += (st.st_mtime + st.st_ctime);
136
 
 
137
 
                /*
138
 
                 * If requested hash the names.
139
 
                 */
140
 
 
141
 
                if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_FILE)) {
142
 
                        int i;
143
 
                        unsigned char tmp_hash[16];
144
 
                        mdfour(tmp_hash, (const unsigned char *)fname, strlen(fname));
145
 
                        for (i=0;i<16;i++)
146
 
                                data->name_hash[i] ^= tmp_hash[i];
147
 
                }
148
 
 
149
 
                /*
150
 
                 * If requested sum the mode_t's.
151
 
                 */
152
 
 
153
 
                if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SECURITY))
154
 
                        data->mode_sum += st.st_mode;
155
 
        }
156
 
        
157
 
        CloseDir(dp);
158
 
        
159
 
        return True;
160
 
}
161
 
 
162
 
/****************************************************************************
163
 
 Register a change notify request.
164
 
*****************************************************************************/
165
 
 
166
 
static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags)
167
 
{
168
 
        struct change_data data;
169
 
 
170
 
        if (!notify_hash(conn, path, flags, &data, NULL))
171
 
                return NULL;
172
 
 
173
 
        data.last_check_time = time(NULL);
174
 
 
175
 
        return (void *)memdup(&data, sizeof(data));
176
 
}
177
 
 
178
 
/****************************************************************************
179
 
 Check if a change notify should be issued.
180
 
 A time of zero means instantaneous check - don't modify the last check time.
181
 
*****************************************************************************/
182
 
 
183
 
static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
184
 
{
185
 
        struct change_data *data = (struct change_data *)datap;
186
 
        struct change_data data2;
187
 
        int cnto = lp_change_notify_timeout(SNUM(conn));
188
 
 
189
 
        if (t && cnto <= 0) {
190
 
                /* Change notify turned off on this share.
191
 
                 * Only scan when (t==0) - we think something changed. */
192
 
                return False;
193
 
        }
194
 
 
195
 
        if (t && t < data->last_check_time + cnto) {
196
 
                return False;
197
 
        }
198
 
 
199
 
        if (!change_to_user(conn,vuid))
200
 
                return True;
201
 
        if (!set_current_service(conn,FLAG_CASELESS_PATHNAMES,True)) {
202
 
                change_to_root_user();
203
 
                return True;
204
 
        }
205
 
 
206
 
        if (!notify_hash(conn, path, flags, &data2, data) ||
207
 
            TIMESTAMP_NEQ(data2.modify_time, data->modify_time) ||
208
 
            TIMESTAMP_NEQ(data2.status_time, data->status_time) ||
209
 
            data2.total_time != data->total_time ||
210
 
            data2.num_entries != data->num_entries ||
211
 
                data2.mode_sum != data->mode_sum ||
212
 
                memcmp(data2.name_hash, data->name_hash, sizeof(data2.name_hash))) {
213
 
                change_to_root_user();
214
 
                return True;
215
 
        }
216
 
 
217
 
        if (t) {
218
 
                data->last_check_time = t;
219
 
        }
220
 
 
221
 
        change_to_root_user();
222
 
 
223
 
        return False;
224
 
}
225
 
 
226
 
/****************************************************************************
227
 
 Remove a change notify data structure.
228
 
*****************************************************************************/
229
 
 
230
 
static void hash_remove_notify(void *datap)
231
 
{
232
 
        free(datap);
233
 
}
234
 
 
235
 
/****************************************************************************
236
 
 Setup hash based change notify.
237
 
****************************************************************************/
238
 
 
239
 
struct cnotify_fns *hash_notify_init(void) 
240
 
{
241
 
        static struct cnotify_fns cnotify;
242
 
 
243
 
        cnotify.register_notify = hash_register_notify;
244
 
        cnotify.check_notify = hash_check_notify;
245
 
        cnotify.remove_notify = hash_remove_notify;
246
 
        cnotify.select_time = 60; /* Start with 1 minute default. */
247
 
        cnotify.notification_fd = -1;
248
 
 
249
 
        return &cnotify;
250
 
}
251
 
 
252
 
/*
253
 
  change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
254
 
  change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
255
 
 
256
 
  chain_size = 0;
257
 
  file_chain_reset();
258
 
 
259
 
  uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : 
260
 
  SVAL(cnbp->request_buf,smb_uid);
261
 
*/