1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
25 */
26
27/*
28 * File Change Notification (FCN)
29 * SMB1 specific part.
30 */
31
32/*
33 * SMB: nt_transact_notify_change
34 *
35 *  Client Setup Words                 Description
36 *  ================================== =================================
37 *
38 *  ULONG CompletionFilter;            Specifies operation to monitor
39 *  USHORT Fid;                        Fid of directory to monitor
40 *  BOOLEAN WatchTree;                 TRUE = watch all subdirectories too
41 *  UCHAR Reserved;                    MBZ
42 *
43 * This command notifies the client when the directory specified by Fid is
44 * modified.  See smb_notify.c for details.
45 *
46 * The MaxParameterCount field in the NT transact header determines
47 * the size of the buffer used to return change information:
48 *
49 *  Server Response                    Description
50 *  ================================== ================================
51 *  ParameterCount                     # of bytes of change data
52 *  Parameters[ ParameterCount ]       FILE_NOTIFY_INFORMATION
53 *                                      structures
54 *
55 * See smb_notify.c for details of FILE_NOTIFY_INFORMATION
56 */
57
58#include <smbsrv/smb_kproto.h>
59
60/*
61 * smb_nt_transact_notify_change
62 *
63 * Handle and SMB NT transact NOTIFY CHANGE request.
64 * Basically, wait until "something has changed", and either
65 * return information about what changed, or return a special
66 * error telling the client "many things changed".
67 *
68 * The implementation uses a per-node list of waiting notify
69 * requests like this one, each with a blocked worker thead.
70 * Later, FEM and/or smbsrv events wake these threads, which
71 * then send the reply to the client.
72 */
73smb_sdrc_t
74smb_nt_transact_notify_change(smb_request_t *sr, struct smb_xa *xa)
75{
76	mbuf_chain_t		tmp_mbc;
77	uint32_t		oBufSize;
78	uint32_t		CompletionFilter;
79	unsigned char		WatchTree;
80	uint32_t		status;
81
82	if (smb_mbc_decodef(&xa->req_setup_mb, "lwb",
83	    &CompletionFilter, &sr->smb_fid, &WatchTree) != 0) {
84		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
85		return (SDRC_ERROR);
86	}
87
88	smbsr_lookup_file(sr);
89	if (sr->fid_ofile == NULL) {
90		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
91		return (SDRC_ERROR);
92	}
93
94	oBufSize = xa->rep_param_mb.max_bytes;
95	CompletionFilter &= FILE_NOTIFY_VALID_MASK;
96	if (WatchTree)
97		CompletionFilter |= FILE_NOTIFY_CHANGE_EV_SUBDIR;
98
99	/*
100	 * Check for events and consume, non-blocking.
101	 * Special return STATUS_PENDING means:
102	 *   No events; caller must call "act2" next.
103	 */
104	status = smb_notify_act1(sr, oBufSize, CompletionFilter);
105	if (status == NT_STATUS_PENDING) {
106		status = smb_notify_act2(sr);
107		if (status == NT_STATUS_PENDING) {
108			/* See: smb_nt_transact_notify_finish */
109			return (SDRC_SR_KEPT);
110		}
111		/* else: some other error, or even success */
112	}
113
114	/*
115	 * SMB1 expects an empty trans response after the
116	 * FID we're watching is closed.
117	 */
118	if (status == NT_STATUS_NOTIFY_CLEANUP) {
119		status = 0;
120		MBC_FLUSH(&sr->raw_data);
121	}
122
123	if (status != 0) {
124		smbsr_status(sr, status, 0, 0);
125		if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR)
126			return (SDRC_ERROR);
127		/* Else continue with NT_STATUS_NOTIFY_ENUM_DIR etc. */
128	}
129
130	/*
131	 * The nt_trans call expects the output in rep_param_mb,
132	 * but our common code puts it in raw_data.  Move it
133	 * where the caller expects it via swaping the two,
134	 * which lets the normal cleanup take care of both.
135	 */
136	tmp_mbc = xa->rep_param_mb;
137	xa->rep_param_mb = sr->raw_data;
138	sr->raw_data = tmp_mbc;
139
140	return (SDRC_SUCCESS);
141}
142
143/*
144 * This is called via taskq_dispatch in smb_notify.c
145 * to finish up an NT transact notify change request.
146 */
147void
148smb_nt_transact_notify_finish(void *arg)
149{
150	smb_request_t	*sr = arg;
151	struct smb_xa	*xa;
152	smb_disp_stats_t *sds;
153	int		total_bytes, n_setup, n_param, n_data;
154	int		param_off, param_pad, data_off, data_pad;
155	uint32_t	status;
156
157	SMB_REQ_VALID(sr);
158
159	/*
160	 * Common part of notify, puts data in sr->raw_data
161	 */
162	status = smb_notify_act3(sr);
163
164	/*
165	 * SMB1 expects an empty trans response after the
166	 * FID we're watching is closed.
167	 */
168	if (status == NT_STATUS_NOTIFY_CLEANUP) {
169		status = 0;
170		MBC_FLUSH(&sr->raw_data);
171	}
172
173	if (status != 0) {
174		smbsr_status(sr, status, 0, 0);
175		if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR) {
176			(void) smb_mbc_encodef(&sr->reply, "bwbw",
177			    (short)0, 0L, (short)0, 0L);
178			goto sendit;
179		}
180		/* Else continue with NT_STATUS_NOTIFY_ENUM_DIR etc. */
181	}
182
183	/*
184	 * setup the NT transact reply
185	 *
186	 * Note that this is a copy/paste of code from
187	 * smb_nt_trans_dispatch(), with minor changes.
188	 * Intentionally keeping this similar to the
189	 * original rather than hand-optimizing.
190	 *
191	 * The "setup" and "data" parts of this trans reply
192	 * (n_setup, n_data, rep_setup_mb, rep_data_mb) are
193	 * always empty.  sr->raw_data replaces rep_param_mb.
194	 */
195	xa = sr->r_xa;
196	n_setup = MBC_LENGTH(&xa->rep_setup_mb);
197	n_param = MBC_LENGTH(&sr->raw_data);
198	n_data  = MBC_LENGTH(&xa->rep_data_mb);
199
200	n_setup = (n_setup + 1) / 2;	/* Convert to setup words */
201	param_pad = 1;			/* must be one */
202	param_off = param_pad + 32 + 37 + (n_setup << 1) + 2;
203	/* Pad to 4 bytes */
204	data_pad = (4 - ((param_off + n_param) & 3)) % 4;
205	/* Param off from hdr */
206	data_off = param_off + n_param + data_pad;
207	total_bytes = param_pad + n_param + data_pad + n_data;
208
209	(void) smbsr_encode_result(sr, 18+n_setup, total_bytes,
210	    "b3.llllllllbCw#.C#.C",
211	    18 + n_setup,	/* wct */
212	    n_param,		/* Total Parameter Bytes */
213	    n_data,		/* Total Data Bytes */
214	    n_param,		/* Total Parameter Bytes this buffer */
215	    param_off,		/* Param offset from header start */
216	    0,			/* Param displacement */
217	    n_data,		/* Total Data Bytes this buffer */
218	    data_off,		/* Data offset from header start */
219	    0,			/* Data displacement */
220	    n_setup,		/* suwcnt */
221	    &xa->rep_setup_mb,	/* setup[] */
222	    total_bytes,	/* Total data bytes */
223	    param_pad,
224	    &sr->raw_data,	/* output mbc */
225	    data_pad,
226	    &xa->rep_data_mb);
227
228sendit:
229	/*
230	 * When smb_nt_transact_notify_change returned SDRC_SR_KEPT
231	 * the dispatcher skipped the "done" probe, so do it now.
232	 * Note: Don't use this probe in response time statistics.
233	 */
234	DTRACE_SMB_DONE(op__NtTransact, smb_request_t *, sr);
235
236	sds = &sr->sr_server->sv_disp_stats1[sr->smb_com];
237	atomic_add_64(&sds->sdt_txb, (int64_t)sr->reply.chain_offset);
238
239	smbsr_send_reply(sr);	/* also puts the SMB header. */
240	smbsr_cleanup(sr);
241
242	mutex_enter(&sr->sr_mutex);
243	sr->sr_state = SMB_REQ_STATE_COMPLETED;
244	mutex_exit(&sr->sr_mutex);
245
246	smb_request_free(sr);
247}
248