summaryrefslogtreecommitdiff
path: root/source/smbd/notify.c
blob: 3c21ce1e1b57bca049a298efac134cef9dca7d52 (plain)
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
/*
   Unix SMB/Netbios implementation.
   Version 3.0
   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"

extern int DEBUGLEVEL;

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, uint32 error_code)
{
	char outbuf[smb_size+38];

	memset(outbuf, '\0', sizeof(outbuf));
	construct_reply_common(inbuf, outbuf);

	/*
	 * If we're returning a 'too much in the directory changed' we need to
	 * set this is an NT error status flags. If we don't then the (probably
	 * untested) code in the NT redirector has a bug in that it doesn't re-issue
	 * the change notify.... Ah - I *love* it when I get so deeply into this I
	 * can even determine how MS failed to test stuff and why.... :-). JRA.
	 */
	
	SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
	ERROR(0,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);

	if (!send_smb(smbd_server_fd(),outbuf))
		exit_server("change_notify_reply_packet: send_smb failed.\n");
}

/****************************************************************************
remove an entry from the list and free it, also closing any
directory handle if necessary
Notice the horrible stuff we have to do because this is a singly linked list.
*****************************************************************************/
static void change_notify_remove(struct change_notify *cnbp)
{
	cnotify->remove_notify(cnbp->change_data);
	DLIST_REMOVE(change_notify_list, cnbp);
	ZERO_STRUCTP(cnbp);
	free(cnbp);
}


/****************************************************************************
 Delete entries by fnum from the change notify pending queue.
*****************************************************************************/
void remove_pending_change_notify_requests_by_fid(files_struct *fsp)
{
	struct change_notify *cnbp, *next;

	for (cnbp=change_notify_list; cnbp; cnbp=next) {
		next=cnbp->next;
		if (cnbp->fsp->fnum == fsp->fnum) {
			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)
{
	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,NT_STATUS_CANCELLED);
			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)) {
			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 = (struct change_notify *)malloc(sizeof(*cnbp))) == NULL) {
		DEBUG(0,("call_nt_transact_notify_change: 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) {
		free(cnbp);
		return False;
	}

	DLIST_ADD(change_notify_list, cnbp);

	return True;
}


/****************************************************************************
initialise the change notify subsystem
****************************************************************************/
BOOL init_change_notify(void)
{
#if HAVE_KERNEL_CHANGE_NOTIFY
	cnotify = kernel_notify_init();
#endif
	if (!cnotify) cnotify = hash_notify_init();
	
	if (!cnotify) {
		DEBUG(0,("Failed to init change notify system\n"));
		return False;
	}

	return True;
}