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 */
73 smb_sdrc_t
smb_nt_transact_notify_change(smb_request_t * sr,struct smb_xa * xa)74 smb_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 */
147 void
smb_nt_transact_notify_finish(void * arg)148 smb_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
228 sendit:
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