libnmb: Make nb_packet_read_recv return a talloc'ed pkt
[samba.git] / source3 / libsmb / clisymlink.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Client implementation of setting symlinks using reparse points
4  * Copyright (C) Volker Lendecke 2011
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include "system/filesys.h"
22 #include "libsmb/libsmb.h"
23 #include "../lib/util/tevent_ntstatus.h"
24 #include "async_smb.h"
25 #include "libsmb/clirap.h"
26 #include "trans2.h"
27 #include "libcli/security/secdesc.h"
28 #include "libcli/security/security.h"
29 #include "../libcli/smb/smbXcli_base.h"
30
31 struct cli_symlink_state {
32         struct tevent_context *ev;
33         struct cli_state *cli;
34         const char *link_target;
35         const char *newpath;
36         uint32_t flags;
37
38         uint16_t fnum;
39
40         uint16_t setup[4];
41         NTSTATUS set_reparse_status;
42 };
43
44 static void cli_symlink_create_done(struct tevent_req *subreq);
45 static void cli_symlink_set_reparse_done(struct tevent_req *subreq);
46 static void cli_symlink_delete_on_close_done(struct tevent_req *subreq);
47 static void cli_symlink_close_done(struct tevent_req *subreq);
48
49 struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx,
50                                     struct tevent_context *ev,
51                                     struct cli_state *cli,
52                                     const char *link_target,
53                                     const char *newpath,
54                                     uint32_t flags)
55 {
56         struct tevent_req *req, *subreq;
57         struct cli_symlink_state *state;
58
59         req = tevent_req_create(mem_ctx, &state, struct cli_symlink_state);
60         if (req == NULL) {
61                 return NULL;
62         }
63         state->ev = ev;
64         state->cli = cli;
65         state->link_target = link_target;
66         state->newpath = newpath;
67         state->flags = flags;
68
69         subreq = cli_ntcreate_send(
70                 state, ev, cli, state->newpath, 0,
71                 SYNCHRONIZE_ACCESS|DELETE_ACCESS|
72                 FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
73                 FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE, FILE_CREATE,
74                 FILE_OPEN_REPARSE_POINT|FILE_SYNCHRONOUS_IO_NONALERT|
75                 FILE_NON_DIRECTORY_FILE, 0);
76         if (tevent_req_nomem(subreq, req)) {
77                 return tevent_req_post(req, ev);
78         }
79         tevent_req_set_callback(subreq, cli_symlink_create_done, req);
80         return req;
81 }
82
83 static void cli_symlink_create_done(struct tevent_req *subreq)
84 {
85         struct tevent_req *req = tevent_req_callback_data(
86                 subreq, struct tevent_req);
87         struct cli_symlink_state *state = tevent_req_data(
88                 req, struct cli_symlink_state);
89         DATA_BLOB data;
90         NTSTATUS status;
91
92         status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
93         TALLOC_FREE(subreq);
94         if (tevent_req_nterror(req, status)) {
95                 return;
96         }
97
98         if (!symlink_reparse_buffer_marshall(
99                     state->link_target, NULL, state->flags, state,
100                     &data.data, &data.length)) {
101                 tevent_req_oom(req);
102                 return;
103         }
104
105         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
106                 subreq = cli_smb2_set_reparse_point_fnum_send(state,
107                                                 state->ev,
108                                                 state->cli,
109                                                 state->fnum,
110                                                 data);
111         } else {
112                 SIVAL(state->setup, 0, FSCTL_SET_REPARSE_POINT);
113                 SSVAL(state->setup, 4, state->fnum);
114                 SCVAL(state->setup, 6, 1); /* IsFcntl */
115                 SCVAL(state->setup, 7, 0); /* IsFlags */
116
117
118                 subreq = cli_trans_send(state, state->ev, state->cli, 0,
119                                 SMBnttrans,
120                                 NULL, -1, /* name, fid */
121                                 NT_TRANSACT_IOCTL, 0,
122                                 state->setup, 4, 0, /* setup */
123                                 NULL, 0, 0,         /* param */
124                                 data.data, data.length, 0); /* data */
125         }
126
127         if (tevent_req_nomem(subreq, req)) {
128                 return;
129         }
130         tevent_req_set_callback(subreq, cli_symlink_set_reparse_done, req);
131 }
132
133 static void cli_symlink_set_reparse_done(struct tevent_req *subreq)
134 {
135         struct tevent_req *req = tevent_req_callback_data(
136                 subreq, struct tevent_req);
137         struct cli_symlink_state *state = tevent_req_data(
138                 req, struct cli_symlink_state);
139
140         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
141                 state->set_reparse_status =
142                         cli_smb2_set_reparse_point_fnum_recv(subreq);
143         } else {
144                 state->set_reparse_status = cli_trans_recv(
145                         subreq, NULL, NULL,
146                         NULL, 0, NULL,  /* rsetup */
147                         NULL, 0, NULL,  /* rparam */
148                         NULL, 0, NULL); /* rdata */
149         }
150         TALLOC_FREE(subreq);
151
152         if (NT_STATUS_IS_OK(state->set_reparse_status)) {
153                 subreq = cli_close_send(state, state->ev, state->cli,
154                                         state->fnum);
155                 if (tevent_req_nomem(subreq, req)) {
156                         return;
157                 }
158                 tevent_req_set_callback(subreq, cli_symlink_close_done, req);
159                 return;
160         }
161         subreq = cli_nt_delete_on_close_send(
162                 state, state->ev, state->cli, state->fnum, true);
163         if (tevent_req_nomem(subreq, req)) {
164                 return;
165         }
166         tevent_req_set_callback(subreq, cli_symlink_delete_on_close_done, req);
167 }
168
169 static void cli_symlink_delete_on_close_done(struct tevent_req *subreq)
170 {
171         struct tevent_req *req = tevent_req_callback_data(
172                 subreq, struct tevent_req);
173         struct cli_symlink_state *state = tevent_req_data(
174                 req, struct cli_symlink_state);
175
176         /*
177          * Ignore status, we can't do much anyway in case of failure
178          */
179
180         (void)cli_nt_delete_on_close_recv(subreq);
181         TALLOC_FREE(subreq);
182
183         subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
184         if (tevent_req_nomem(subreq, req)) {
185                 return;
186         }
187         tevent_req_set_callback(subreq, cli_symlink_close_done, req);
188 }
189
190 static void cli_symlink_close_done(struct tevent_req *subreq)
191 {
192         struct tevent_req *req = tevent_req_callback_data(
193                 subreq, struct tevent_req);
194         struct cli_symlink_state *state = tevent_req_data(
195                 req, struct cli_symlink_state);
196         NTSTATUS status;
197
198         status = cli_close_recv(subreq);
199         TALLOC_FREE(subreq);
200
201         if (tevent_req_nterror(req, status)) {
202                 return;
203         }
204         if (tevent_req_nterror(req, state->set_reparse_status)) {
205                 return;
206         }
207         tevent_req_done(req);
208 }
209
210 NTSTATUS cli_symlink_recv(struct tevent_req *req)
211 {
212         return tevent_req_simple_recv_ntstatus(req);
213 }
214
215 NTSTATUS cli_symlink(struct cli_state *cli, const char *link_target,
216                      const char *newname, uint32_t flags)
217 {
218         TALLOC_CTX *frame = talloc_stackframe();
219         struct tevent_context *ev;
220         struct tevent_req *req;
221         NTSTATUS status = NT_STATUS_NO_MEMORY;
222
223         if (smbXcli_conn_has_async_calls(cli->conn)) {
224                 status = NT_STATUS_INVALID_PARAMETER;
225                 goto fail;
226         }
227         ev = samba_tevent_context_init(frame);
228         if (ev == NULL) {
229                 goto fail;
230         }
231         req = cli_symlink_send(frame, ev, cli, link_target, newname, flags);
232         if (req == NULL) {
233                 goto fail;
234         }
235         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
236                 goto fail;
237         }
238         status = cli_symlink_recv(req);
239  fail:
240         TALLOC_FREE(frame);
241         return status;
242 }
243
244 struct cli_readlink_state {
245         struct tevent_context *ev;
246         struct cli_state *cli;
247         uint16_t fnum;
248
249         uint16_t setup[4];
250         NTSTATUS get_reparse_status;
251         uint8_t *data;
252         uint32_t num_data;
253 };
254
255 static void cli_readlink_opened(struct tevent_req *subreq);
256 static void cli_readlink_got_reparse_data(struct tevent_req *subreq);
257 static void cli_readlink_closed(struct tevent_req *subreq);
258
259 struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx,
260                                      struct tevent_context *ev,
261                                      struct cli_state *cli,
262                                      const char *fname)
263 {
264         struct tevent_req *req, *subreq;
265         struct cli_readlink_state *state;
266
267         req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state);
268         if (req == NULL) {
269                 return NULL;
270         }
271         state->ev = ev;
272         state->cli = cli;
273
274         subreq = cli_ntcreate_send(
275                 state, ev, cli, fname, 0, FILE_READ_ATTRIBUTES | FILE_READ_EA,
276                 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
277                 FILE_OPEN, FILE_OPEN_REPARSE_POINT, 0);
278         if (tevent_req_nomem(subreq, req)) {
279                 return tevent_req_post(req, ev);
280         }
281         tevent_req_set_callback(subreq, cli_readlink_opened, req);
282         return req;
283 }
284
285 static void cli_readlink_opened(struct tevent_req *subreq)
286 {
287         struct tevent_req *req = tevent_req_callback_data(
288                 subreq, struct tevent_req);
289         struct cli_readlink_state *state = tevent_req_data(
290                 req, struct cli_readlink_state);
291         NTSTATUS status;
292
293         status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
294         TALLOC_FREE(subreq);
295         if (tevent_req_nterror(req, status)) {
296                 return;
297         }
298
299         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
300                 subreq = cli_smb2_get_reparse_point_fnum_send(state,
301                                                 state->ev,
302                                                 state->cli,
303                                                 state->fnum);
304         } else {
305                 SIVAL(state->setup, 0, FSCTL_GET_REPARSE_POINT);
306                 SSVAL(state->setup, 4, state->fnum);
307                 SCVAL(state->setup, 6, 1); /* IsFcntl */
308                 SCVAL(state->setup, 7, 0); /* IsFlags */
309
310                 subreq = cli_trans_send(state, state->ev, state->cli,
311                                 0, SMBnttrans,
312                                 NULL, -1, /* name, fid */
313                                 NT_TRANSACT_IOCTL, 0,
314                                 state->setup, 4, 0, /* setup */
315                                 NULL, 0, 0,         /* param */
316                                 NULL, 0, 16384); /* data */
317         }
318
319         if (tevent_req_nomem(subreq, req)) {
320                 return;
321         }
322         tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req);
323 }
324
325 static void cli_readlink_got_reparse_data(struct tevent_req *subreq)
326 {
327         struct tevent_req *req = tevent_req_callback_data(
328                 subreq, struct tevent_req);
329         struct cli_readlink_state *state = tevent_req_data(
330                 req, struct cli_readlink_state);
331
332         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
333                 DATA_BLOB recv_data;
334                 state->get_reparse_status =
335                         cli_smb2_get_reparse_point_fnum_recv(subreq,
336                                                         state,
337                                                         &recv_data);
338                 if (NT_STATUS_IS_OK(state->get_reparse_status)) {
339                         state->data = recv_data.data;
340                         state->num_data = recv_data.length;
341                 }
342         } else {
343                 state->get_reparse_status = cli_trans_recv(
344                         subreq, state, NULL,
345                         NULL, 0, NULL,  /* rsetup */
346                         NULL, 0, NULL,  /* rparam */
347                         &state->data, 20, &state->num_data); /* rdata */
348         }
349         TALLOC_FREE(subreq);
350
351         subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
352         if (tevent_req_nomem(subreq, req)) {
353                 return;
354         }
355         tevent_req_set_callback(subreq, cli_readlink_closed, req);
356 }
357
358 static void cli_readlink_closed(struct tevent_req *subreq)
359 {
360         struct tevent_req *req = tevent_req_callback_data(
361                 subreq, struct tevent_req);
362         NTSTATUS status;
363
364         status = cli_close_recv(subreq);
365         TALLOC_FREE(subreq);
366         if (tevent_req_nterror(req, status)) {
367                 return;
368         }
369         tevent_req_done(req);
370 }
371
372 NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
373                            char **psubstitute_name, char **pprint_name,
374                            uint32_t *pflags)
375 {
376         struct cli_readlink_state *state = tevent_req_data(
377                 req, struct cli_readlink_state);
378         NTSTATUS status;
379         char *substitute_name;
380         char *print_name;
381         uint32_t flags;
382
383         if (tevent_req_is_nterror(req, &status)) {
384                 return status;
385         }
386
387         if (!symlink_reparse_buffer_parse(state->data, state->num_data,
388                                           talloc_tos(), &substitute_name,
389                                           &print_name, &flags)) {
390                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
391         }
392
393         if (psubstitute_name != NULL) {
394                 *psubstitute_name = talloc_move(mem_ctx, &substitute_name);
395         }
396         TALLOC_FREE(substitute_name);
397
398         if (pprint_name != NULL) {
399                 *pprint_name = talloc_move(mem_ctx, &print_name);
400         }
401         TALLOC_FREE(print_name);
402
403         if (pflags != NULL) {
404                 *pflags = flags;
405         }
406         return NT_STATUS_OK;
407 }
408
409 NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
410                        TALLOC_CTX *mem_ctx, char **psubstitute_name,
411                       char **pprint_name, uint32_t *pflags)
412 {
413         TALLOC_CTX *frame = talloc_stackframe();
414         struct tevent_context *ev;
415         struct tevent_req *req;
416         NTSTATUS status = NT_STATUS_NO_MEMORY;
417
418         if (smbXcli_conn_has_async_calls(cli->conn)) {
419                 status = NT_STATUS_INVALID_PARAMETER;
420                 goto fail;
421         }
422         ev = samba_tevent_context_init(frame);
423         if (ev == NULL) {
424                 goto fail;
425         }
426         req = cli_readlink_send(frame, ev, cli, fname);
427         if (req == NULL) {
428                 goto fail;
429         }
430         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
431                 goto fail;
432         }
433         status = cli_readlink_recv(req, mem_ctx, psubstitute_name,
434                                    pprint_name, pflags);
435  fail:
436         TALLOC_FREE(frame);
437         return status;
438 }