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/>.
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"
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"
32 struct cli_create_reparse_point_state
{
33 struct tevent_context
*ev
;
34 struct cli_state
*cli
;
35 DATA_BLOB reparse_blob
;
37 NTSTATUS set_reparse_status
;
40 static void cli_create_reparse_point_opened(struct tevent_req
*subreq
);
41 static void cli_create_reparse_point_done(struct tevent_req
*subreq
);
42 static void cli_create_reparse_point_doc_done(struct tevent_req
*subreq
);
43 static void cli_create_reparse_point_closed(struct tevent_req
*subreq
);
45 struct tevent_req
*cli_create_reparse_point_send(TALLOC_CTX
*mem_ctx
,
46 struct tevent_context
*ev
,
47 struct cli_state
*cli
,
49 DATA_BLOB reparse_blob
)
51 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
52 struct cli_create_reparse_point_state
*state
= NULL
;
54 req
= tevent_req_create(mem_ctx
,
56 struct cli_create_reparse_point_state
);
62 state
->reparse_blob
= reparse_blob
;
65 * The create arguments were taken from a Windows->Windows
66 * symlink create call.
68 subreq
= cli_ntcreate_send(
74 SYNCHRONIZE_ACCESS
| DELETE_ACCESS
| FILE_READ_ATTRIBUTES
|
75 FILE_WRITE_ATTRIBUTES
,
76 FILE_ATTRIBUTE_NORMAL
,
79 FILE_OPEN_REPARSE_POINT
| FILE_SYNCHRONOUS_IO_NONALERT
|
80 FILE_NON_DIRECTORY_FILE
,
81 SMB2_IMPERSONATION_IMPERSONATION
,
83 if (tevent_req_nomem(subreq
, req
)) {
84 return tevent_req_post(req
, ev
);
86 tevent_req_set_callback(subreq
, cli_create_reparse_point_opened
, req
);
90 static void cli_create_reparse_point_opened(struct tevent_req
*subreq
)
92 struct tevent_req
*req
=
93 tevent_req_callback_data(subreq
, struct tevent_req
);
94 struct cli_create_reparse_point_state
*state
=
95 tevent_req_data(req
, struct cli_create_reparse_point_state
);
98 status
= cli_ntcreate_recv(subreq
, &state
->fnum
, NULL
);
100 if (tevent_req_nterror(req
, status
)) {
104 subreq
= cli_fsctl_send(state
,
108 FSCTL_SET_REPARSE_POINT
,
109 &state
->reparse_blob
,
111 if (tevent_req_nomem(subreq
, req
)) {
114 tevent_req_set_callback(subreq
, cli_create_reparse_point_done
, req
);
117 static void cli_create_reparse_point_done(struct tevent_req
*subreq
)
119 struct tevent_req
*req
= tevent_req_callback_data(
120 subreq
, struct tevent_req
);
121 struct cli_create_reparse_point_state
*state
=
122 tevent_req_data(req
, struct cli_create_reparse_point_state
);
124 state
->set_reparse_status
= cli_fsctl_recv(subreq
, NULL
, NULL
);
127 if (NT_STATUS_IS_OK(state
->set_reparse_status
)) {
128 subreq
= cli_close_send(state
,
133 if (tevent_req_nomem(subreq
, req
)) {
136 tevent_req_set_callback(subreq
,
137 cli_create_reparse_point_closed
,
141 subreq
= cli_nt_delete_on_close_send(
142 state
, state
->ev
, state
->cli
, state
->fnum
, true);
143 if (tevent_req_nomem(subreq
, req
)) {
146 tevent_req_set_callback(subreq
,
147 cli_create_reparse_point_doc_done
,
151 static void cli_create_reparse_point_doc_done(struct tevent_req
*subreq
)
153 struct tevent_req
*req
= tevent_req_callback_data(
154 subreq
, struct tevent_req
);
155 struct cli_create_reparse_point_state
*state
=
156 tevent_req_data(req
, struct cli_create_reparse_point_state
);
159 * Ignore status, we can't do much anyway in case of failure
162 (void)cli_nt_delete_on_close_recv(subreq
);
165 subreq
= cli_close_send(state
, state
->ev
, state
->cli
, state
->fnum
, 0);
166 if (tevent_req_nomem(subreq
, req
)) {
169 tevent_req_set_callback(subreq
, cli_create_reparse_point_closed
, req
);
172 static void cli_create_reparse_point_closed(struct tevent_req
*subreq
)
174 struct tevent_req
*req
= tevent_req_callback_data(
175 subreq
, struct tevent_req
);
176 struct cli_create_reparse_point_state
*state
=
177 tevent_req_data(req
, struct cli_create_reparse_point_state
);
180 status
= cli_close_recv(subreq
);
183 if (tevent_req_nterror(req
, status
)) {
186 if (tevent_req_nterror(req
, state
->set_reparse_status
)) {
189 tevent_req_done(req
);
192 NTSTATUS
cli_create_reparse_point_recv(struct tevent_req
*req
)
194 return tevent_req_simple_recv_ntstatus(req
);
197 struct cli_symlink_state
{
201 static void cli_symlink_done(struct tevent_req
*subreq
);
203 struct tevent_req
*cli_symlink_send(TALLOC_CTX
*mem_ctx
,
204 struct tevent_context
*ev
,
205 struct cli_state
*cli
,
206 const char *link_target
,
210 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
211 struct cli_symlink_state
*state
= NULL
;
212 struct reparse_data_buffer reparse_buf
= {
213 .tag
= IO_REPARSE_TAG_SYMLINK
,
214 .parsed
.lnk
.substitute_name
=
215 discard_const_p(char, link_target
),
216 .parsed
.lnk
.print_name
= discard_const_p(char, link_target
),
217 .parsed
.lnk
.flags
= flags
,
222 req
= tevent_req_create(mem_ctx
, &state
, struct cli_symlink_state
);
227 buflen
= reparse_data_buffer_marshall(&reparse_buf
, NULL
, 0);
230 return tevent_req_post(req
, ev
);
233 buf
= talloc_array(state
, uint8_t, buflen
);
234 if (tevent_req_nomem(buf
, req
)) {
235 return tevent_req_post(req
, ev
);
238 buflen
= reparse_data_buffer_marshall(&reparse_buf
, buf
, buflen
);
239 if (buflen
!= talloc_array_length(buf
)) {
240 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
241 return tevent_req_post(req
, ev
);
244 subreq
= cli_create_reparse_point_send(state
,
252 if (tevent_req_nomem(subreq
, req
)) {
253 return tevent_req_post(req
, ev
);
255 tevent_req_set_callback(subreq
, cli_symlink_done
, req
);
259 static void cli_symlink_done(struct tevent_req
*subreq
)
261 NTSTATUS status
= cli_symlink_recv(subreq
);
262 tevent_req_simple_finish_ntstatus(subreq
, status
);
265 NTSTATUS
cli_symlink_recv(struct tevent_req
*req
)
267 return tevent_req_simple_recv_ntstatus(req
);
270 NTSTATUS
cli_symlink(struct cli_state
*cli
, const char *link_target
,
271 const char *newname
, uint32_t flags
)
273 TALLOC_CTX
*frame
= talloc_stackframe();
274 struct tevent_context
*ev
;
275 struct tevent_req
*req
;
276 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
278 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
279 status
= NT_STATUS_INVALID_PARAMETER
;
282 ev
= samba_tevent_context_init(frame
);
286 req
= cli_symlink_send(frame
, ev
, cli
, link_target
, newname
, flags
);
290 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
293 status
= cli_symlink_recv(req
);
299 struct cli_get_reparse_data_state
{
300 struct tevent_context
*ev
;
301 struct cli_state
*cli
;
304 NTSTATUS get_reparse_status
;
309 static void cli_get_reparse_data_opened(struct tevent_req
*subreq
);
310 static void cli_get_reparse_data_done(struct tevent_req
*subreq
);
311 static void cli_get_reparse_data_closed(struct tevent_req
*subreq
);
313 struct tevent_req
*cli_get_reparse_data_send(TALLOC_CTX
*mem_ctx
,
314 struct tevent_context
*ev
,
315 struct cli_state
*cli
,
318 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
319 struct cli_get_reparse_data_state
*state
= NULL
;
321 req
= tevent_req_create(mem_ctx
,
323 struct cli_get_reparse_data_state
);
330 subreq
= cli_ntcreate_send(state
,
335 FILE_READ_ATTRIBUTES
| FILE_READ_EA
,
337 FILE_SHARE_READ
| FILE_SHARE_WRITE
|
340 FILE_OPEN_REPARSE_POINT
,
341 SMB2_IMPERSONATION_IMPERSONATION
,
343 if (tevent_req_nomem(subreq
, req
)) {
344 return tevent_req_post(req
, ev
);
346 tevent_req_set_callback(subreq
, cli_get_reparse_data_opened
, req
);
350 static void cli_get_reparse_data_opened(struct tevent_req
*subreq
)
352 struct tevent_req
*req
=
353 tevent_req_callback_data(subreq
, struct tevent_req
);
354 struct cli_get_reparse_data_state
*state
=
355 tevent_req_data(req
, struct cli_get_reparse_data_state
);
358 status
= cli_ntcreate_recv(subreq
, &state
->fnum
, NULL
);
360 if (tevent_req_nterror(req
, status
)) {
364 subreq
= cli_fsctl_send(state
,
368 FSCTL_GET_REPARSE_POINT
,
372 if (tevent_req_nomem(subreq
, req
)) {
375 tevent_req_set_callback(subreq
, cli_get_reparse_data_done
, req
);
378 static void cli_get_reparse_data_done(struct tevent_req
*subreq
)
380 struct tevent_req
*req
=
381 tevent_req_callback_data(subreq
, struct tevent_req
);
382 struct cli_get_reparse_data_state
*state
=
383 tevent_req_data(req
, struct cli_get_reparse_data_state
);
388 state
->get_reparse_status
= cli_fsctl_recv(subreq
, state
, &out
);
391 if (NT_STATUS_IS_OK(state
->get_reparse_status
)) {
392 state
->data
= out
.data
;
393 state
->datalen
= out
.length
;
396 subreq
= cli_close_send(state
, state
->ev
, state
->cli
, state
->fnum
, 0);
397 if (tevent_req_nomem(subreq
, req
)) {
400 tevent_req_set_callback(subreq
, cli_get_reparse_data_closed
, req
);
403 static void cli_get_reparse_data_closed(struct tevent_req
*subreq
)
405 struct tevent_req
*req
=
406 tevent_req_callback_data(subreq
, struct tevent_req
);
407 struct cli_get_reparse_data_state
*state
=
408 tevent_req_data(req
, struct cli_get_reparse_data_state
);
411 status
= cli_close_recv(subreq
);
413 if (tevent_req_nterror(req
, status
)) {
416 if (tevent_req_nterror(req
, state
->get_reparse_status
)) {
419 tevent_req_done(req
);
422 NTSTATUS
cli_get_reparse_data_recv(struct tevent_req
*req
,
427 struct cli_get_reparse_data_state
*state
=
428 tevent_req_data(req
, struct cli_get_reparse_data_state
);
431 if (tevent_req_is_nterror(req
, &status
)) {
435 *_data
= talloc_move(mem_ctx
, &state
->data
);
436 *_datalen
= state
->datalen
;
438 tevent_req_received(req
);
443 NTSTATUS
cli_get_reparse_data(struct cli_state
*cli
,
449 TALLOC_CTX
*frame
= talloc_stackframe();
450 struct tevent_context
*ev
;
451 struct tevent_req
*req
;
452 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
454 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
455 status
= NT_STATUS_INVALID_PARAMETER
;
458 ev
= samba_tevent_context_init(frame
);
462 req
= cli_get_reparse_data_send(frame
, ev
, cli
, fname
);
466 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
469 status
= cli_get_reparse_data_recv(req
, mem_ctx
, _data
, _datalen
);
475 struct cli_readlink_state
{
476 struct tevent_context
*ev
;
477 struct cli_state
*cli
;
486 static void cli_readlink_posix1_done(struct tevent_req
*subreq
);
487 static void cli_readlink_got_reparse_data(struct tevent_req
*subreq
);
489 struct tevent_req
*cli_readlink_send(TALLOC_CTX
*mem_ctx
,
490 struct tevent_context
*ev
,
491 struct cli_state
*cli
,
494 struct tevent_req
*req
, *subreq
;
495 struct cli_readlink_state
*state
;
497 req
= tevent_req_create(mem_ctx
, &state
, struct cli_readlink_state
);
504 if (cli
->requested_posix_capabilities
!= 0) {
506 * Only happens for negotiated SMB1 posix caps
508 subreq
= cli_posix_readlink_send(state
, ev
, cli
, fname
);
509 if (tevent_req_nomem(subreq
, req
)) {
510 return tevent_req_post(req
, ev
);
512 tevent_req_set_callback(subreq
, cli_readlink_posix1_done
, req
);
516 subreq
= cli_get_reparse_data_send(state
, ev
, cli
, fname
);
517 if (tevent_req_nomem(subreq
, req
)) {
518 return tevent_req_post(req
, ev
);
520 tevent_req_set_callback(subreq
, cli_readlink_got_reparse_data
, req
);
524 static void cli_readlink_posix1_done(struct tevent_req
*subreq
)
526 struct tevent_req
*req
= tevent_req_callback_data(
527 subreq
, struct tevent_req
);
528 struct cli_readlink_state
*state
= tevent_req_data(
529 req
, struct cli_readlink_state
);
532 status
= cli_posix_readlink_recv(subreq
, state
, &state
->target
);
534 if (tevent_req_nterror(req
, status
)) {
537 tevent_req_done(req
);
540 static void cli_readlink_got_reparse_data(struct tevent_req
*subreq
)
542 struct tevent_req
*req
= tevent_req_callback_data(
543 subreq
, struct tevent_req
);
544 struct cli_readlink_state
*state
= tevent_req_data(
545 req
, struct cli_readlink_state
);
548 status
= cli_get_reparse_data_recv(subreq
,
553 if (tevent_req_nterror(req
, status
)) {
556 tevent_req_done(req
);
559 NTSTATUS
cli_readlink_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
560 char **psubstitute_name
, char **pprint_name
,
563 struct cli_readlink_state
*state
= tevent_req_data(
564 req
, struct cli_readlink_state
);
565 struct reparse_data_buffer buf
= {
570 if (tevent_req_is_nterror(req
, &status
)) {
574 if (state
->target
!= NULL
) {
578 if (psubstitute_name
!= NULL
) {
579 *psubstitute_name
= talloc_move(
580 mem_ctx
, &state
->target
);
582 if (pprint_name
!= NULL
) {
585 if (pflags
!= NULL
) {
591 status
= reparse_data_buffer_parse(state
,
595 if (!NT_STATUS_IS_OK(status
)) {
596 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
598 if (buf
.tag
!= IO_REPARSE_TAG_SYMLINK
) {
599 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
602 if (psubstitute_name
!= NULL
) {
604 talloc_move(mem_ctx
, &buf
.parsed
.lnk
.substitute_name
);
607 if (pprint_name
!= NULL
) {
609 talloc_move(mem_ctx
, &buf
.parsed
.lnk
.print_name
);
612 if (pflags
!= NULL
) {
613 *pflags
= buf
.parsed
.lnk
.flags
;
616 tevent_req_received(req
);
621 NTSTATUS
cli_readlink(struct cli_state
*cli
, const char *fname
,
622 TALLOC_CTX
*mem_ctx
, char **psubstitute_name
,
623 char **pprint_name
, uint32_t *pflags
)
625 TALLOC_CTX
*frame
= talloc_stackframe();
626 struct tevent_context
*ev
;
627 struct tevent_req
*req
;
628 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
630 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
631 status
= NT_STATUS_INVALID_PARAMETER
;
634 ev
= samba_tevent_context_init(frame
);
638 req
= cli_readlink_send(frame
, ev
, cli
, fname
);
642 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
645 status
= cli_readlink_recv(req
, mem_ctx
, psubstitute_name
,
646 pprint_name
, pflags
);