Fix bug 6494 - Incorrect FileStatus returned in NT_CREATE_ANDX.
[ira/wip.git] / source3 / smbd / smb2_break.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/globals.h"
23 #include "../libcli/smb/smb_common.h"
24
25 static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
26                                                       struct tevent_context *ev,
27                                                       struct smbd_smb2_request *smb2req,
28                                                       uint8_t in_oplock_level,
29                                                       uint64_t in_file_id_volatile);
30 static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
31                                             uint8_t *out_oplock_level);
32
33 static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq);
34 NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
35 {
36         const uint8_t *inhdr;
37         const uint8_t *inbody;
38         int i = req->current_idx;
39         size_t expected_body_size = 0x18;
40         size_t body_size;
41         uint8_t in_oplock_level;
42         uint64_t in_file_id_persistent;
43         uint64_t in_file_id_volatile;
44         struct tevent_req *subreq;
45
46         inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
47         if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
48                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
49         }
50
51         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
52
53         body_size = SVAL(inbody, 0x00);
54         if (body_size != expected_body_size) {
55                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
56         }
57
58         in_oplock_level         = CVAL(inbody, 0x02);
59         /* 0x03 1 bytes reserved */
60         /* 0x04 4 bytes reserved */
61         in_file_id_persistent           = BVAL(inbody, 0x08);
62         in_file_id_volatile             = BVAL(inbody, 0x10);
63
64         if (req->compat_chain_fsp) {
65                 /* skip check */
66         } else if (in_file_id_persistent != 0) {
67                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
68         }
69
70         subreq = smbd_smb2_oplock_break_send(req,
71                                              req->sconn->smb2.event_ctx,
72                                              req,
73                                              in_oplock_level,
74                                              in_file_id_volatile);
75         if (subreq == NULL) {
76                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
77         }
78         tevent_req_set_callback(subreq, smbd_smb2_request_oplock_break_done, req);
79
80         return smbd_smb2_request_pending_queue(req, subreq);
81 }
82
83 static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq)
84 {
85         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
86                                         struct smbd_smb2_request);
87         const uint8_t *inbody;
88         int i = req->current_idx;
89         uint64_t in_file_id_persistent;
90         uint64_t in_file_id_volatile;
91         uint8_t out_oplock_level = 0;
92         DATA_BLOB outbody;
93         NTSTATUS status;
94         NTSTATUS error; /* transport error */
95
96         status = smbd_smb2_oplock_break_recv(subreq, &out_oplock_level);
97         TALLOC_FREE(subreq);
98         if (!NT_STATUS_IS_OK(status)) {
99                 error = smbd_smb2_request_error(req, status);
100                 if (!NT_STATUS_IS_OK(error)) {
101                         smbd_server_connection_terminate(req->sconn,
102                                                          nt_errstr(error));
103                         return;
104                 }
105                 return;
106         }
107
108         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
109
110         in_file_id_persistent   = BVAL(inbody, 0x08);
111         in_file_id_volatile     = BVAL(inbody, 0x10);
112
113         outbody = data_blob_talloc(req->out.vector, NULL, 0x18);
114         if (outbody.data == NULL) {
115                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
116                 if (!NT_STATUS_IS_OK(error)) {
117                         smbd_server_connection_terminate(req->sconn,
118                                                          nt_errstr(error));
119                         return;
120                 }
121                 return;
122         }
123
124         SSVAL(outbody.data, 0x00, 0x18);        /* struct size */
125         SCVAL(outbody.data, 0x02,
126               out_oplock_level);                /* oplock level */
127         SCVAL(outbody.data, 0x03, 0);           /* reserved */
128         SIVAL(outbody.data, 0x04, 0);           /* reserved */
129         SBVAL(outbody.data, 0x08,
130               in_file_id_persistent);           /* file id (persistent) */
131         SBVAL(outbody.data, 0x10,
132               in_file_id_volatile);             /* file id (volatile) */
133
134         error = smbd_smb2_request_done(req, outbody, NULL);
135         if (!NT_STATUS_IS_OK(error)) {
136                 smbd_server_connection_terminate(req->sconn,
137                                                  nt_errstr(error));
138                 return;
139         }
140 }
141
142 struct smbd_smb2_oplock_break_state {
143         struct smbd_smb2_request *smb2req;
144         uint8_t out_oplock_level;
145 };
146
147 static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
148                                                       struct tevent_context *ev,
149                                                       struct smbd_smb2_request *smb2req,
150                                                       uint8_t in_oplock_level,
151                                                       uint64_t in_file_id_volatile)
152 {
153         struct tevent_req *req;
154         struct smbd_smb2_oplock_break_state *state;
155         struct smb_request *smbreq;
156         connection_struct *conn = smb2req->tcon->compat_conn;
157         files_struct *fsp;
158
159         req = tevent_req_create(mem_ctx, &state,
160                                 struct smbd_smb2_oplock_break_state);
161         if (req == NULL) {
162                 return NULL;
163         }
164         state->smb2req = smb2req;
165         state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
166
167         DEBUG(10,("smbd_smb2_oplock_break_send: file_id[0x%016llX]\n",
168                   (unsigned long long)in_file_id_volatile));
169
170         smbreq = smbd_smb2_fake_smb_request(smb2req);
171         if (tevent_req_nomem(smbreq, req)) {
172                 return tevent_req_post(req, ev);
173         }
174
175         fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
176         if (fsp == NULL) {
177                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
178                 return tevent_req_post(req, ev);
179         }
180         if (conn != fsp->conn) {
181                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
182                 return tevent_req_post(req, ev);
183         }
184         if (smb2req->session->vuid != fsp->vuid) {
185                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
186                 return tevent_req_post(req, ev);
187         }
188
189         tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
190         return tevent_req_post(req, ev);
191 }
192
193 static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
194                                             uint8_t *out_oplock_level)
195 {
196         NTSTATUS status;
197         struct smbd_smb2_oplock_break_state *state =
198                 tevent_req_data(req,
199                 struct smbd_smb2_oplock_break_state);
200
201         if (tevent_req_is_nterror(req, &status)) {
202                 tevent_req_received(req);
203                 return status;
204         }
205
206         *out_oplock_level = state->out_oplock_level;
207
208         tevent_req_received(req);
209         return NT_STATUS_OK;
210 }