1 /*
2    Unix SMB/CIFS implementation.
3 
4    POSIX NTVFS backend - async request wait routines
5 
6    Copyright (C) Andrew Tridgell 2004
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #include "includes.h"
23 #include "lib/events/events.h"
24 #include "../lib/util/dlinklist.h"
25 #include "vfs_posix.h"
26 #include "smbd/service_stream.h"
27 #include "lib/messaging/irpc.h"
28 
29 /* the context for a single wait instance */
30 struct pvfs_wait {
31 	struct pvfs_wait *next, *prev;
32 	struct pvfs_state *pvfs;
33 	void (*handler)(void *, enum pvfs_wait_notice);
34 	void *private_data;
35 	int msg_type;
36 	struct imessaging_context *msg_ctx;
37 	struct tevent_context *ev;
38 	struct ntvfs_request *req;
39 	enum pvfs_wait_notice reason;
40 };
41 
42 /*
43   called from the ntvfs layer when we have requested setup of an async
44   call.  this ensures that async calls runs with the right state of
45   previous ntvfs handlers in the chain (such as security context)
46 */
pvfs_async_setup(struct ntvfs_module_context * ntvfs,struct ntvfs_request * req,void * private_data)47 NTSTATUS pvfs_async_setup(struct ntvfs_module_context *ntvfs,
48 			  struct ntvfs_request *req, void *private_data)
49 {
50 	struct pvfs_wait *pwait = talloc_get_type(private_data,
51 						  struct pvfs_wait);
52 	pwait->handler(pwait->private_data, pwait->reason);
53 	return NT_STATUS_OK;
54 }
55 
56 /*
57   receive a completion message for a wait
58 */
pvfs_wait_dispatch(struct imessaging_context * msg,void * private_data,uint32_t msg_type,struct server_id src,size_t num_fds,int * fds,DATA_BLOB * data)59 static void pvfs_wait_dispatch(struct imessaging_context *msg,
60 			       void *private_data,
61 			       uint32_t msg_type,
62 			       struct server_id src,
63 			       size_t num_fds,
64 			       int *fds,
65 			       DATA_BLOB *data)
66 {
67 	struct pvfs_wait *pwait = talloc_get_type(private_data,
68 						  struct pvfs_wait);
69 	struct ntvfs_request *req;
70 	void *p = NULL;
71 
72 	if (num_fds != 0) {
73 		DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
74 		return;
75 	}
76 
77 	/* we need to check that this one is for us. See
78 	   imessaging_send_ptr() for the other side of this.
79 	 */
80 	if (data->length == sizeof(void *)) {
81 		void **pp;
82 		pp = (void **)data->data;
83 		p = *pp;
84 	}
85 	if (p == NULL || p != pwait->private_data) {
86 		return;
87 	}
88 
89 	pwait->reason = PVFS_WAIT_EVENT;
90 
91 	/* the extra reference here is to ensure that the req
92 	   structure is not destroyed when the async request reply is
93 	   sent, which would cause problems with the other ntvfs
94 	   modules above us */
95 	req = talloc_reference(msg, pwait->req);
96 	ntvfs_async_setup(pwait->req, pwait);
97 	talloc_unlink(msg, req);
98 }
99 
100 
101 /*
102   receive a timeout on a message wait
103 */
pvfs_wait_timeout(struct tevent_context * ev,struct tevent_timer * te,struct timeval t,void * private_data)104 static void pvfs_wait_timeout(struct tevent_context *ev,
105 			      struct tevent_timer *te, struct timeval t,
106 			      void *private_data)
107 {
108 	struct pvfs_wait *pwait = talloc_get_type(private_data,
109 						  struct pvfs_wait);
110 	struct ntvfs_request *req = pwait->req;
111 
112 	pwait->reason = PVFS_WAIT_TIMEOUT;
113 
114 	req = talloc_reference(ev, req);
115 	if (req != NULL) {
116 		ntvfs_async_setup(req, pwait);
117 		talloc_unlink(ev, req);
118 	}
119 }
120 
121 
122 /*
123   destroy a pending wait
124  */
pvfs_wait_destructor(struct pvfs_wait * pwait)125 static int pvfs_wait_destructor(struct pvfs_wait *pwait)
126 {
127 	if (pwait->msg_type != -1) {
128 		imessaging_deregister(pwait->msg_ctx, pwait->msg_type, pwait);
129 	}
130 	DLIST_REMOVE(pwait->pvfs->wait_list, pwait);
131 	return 0;
132 }
133 
134 /*
135   setup a request to wait on a message of type msg_type, with a
136   timeout (given as an expiry time)
137 
138   the return value is a handle. To stop waiting talloc_free this
139   handle.
140 
141   if msg_type == -1 then no message is registered, and it is assumed
142   that the caller handles any messaging setup needed
143 */
pvfs_wait_message(struct pvfs_state * pvfs,struct ntvfs_request * req,int msg_type,struct timeval end_time,void (* fn)(void *,enum pvfs_wait_notice),void * private_data)144 struct pvfs_wait *pvfs_wait_message(struct pvfs_state *pvfs,
145 				    struct ntvfs_request *req,
146 				    int msg_type,
147 				    struct timeval end_time,
148 				    void (*fn)(void *, enum pvfs_wait_notice),
149 				    void *private_data)
150 {
151 	struct pvfs_wait *pwait;
152 
153 	pwait = talloc(pvfs, struct pvfs_wait);
154 	if (pwait == NULL) {
155 		return NULL;
156 	}
157 
158 	pwait->private_data = private_data;
159 	pwait->handler = fn;
160 	pwait->msg_ctx = pvfs->ntvfs->ctx->msg_ctx;
161 	pwait->ev = pvfs->ntvfs->ctx->event_ctx;
162 	pwait->msg_type = msg_type;
163 	pwait->req = talloc_reference(pwait, req);
164 	pwait->pvfs = pvfs;
165 
166 	if (!timeval_is_zero(&end_time)) {
167 		/* setup a timer */
168 		tevent_add_timer(pwait->ev, pwait, end_time, pvfs_wait_timeout, pwait);
169 	}
170 
171 	/* register with the messaging subsystem for this message
172 	   type */
173 	if (msg_type != -1) {
174 		imessaging_register(pwait->msg_ctx,
175 				   pwait,
176 				   msg_type,
177 				   pvfs_wait_dispatch);
178 	}
179 
180 	/* tell the main smb server layer that we will be replying
181 	   asynchronously */
182 	req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
183 
184 	DLIST_ADD(pvfs->wait_list, pwait);
185 
186 	/* make sure we cleanup the timer and message handler */
187 	talloc_set_destructor(pwait, pvfs_wait_destructor);
188 
189 	return pwait;
190 }
191 
192 
193 /*
194   cancel an outstanding async request
195 */
pvfs_cancel(struct ntvfs_module_context * ntvfs,struct ntvfs_request * req)196 NTSTATUS pvfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
197 {
198 	struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
199 				  struct pvfs_state);
200 	struct pvfs_wait *pwait;
201 
202 	for (pwait=pvfs->wait_list;pwait;pwait=pwait->next) {
203 		if (pwait->req == req) {
204 			/* trigger a cancel on the request */
205 			pwait->reason = PVFS_WAIT_CANCEL;
206 			ntvfs_async_setup(pwait->req, pwait);
207 			return NT_STATUS_OK;
208 		}
209 	}
210 
211 	return NT_STATUS_DOS(ERRDOS, ERRcancelviolation);
212 }
213