1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
/*
Unix SMB/CIFS implementation.
change notify handling
Copyright (C) Andrew Tridgell 2000
Copyright (C) Jeremy Allison 1994-1998
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
static struct cnotify_fns *cnotify;
/****************************************************************************
This is the structure to queue to implement NT change
notify. It consists of smb_size bytes stored from the
transact command (to keep the mid, tid etc around).
Plus the fid to examine and notify private data.
*****************************************************************************/
struct change_notify {
struct change_notify *next, *prev;
files_struct *fsp;
connection_struct *conn;
uint32 flags;
char request_buf[smb_size];
void *change_data;
};
static struct change_notify *change_notify_list;
/****************************************************************************
Setup the common parts of the return packet and send it.
*****************************************************************************/
static void change_notify_reply_packet(char *inbuf, NTSTATUS error_code)
{
char outbuf[smb_size+38];
memset(outbuf, '\0', sizeof(outbuf));
construct_reply_common(inbuf, outbuf);
ERROR_NT(error_code);
/*
* Seems NT needs a transact command with an error code
* in it. This is a longer packet than a simple error.
*/
set_message(outbuf,18,0,False);
show_msg(outbuf);
if (!send_smb(smbd_server_fd(),outbuf))
exit_server("change_notify_reply_packet: send_smb failed.");
}
/****************************************************************************
Remove an entry from the list and free it, also closing any
directory handle if necessary.
*****************************************************************************/
static void change_notify_remove(struct change_notify *cnbp)
{
cnotify->remove_notify(cnbp->change_data);
DLIST_REMOVE(change_notify_list, cnbp);
ZERO_STRUCTP(cnbp);
SAFE_FREE(cnbp);
}
/****************************************************************************
Delete entries by fnum from the change notify pending queue.
*****************************************************************************/
void remove_pending_change_notify_requests_by_fid(files_struct *fsp, NTSTATUS status)
{
struct change_notify *cnbp, *next;
for (cnbp=change_notify_list; cnbp; cnbp=next) {
next=cnbp->next;
if (cnbp->fsp->fnum == fsp->fnum) {
change_notify_reply_packet(cnbp->request_buf,status);
change_notify_remove(cnbp);
}
}
}
/****************************************************************************
Delete entries by mid from the change notify pending queue. Always send reply.
*****************************************************************************/
void remove_pending_change_notify_requests_by_mid(int mid)
{
struct change_notify *cnbp, *next;
for (cnbp=change_notify_list; cnbp; cnbp=next) {
next=cnbp->next;
if(SVAL(cnbp->request_buf,smb_mid) == mid) {
change_notify_reply_packet(cnbp->request_buf,NT_STATUS_CANCELLED);
change_notify_remove(cnbp);
}
}
}
/****************************************************************************
Delete entries by filename and cnum from the change notify pending queue.
Always send reply.
*****************************************************************************/
void remove_pending_change_notify_requests_by_filename(files_struct *fsp, NTSTATUS status)
{
struct change_notify *cnbp, *next;
for (cnbp=change_notify_list; cnbp; cnbp=next) {
next=cnbp->next;
/*
* We know it refers to the same directory if the connection number and
* the filename are identical.
*/
if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) {
change_notify_reply_packet(cnbp->request_buf,status);
change_notify_remove(cnbp);
}
}
}
/****************************************************************************
Return true if there are pending change notifies.
****************************************************************************/
int change_notify_timeout(void)
{
return cnotify->select_time;
}
/****************************************************************************
Process the change notify queue. Note that this is only called as root.
Returns True if there are still outstanding change notify requests on the
queue.
*****************************************************************************/
BOOL process_pending_change_notify_queue(time_t t)
{
struct change_notify *cnbp, *next;
uint16 vuid;
for (cnbp=change_notify_list; cnbp; cnbp=next) {
next=cnbp->next;
vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(cnbp->request_buf,smb_uid);
if (cnotify->check_notify(cnbp->conn, vuid, cnbp->fsp->fsp_name, cnbp->flags, cnbp->change_data, t)) {
DEBUG(10,("process_pending_change_notify_queue: dir %s changed !\n", cnbp->fsp->fsp_name ));
change_notify_reply_packet(cnbp->request_buf,STATUS_NOTIFY_ENUM_DIR);
change_notify_remove(cnbp);
}
}
return (change_notify_list != NULL);
}
/****************************************************************************
Now queue an entry on the notify change list.
We only need to save smb_size bytes from this incoming packet
as we will always by returning a 'read the directory yourself'
error.
****************************************************************************/
BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn, uint32 flags)
{
struct change_notify *cnbp;
if((cnbp = SMB_MALLOC_P(struct change_notify)) == NULL) {
DEBUG(0,("change_notify_set: malloc fail !\n" ));
return -1;
}
ZERO_STRUCTP(cnbp);
memcpy(cnbp->request_buf, inbuf, smb_size);
cnbp->fsp = fsp;
cnbp->conn = conn;
cnbp->flags = flags;
cnbp->change_data = cnotify->register_notify(conn, fsp->fsp_name, flags);
if (!cnbp->change_data) {
SAFE_FREE(cnbp);
return False;
}
DLIST_ADD(change_notify_list, cnbp);
/* Push the MID of this packet on the signing queue. */
srv_defer_sign_response(SVAL(inbuf,smb_mid));
return True;
}
/****************************************************************************
Initialise the change notify subsystem.
****************************************************************************/
BOOL init_change_notify(void)
{
cnotify = NULL;
#if HAVE_KERNEL_CHANGE_NOTIFY
if (cnotify == NULL && lp_kernel_change_notify())
cnotify = kernel_notify_init();
#endif
#if HAVE_FAM_CHANGE_NOTIFY
if (cnotify == NULL && lp_fam_change_notify())
cnotify = fam_notify_init();
#endif
if (!cnotify) cnotify = hash_notify_init();
if (!cnotify) {
DEBUG(0,("Failed to init change notify system\n"));
return False;
}
return True;
}
|