smb2_change_notify.c revision 148d1a4158dc830f7b293a2ceb62ee54c2ebd72f
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.
24 * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
25 */
26
27/*
28 * Dispatch function for SMB2_CHANGE_NOTIFY
29 */
30
31#include <smbsrv/smb2_kproto.h>
32
33/* For the output DataOffset fields in here. */
34#define	DATA_OFF	(SMB2_HDR_SIZE + 8)
35
36smb_sdrc_t
37smb2_change_notify(smb_request_t *sr)
38{
39	uint16_t StructSize;
40	uint16_t iFlags;
41	uint32_t oBufLength;
42	smb2fid_t smb2fid;
43	uint32_t CompletionFilter;
44	uint32_t reserved;
45	uint32_t status;
46	int rc = 0;
47
48	/*
49	 * SMB2 Change Notify request
50	 */
51	rc = smb_mbc_decodef(
52	    &sr->smb_data,		"wwlqqll",
53	    &StructSize,		/* w */
54	    &iFlags,			/* w */
55	    &oBufLength,		/* l */
56	    &smb2fid.persistent,	/* q */
57	    &smb2fid.temporal,		/* q */
58	    &CompletionFilter,		/* l */
59	    &reserved);			/* l */
60	if (rc || StructSize != 32)
61		return (SDRC_ERROR);
62
63	status = smb2sr_lookup_fid(sr, &smb2fid);
64	DTRACE_SMB2_START(op__ChangeNotify, smb_request_t *, sr);
65
66	if (status != 0)
67		goto errout; /* Bad FID */
68
69	/*
70	 * Only deal with change notify last in a compound,
71	 * because it blocks indefinitely.  This status gets
72	 * "sticky" handling in smb2sr_work().
73	 */
74	if (sr->smb2_next_command != 0) {
75		status = NT_STATUS_INSUFFICIENT_RESOURCES;
76		goto errout;
77	}
78
79	CompletionFilter &= FILE_NOTIFY_VALID_MASK;
80	if (iFlags & SMB2_WATCH_TREE)
81		CompletionFilter |= FILE_NOTIFY_CHANGE_EV_SUBDIR;
82
83	if (oBufLength > smb2_max_trans)
84		oBufLength = smb2_max_trans;
85
86	/*
87	 * Check for events and consume, non-blocking.
88	 * Special return STATUS_PENDING means:
89	 *   No events; caller must call "act2" next.
90	 * SMB2 does that in "async mode".
91	 */
92	status = smb_notify_act1(sr, oBufLength, CompletionFilter);
93	if (status == NT_STATUS_PENDING) {
94		status = smb2sr_go_async(sr);
95		if (status != 0)
96			goto errout;
97		status = smb_notify_act2(sr);
98		if (status == NT_STATUS_PENDING) {
99			/* See next: smb2_change_notify_finish */
100			return (SDRC_SR_KEPT);
101		}
102	}
103
104errout:
105	sr->smb2_status = status;
106	DTRACE_SMB2_DONE(op__ChangeNotify, smb_request_t *, sr);
107
108	if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_SUCCESS) {
109		oBufLength = sr->raw_data.chain_offset;
110		(void) smb_mbc_encodef(
111		    &sr->reply, "wwlC",
112		    9,	/* StructSize */	/* w */
113		    DATA_OFF,			/* w */
114		    oBufLength,			/* l */
115		    &sr->raw_data);		/* C */
116	} else {
117		smb2sr_put_error(sr, status);
118	}
119
120	return (SDRC_SUCCESS);
121}
122
123/*
124 * This is called via taskq_dispatch in smb_notify.c
125 * to finish up an NT transact notify change request.
126 * Build an SMB2 Change Notify reply and send it.
127 */
128void
129smb2_change_notify_finish(void *arg)
130{
131	smb_request_t *sr = arg;
132	smb_disp_stats_t *sds;
133	uint32_t status;
134	uint32_t oBufLength;
135
136	SMB_REQ_VALID(sr);
137
138	/*
139	 * Common part of notify, puts data in sr->raw_data
140	 */
141	status = smb_notify_act3(sr);
142
143	/*
144	 * The prior thread returned SDRC_SR_KEPT and skiped
145	 * the dtrace DONE probe, so fire that here.
146	 */
147	sr->smb2_status = status;
148	DTRACE_SMB2_DONE(op__ChangeNotify, smb_request_t *, sr);
149
150	if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_SUCCESS) {
151		oBufLength = sr->raw_data.chain_offset;
152		(void) smb_mbc_encodef(
153		    &sr->reply, "wwlC",
154		    9,	/* StructSize */	/* w */
155		    DATA_OFF,			/* w */
156		    oBufLength,			/* l */
157		    &sr->raw_data);		/* C */
158	} else {
159		smb2sr_put_error(sr, status);
160	}
161
162	/*
163	 * Record some statistics: (just tx bytes here)
164	 */
165	sds = &sr->session->s_server->sv_disp_stats2[SMB2_CHANGE_NOTIFY];
166	atomic_add_64(&sds->sdt_txb, (int64_t)(sr->reply.chain_offset));
167
168	/*
169	 * Put (overwrite) the final SMB2 header,
170	 * sign, send.
171	 */
172	(void) smb2_encode_header(sr, B_TRUE);
173	if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
174		smb2_sign_reply(sr);
175	smb2_send_reply(sr);
176
177	mutex_enter(&sr->sr_mutex);
178	sr->sr_state = SMB_REQ_STATE_COMPLETED;
179	mutex_exit(&sr->sr_mutex);
180
181	smb_request_free(sr);
182}
183