]> git.samba.org - samba.git/blob - source3/smbd/smb2_flush.c
s3:utils: let smbstatus report anonymous signing/encryption explicitly
[samba.git] / source3 / smbd / smb2_flush.c
1 /*
2    Unix SMB/CIFS implementation.
3    Core SMB2 server
4
5    Copyright (C) Stefan Metzmacher 2009
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "smbd/smbd.h"
23 #include "smbd/globals.h"
24 #include "../libcli/smb/smb_common.h"
25 #include "../lib/util/tevent_ntstatus.h"
26 #include "libcli/security/security.h"
27
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_SMB2
30
31 static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx,
32                                                struct tevent_context *ev,
33                                                struct smbd_smb2_request *smb2req,
34                                                struct files_struct *fsp);
35 static NTSTATUS smbd_smb2_flush_recv(struct tevent_req *req);
36
37 static void smbd_smb2_request_flush_done(struct tevent_req *subreq);
38 NTSTATUS smbd_smb2_request_process_flush(struct smbd_smb2_request *req)
39 {
40         NTSTATUS status;
41         const uint8_t *inbody;
42         uint64_t in_file_id_persistent;
43         uint64_t in_file_id_volatile;
44         struct files_struct *in_fsp;
45         struct tevent_req *subreq;
46
47         status = smbd_smb2_request_verify_sizes(req, 0x18);
48         if (!NT_STATUS_IS_OK(status)) {
49                 return smbd_smb2_request_error(req, status);
50         }
51         inbody = SMBD_SMB2_IN_BODY_PTR(req);
52
53         in_file_id_persistent   = BVAL(inbody, 0x08);
54         in_file_id_volatile     = BVAL(inbody, 0x10);
55
56         in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
57         if (in_fsp == NULL) {
58                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
59         }
60
61         subreq = smbd_smb2_flush_send(req, req->sconn->ev_ctx,
62                                       req, in_fsp);
63         if (subreq == NULL) {
64                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
65         }
66         tevent_req_set_callback(subreq, smbd_smb2_request_flush_done, req);
67
68         return smbd_smb2_request_pending_queue(req, subreq, 500);
69 }
70
71 static void smbd_smb2_request_flush_done(struct tevent_req *subreq)
72 {
73         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
74                                         struct smbd_smb2_request);
75         DATA_BLOB outbody;
76         NTSTATUS status;
77         NTSTATUS error; /* transport error */
78
79         status = smbd_smb2_flush_recv(subreq);
80         TALLOC_FREE(subreq);
81         if (!NT_STATUS_IS_OK(status)) {
82                 error = smbd_smb2_request_error(req, status);
83                 if (!NT_STATUS_IS_OK(error)) {
84                         smbd_server_connection_terminate(req->xconn,
85                                                          nt_errstr(error));
86                         return;
87                 }
88                 return;
89         }
90
91         outbody = smbd_smb2_generate_outbody(req, 0x04);
92         if (outbody.data == NULL) {
93                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
94                 if (!NT_STATUS_IS_OK(error)) {
95                         smbd_server_connection_terminate(req->xconn,
96                                                          nt_errstr(error));
97                         return;
98                 }
99                 return;
100         }
101
102         SSVAL(outbody.data, 0x00, 0x04);        /* struct size */
103         SSVAL(outbody.data, 0x02, 0);           /* reserved */
104
105         error = smbd_smb2_request_done(req, outbody, NULL);
106         if (!NT_STATUS_IS_OK(error)) {
107                 smbd_server_connection_terminate(req->xconn,
108                                                  nt_errstr(error));
109                 return;
110         }
111 }
112
113 struct smbd_smb2_flush_state {
114         struct smbd_smb2_request *smb2req;
115         struct files_struct *fsp;
116 };
117
118 static void smbd_smb2_flush_done(struct tevent_req *subreq);
119
120 static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx,
121                                                struct tevent_context *ev,
122                                                struct smbd_smb2_request *smb2req,
123                                                struct files_struct *fsp)
124 {
125         struct tevent_req *req;
126         struct tevent_req *subreq;
127         struct smbd_smb2_flush_state *state;
128         struct smb_request *smbreq;
129         bool is_compound = false;
130         bool is_last_in_compound = false;
131         NTSTATUS status;
132
133         req = tevent_req_create(mem_ctx, &state,
134                                 struct smbd_smb2_flush_state);
135         if (req == NULL) {
136                 return NULL;
137         }
138         state->smb2req = smb2req;
139         state->fsp = fsp;
140
141         DEBUG(10,("smbd_smb2_flush: %s - %s\n",
142                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
143
144         smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
145         if (tevent_req_nomem(smbreq, req)) {
146                 return tevent_req_post(req, ev);
147         }
148
149         if (IS_IPC(smbreq->conn)) {
150                 tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
151                 return tevent_req_post(req, ev);
152         }
153
154         status = check_any_access_fsp(fsp, FILE_WRITE_DATA|FILE_APPEND_DATA);
155         if (!NT_STATUS_IS_OK(status)) {
156                 bool allow_dir_flush = false;
157                 uint32_t flush_access = FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY;
158
159                 if (!fsp->fsp_flags.is_directory) {
160                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
161                         return tevent_req_post(req, ev);
162                 }
163
164                 /*
165                  * Directories are not writable in the conventional
166                  * sense, but if opened with *either*
167                  * FILE_ADD_FILE or FILE_ADD_SUBDIRECTORY
168                  * they can be flushed.
169                  */
170
171                 status = check_any_access_fsp(fsp, flush_access);
172                 if (NT_STATUS_IS_OK(status)) {
173                         allow_dir_flush = true;
174                 }
175
176                 if (allow_dir_flush == false) {
177                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
178                         return tevent_req_post(req, ev);
179                 }
180         }
181
182         if (fsp_get_io_fd(fsp) == -1) {
183                 tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
184                 return tevent_req_post(req, ev);
185         }
186
187         if (!lp_strict_sync(SNUM(smbreq->conn))) {
188                 /*
189                  * No strict sync. Don't really do
190                  * anything here.
191                  */
192                 tevent_req_done(req);
193                 return tevent_req_post(req, ev);
194         }
195
196         subreq = SMB_VFS_FSYNC_SEND(state, ev, fsp);
197         if (tevent_req_nomem(subreq, req)) {
198                 return tevent_req_post(req, ev);
199         }
200
201         tevent_req_set_callback(subreq, smbd_smb2_flush_done, req);
202
203         is_compound = smbd_smb2_is_compound(smb2req);
204         is_last_in_compound = smbd_smb2_is_last_in_compound(smb2req);
205
206         if (is_compound && !is_last_in_compound) {
207                 /*
208                  * Can't go async if we're not the
209                  * last request in a compound request.
210                  * Cause this request to complete synchronously.
211                  */
212                 smb2_request_set_async_internal(state->smb2req, true);
213         }
214
215         /* Ensure any close request knows about this outstanding IO. */
216         if (!aio_add_req_to_fsp(fsp, req)) {
217                 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
218                 return tevent_req_post(req, ev);
219         }
220
221         return req;
222
223 }
224
225 static void smbd_smb2_flush_done(struct tevent_req *subreq)
226 {
227         struct tevent_req *req = tevent_req_callback_data(
228                 subreq, struct tevent_req);
229         struct smbd_smb2_flush_state *state = tevent_req_data(
230                 req, struct smbd_smb2_flush_state);
231         int ret;
232         struct vfs_aio_state vfs_aio_state;
233
234         ret = SMB_VFS_FSYNC_RECV(subreq, &vfs_aio_state);
235         TALLOC_FREE(subreq);
236         if (ret == -1) {
237                 tevent_req_nterror(req, map_nt_error_from_unix(vfs_aio_state.error));
238                 return;
239         }
240         if (state->fsp->fsp_flags.modified) {
241                 trigger_write_time_update_immediate(state->fsp);
242         }
243         tevent_req_done(req);
244 }
245
246 static NTSTATUS smbd_smb2_flush_recv(struct tevent_req *req)
247 {
248         NTSTATUS status;
249
250         if (tevent_req_is_nterror(req, &status)) {
251                 tevent_req_received(req);
252                 return status;
253         }
254
255         tevent_req_received(req);
256         return NT_STATUS_OK;
257 }