s3: smbd: Add some DEVELOPER-only code to panic if the destructor for an aio_lnk...
[Samba.git] / source3 / libsmb / clisymlink.c
blob5cf2646961f0e63b6d67d6ee87b7edecef259572
1 /*
2 * Unix SMB/CIFS implementation.
3 * Client implementation of setting symlinks using reparse points
4 * Copyright (C) Volker Lendecke 2011
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.
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.
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/>.
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 #include "libcli/smb/reparse.h"
31 #include "libcli/smb/reparse_symlink.h"
33 struct cli_symlink_state {
34 struct tevent_context *ev;
35 struct cli_state *cli;
36 const char *link_target;
37 const char *newpath;
38 uint32_t flags;
40 uint16_t fnum;
41 DATA_BLOB in;
43 NTSTATUS set_reparse_status;
46 static void cli_symlink_create_done(struct tevent_req *subreq);
47 static void cli_symlink_set_reparse_done(struct tevent_req *subreq);
48 static void cli_symlink_delete_on_close_done(struct tevent_req *subreq);
49 static void cli_symlink_close_done(struct tevent_req *subreq);
51 struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx,
52 struct tevent_context *ev,
53 struct cli_state *cli,
54 const char *link_target,
55 const char *newpath,
56 uint32_t flags)
58 struct tevent_req *req, *subreq;
59 struct cli_symlink_state *state;
61 req = tevent_req_create(mem_ctx, &state, struct cli_symlink_state);
62 if (req == NULL) {
63 return NULL;
65 state->ev = ev;
66 state->cli = cli;
67 state->link_target = link_target;
68 state->newpath = newpath;
69 state->flags = flags;
71 subreq = cli_ntcreate_send(
72 state, ev, cli, state->newpath, 0,
73 SYNCHRONIZE_ACCESS|DELETE_ACCESS|
74 FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
75 FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE, FILE_CREATE,
76 FILE_OPEN_REPARSE_POINT|FILE_SYNCHRONOUS_IO_NONALERT|
77 FILE_NON_DIRECTORY_FILE,
78 SMB2_IMPERSONATION_IMPERSONATION, 0);
79 if (tevent_req_nomem(subreq, req)) {
80 return tevent_req_post(req, ev);
82 tevent_req_set_callback(subreq, cli_symlink_create_done, req);
83 return req;
86 static void cli_symlink_create_done(struct tevent_req *subreq)
88 struct tevent_req *req = tevent_req_callback_data(
89 subreq, struct tevent_req);
90 struct cli_symlink_state *state = tevent_req_data(
91 req, struct cli_symlink_state);
92 NTSTATUS status;
94 status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
95 TALLOC_FREE(subreq);
96 if (tevent_req_nterror(req, status)) {
97 return;
100 if (!symlink_reparse_buffer_marshall(
101 state->link_target, NULL, 0, state->flags, state,
102 &state->in.data, &state->in.length)) {
103 tevent_req_oom(req);
104 return;
107 subreq = cli_fsctl_send(
108 state,
109 state->ev,
110 state->cli,
111 state->fnum,
112 FSCTL_SET_REPARSE_POINT,
113 &state->in,
115 if (tevent_req_nomem(subreq, req)) {
116 return;
118 tevent_req_set_callback(subreq, cli_symlink_set_reparse_done, req);
121 static void cli_symlink_set_reparse_done(struct tevent_req *subreq)
123 struct tevent_req *req = tevent_req_callback_data(
124 subreq, struct tevent_req);
125 struct cli_symlink_state *state = tevent_req_data(
126 req, struct cli_symlink_state);
128 state->set_reparse_status = cli_fsctl_recv(subreq, NULL, NULL);
129 TALLOC_FREE(subreq);
131 if (NT_STATUS_IS_OK(state->set_reparse_status)) {
132 subreq = cli_close_send(state, state->ev, state->cli,
133 state->fnum);
134 if (tevent_req_nomem(subreq, req)) {
135 return;
137 tevent_req_set_callback(subreq, cli_symlink_close_done, req);
138 return;
140 subreq = cli_nt_delete_on_close_send(
141 state, state->ev, state->cli, state->fnum, true);
142 if (tevent_req_nomem(subreq, req)) {
143 return;
145 tevent_req_set_callback(subreq, cli_symlink_delete_on_close_done, req);
148 static void cli_symlink_delete_on_close_done(struct tevent_req *subreq)
150 struct tevent_req *req = tevent_req_callback_data(
151 subreq, struct tevent_req);
152 struct cli_symlink_state *state = tevent_req_data(
153 req, struct cli_symlink_state);
156 * Ignore status, we can't do much anyway in case of failure
159 (void)cli_nt_delete_on_close_recv(subreq);
160 TALLOC_FREE(subreq);
162 subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
163 if (tevent_req_nomem(subreq, req)) {
164 return;
166 tevent_req_set_callback(subreq, cli_symlink_close_done, req);
169 static void cli_symlink_close_done(struct tevent_req *subreq)
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 NTSTATUS status;
177 status = cli_close_recv(subreq);
178 TALLOC_FREE(subreq);
180 if (tevent_req_nterror(req, status)) {
181 return;
183 if (tevent_req_nterror(req, state->set_reparse_status)) {
184 return;
186 tevent_req_done(req);
189 NTSTATUS cli_symlink_recv(struct tevent_req *req)
191 return tevent_req_simple_recv_ntstatus(req);
194 NTSTATUS cli_symlink(struct cli_state *cli, const char *link_target,
195 const char *newname, uint32_t flags)
197 TALLOC_CTX *frame = talloc_stackframe();
198 struct tevent_context *ev;
199 struct tevent_req *req;
200 NTSTATUS status = NT_STATUS_NO_MEMORY;
202 if (smbXcli_conn_has_async_calls(cli->conn)) {
203 status = NT_STATUS_INVALID_PARAMETER;
204 goto fail;
206 ev = samba_tevent_context_init(frame);
207 if (ev == NULL) {
208 goto fail;
210 req = cli_symlink_send(frame, ev, cli, link_target, newname, flags);
211 if (req == NULL) {
212 goto fail;
214 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
215 goto fail;
217 status = cli_symlink_recv(req);
218 fail:
219 TALLOC_FREE(frame);
220 return status;
223 struct cli_get_reparse_data_state {
224 struct tevent_context *ev;
225 struct cli_state *cli;
226 uint16_t fnum;
228 NTSTATUS get_reparse_status;
229 uint8_t *data;
230 uint32_t datalen;
233 static void cli_get_reparse_data_opened(struct tevent_req *subreq);
234 static void cli_get_reparse_data_done(struct tevent_req *subreq);
235 static void cli_get_reparse_data_closed(struct tevent_req *subreq);
237 struct tevent_req *cli_get_reparse_data_send(TALLOC_CTX *mem_ctx,
238 struct tevent_context *ev,
239 struct cli_state *cli,
240 const char *fname)
242 struct tevent_req *req = NULL, *subreq = NULL;
243 struct cli_get_reparse_data_state *state = NULL;
245 req = tevent_req_create(mem_ctx,
246 &state,
247 struct cli_get_reparse_data_state);
248 if (req == NULL) {
249 return NULL;
251 state->ev = ev;
252 state->cli = cli;
254 subreq = cli_ntcreate_send(state,
256 cli,
257 fname,
259 FILE_READ_ATTRIBUTES | FILE_READ_EA,
261 FILE_SHARE_READ | FILE_SHARE_WRITE |
262 FILE_SHARE_DELETE,
263 FILE_OPEN,
264 FILE_OPEN_REPARSE_POINT,
265 SMB2_IMPERSONATION_IMPERSONATION,
267 if (tevent_req_nomem(subreq, req)) {
268 return tevent_req_post(req, ev);
270 tevent_req_set_callback(subreq, cli_get_reparse_data_opened, req);
271 return req;
274 static void cli_get_reparse_data_opened(struct tevent_req *subreq)
276 struct tevent_req *req =
277 tevent_req_callback_data(subreq, struct tevent_req);
278 struct cli_get_reparse_data_state *state =
279 tevent_req_data(req, struct cli_get_reparse_data_state);
280 NTSTATUS status;
282 status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
283 TALLOC_FREE(subreq);
284 if (tevent_req_nterror(req, status)) {
285 return;
288 subreq = cli_fsctl_send(state,
289 state->ev,
290 state->cli,
291 state->fnum,
292 FSCTL_GET_REPARSE_POINT,
293 NULL,
294 65536);
296 if (tevent_req_nomem(subreq, req)) {
297 return;
299 tevent_req_set_callback(subreq, cli_get_reparse_data_done, req);
302 static void cli_get_reparse_data_done(struct tevent_req *subreq)
304 struct tevent_req *req =
305 tevent_req_callback_data(subreq, struct tevent_req);
306 struct cli_get_reparse_data_state *state =
307 tevent_req_data(req, struct cli_get_reparse_data_state);
308 DATA_BLOB out = {
309 .data = NULL,
312 state->get_reparse_status = cli_fsctl_recv(subreq, state, &out);
313 TALLOC_FREE(subreq);
315 if (NT_STATUS_IS_OK(state->get_reparse_status)) {
316 state->data = out.data;
317 state->datalen = out.length;
320 subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
321 if (tevent_req_nomem(subreq, req)) {
322 return;
324 tevent_req_set_callback(subreq, cli_get_reparse_data_closed, req);
327 static void cli_get_reparse_data_closed(struct tevent_req *subreq)
329 struct tevent_req *req =
330 tevent_req_callback_data(subreq, struct tevent_req);
331 struct cli_get_reparse_data_state *state =
332 tevent_req_data(req, struct cli_get_reparse_data_state);
333 NTSTATUS status;
335 status = cli_close_recv(subreq);
336 TALLOC_FREE(subreq);
337 if (tevent_req_nterror(req, status)) {
338 return;
340 if (tevent_req_nterror(req, state->get_reparse_status)) {
341 return;
343 tevent_req_done(req);
346 NTSTATUS cli_get_reparse_data_recv(struct tevent_req *req,
347 TALLOC_CTX *mem_ctx,
348 uint8_t **_data,
349 uint32_t *_datalen)
351 struct cli_get_reparse_data_state *state =
352 tevent_req_data(req, struct cli_get_reparse_data_state);
353 NTSTATUS status;
355 if (tevent_req_is_nterror(req, &status)) {
356 return status;
359 *_data = talloc_move(mem_ctx, &state->data);
360 *_datalen = state->datalen;
362 tevent_req_received(req);
364 return NT_STATUS_OK;
367 NTSTATUS cli_get_reparse_data(struct cli_state *cli,
368 const char *fname,
369 TALLOC_CTX *mem_ctx,
370 uint8_t **_data,
371 uint32_t *_datalen)
373 TALLOC_CTX *frame = talloc_stackframe();
374 struct tevent_context *ev;
375 struct tevent_req *req;
376 NTSTATUS status = NT_STATUS_NO_MEMORY;
378 if (smbXcli_conn_has_async_calls(cli->conn)) {
379 status = NT_STATUS_INVALID_PARAMETER;
380 goto fail;
382 ev = samba_tevent_context_init(frame);
383 if (ev == NULL) {
384 goto fail;
386 req = cli_get_reparse_data_send(frame, ev, cli, fname);
387 if (req == NULL) {
388 goto fail;
390 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
391 goto fail;
393 status = cli_get_reparse_data_recv(req, mem_ctx, _data, _datalen);
394 fail:
395 TALLOC_FREE(frame);
396 return status;
399 struct cli_readlink_state {
400 struct tevent_context *ev;
401 struct cli_state *cli;
402 uint16_t fnum;
404 uint16_t setup[4];
405 uint8_t *data;
406 uint32_t num_data;
407 char *target;
410 static void cli_readlink_posix1_done(struct tevent_req *subreq);
411 static void cli_readlink_got_reparse_data(struct tevent_req *subreq);
413 struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx,
414 struct tevent_context *ev,
415 struct cli_state *cli,
416 const char *fname)
418 struct tevent_req *req, *subreq;
419 struct cli_readlink_state *state;
421 req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state);
422 if (req == NULL) {
423 return NULL;
425 state->ev = ev;
426 state->cli = cli;
428 if (cli->requested_posix_capabilities != 0) {
430 * Only happens for negotiated SMB1 posix caps
432 subreq = cli_posix_readlink_send(state, ev, cli, fname);
433 if (tevent_req_nomem(subreq, req)) {
434 return tevent_req_post(req, ev);
436 tevent_req_set_callback(subreq, cli_readlink_posix1_done, req);
437 return req;
440 subreq = cli_get_reparse_data_send(state, ev, cli, fname);
441 if (tevent_req_nomem(subreq, req)) {
442 return tevent_req_post(req, ev);
444 tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req);
445 return req;
448 static void cli_readlink_posix1_done(struct tevent_req *subreq)
450 struct tevent_req *req = tevent_req_callback_data(
451 subreq, struct tevent_req);
452 struct cli_readlink_state *state = tevent_req_data(
453 req, struct cli_readlink_state);
454 NTSTATUS status;
456 status = cli_posix_readlink_recv(subreq, state, &state->target);
457 TALLOC_FREE(subreq);
458 if (tevent_req_nterror(req, status)) {
459 return;
461 tevent_req_done(req);
464 static void cli_readlink_got_reparse_data(struct tevent_req *subreq)
466 struct tevent_req *req = tevent_req_callback_data(
467 subreq, struct tevent_req);
468 struct cli_readlink_state *state = tevent_req_data(
469 req, struct cli_readlink_state);
470 NTSTATUS status;
472 status = cli_get_reparse_data_recv(subreq,
473 state,
474 &state->data,
475 &state->num_data);
476 TALLOC_FREE(subreq);
477 if (tevent_req_nterror(req, status)) {
478 return;
480 tevent_req_done(req);
483 NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
484 char **psubstitute_name, char **pprint_name,
485 uint32_t *pflags)
487 struct cli_readlink_state *state = tevent_req_data(
488 req, struct cli_readlink_state);
489 struct reparse_data_buffer buf = {
490 .tag = 0,
492 NTSTATUS status;
494 if (tevent_req_is_nterror(req, &status)) {
495 return status;
498 if (state->target != NULL) {
500 * SMB1 posix version
502 if (psubstitute_name != NULL) {
503 *psubstitute_name = talloc_move(
504 mem_ctx, &state->target);
506 if (pprint_name != NULL) {
507 *pprint_name = NULL;
509 if (pflags != NULL) {
510 *pflags = 0;
512 return NT_STATUS_OK;
515 status = reparse_data_buffer_parse(state,
516 &buf,
517 state->data,
518 state->num_data);
519 if (!NT_STATUS_IS_OK(status)) {
520 return NT_STATUS_INVALID_NETWORK_RESPONSE;
522 if (buf.tag != IO_REPARSE_TAG_SYMLINK) {
523 return NT_STATUS_INVALID_NETWORK_RESPONSE;
526 if (psubstitute_name != NULL) {
527 *psubstitute_name =
528 talloc_move(mem_ctx, &buf.parsed.lnk.substitute_name);
531 if (pprint_name != NULL) {
532 *pprint_name =
533 talloc_move(mem_ctx, &buf.parsed.lnk.print_name);
536 if (pflags != NULL) {
537 *pflags = buf.parsed.lnk.flags;
540 tevent_req_received(req);
542 return NT_STATUS_OK;
545 NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
546 TALLOC_CTX *mem_ctx, char **psubstitute_name,
547 char **pprint_name, uint32_t *pflags)
549 TALLOC_CTX *frame = talloc_stackframe();
550 struct tevent_context *ev;
551 struct tevent_req *req;
552 NTSTATUS status = NT_STATUS_NO_MEMORY;
554 if (smbXcli_conn_has_async_calls(cli->conn)) {
555 status = NT_STATUS_INVALID_PARAMETER;
556 goto fail;
558 ev = samba_tevent_context_init(frame);
559 if (ev == NULL) {
560 goto fail;
562 req = cli_readlink_send(frame, ev, cli, fname);
563 if (req == NULL) {
564 goto fail;
566 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
567 goto fail;
569 status = cli_readlink_recv(req, mem_ctx, psubstitute_name,
570 pprint_name, pflags);
571 fail:
572 TALLOC_FREE(frame);
573 return status;