xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c (revision bfe5e737326ea1aafea02849716d8aceacf5c2eb)
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 2016 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
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
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 	sds = &sr->sr_server->sv_disp_stats1[sr->smb_com];
230 	atomic_add_64(&sds->sdt_txb, (int64_t)sr->reply.chain_offset);
231 
232 	smbsr_send_reply(sr);	/* also puts the SMB header. */
233 	smbsr_cleanup(sr);
234 
235 	mutex_enter(&sr->sr_mutex);
236 	sr->sr_state = SMB_REQ_STATE_COMPLETED;
237 	mutex_exit(&sr->sr_mutex);
238 
239 	smb_request_free(sr);
240 }
241