/src/samba/source3/libsmb/clisymlink.c
Line | Count | Source (jump to first uncovered line) |
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 "source3/include/client.h" |
23 | | #include "source3/libsmb/proto.h" |
24 | | #include "../lib/util/tevent_ntstatus.h" |
25 | | #include "async_smb.h" |
26 | | #include "libsmb/clirap.h" |
27 | | #include "trans2.h" |
28 | | #include "libcli/security/secdesc.h" |
29 | | #include "libcli/security/security.h" |
30 | | #include "../libcli/smb/smbXcli_base.h" |
31 | | #include "libcli/smb/reparse.h" |
32 | | |
33 | | struct cli_create_reparse_point_state { |
34 | | struct tevent_context *ev; |
35 | | struct cli_state *cli; |
36 | | DATA_BLOB reparse_blob; |
37 | | uint16_t fnum; |
38 | | NTSTATUS set_reparse_status; |
39 | | }; |
40 | | |
41 | | static void cli_create_reparse_point_opened(struct tevent_req *subreq); |
42 | | static void cli_create_reparse_point_done(struct tevent_req *subreq); |
43 | | static void cli_create_reparse_point_doc_done(struct tevent_req *subreq); |
44 | | static void cli_create_reparse_point_closed(struct tevent_req *subreq); |
45 | | |
46 | | struct tevent_req *cli_create_reparse_point_send(TALLOC_CTX *mem_ctx, |
47 | | struct tevent_context *ev, |
48 | | struct cli_state *cli, |
49 | | const char *fname, |
50 | | DATA_BLOB reparse_blob) |
51 | 0 | { |
52 | 0 | struct tevent_req *req = NULL, *subreq = NULL; |
53 | 0 | struct cli_create_reparse_point_state *state = NULL; |
54 | |
|
55 | 0 | req = tevent_req_create(mem_ctx, |
56 | 0 | &state, |
57 | 0 | struct cli_create_reparse_point_state); |
58 | 0 | if (req == NULL) { |
59 | 0 | return NULL; |
60 | 0 | } |
61 | 0 | state->ev = ev; |
62 | 0 | state->cli = cli; |
63 | 0 | state->reparse_blob = reparse_blob; |
64 | | |
65 | | /* |
66 | | * The create arguments were taken from a Windows->Windows |
67 | | * symlink create call. |
68 | | */ |
69 | 0 | subreq = cli_ntcreate_send( |
70 | 0 | state, |
71 | 0 | ev, |
72 | 0 | cli, |
73 | 0 | fname, |
74 | 0 | 0, |
75 | 0 | SYNCHRONIZE_ACCESS | DELETE_ACCESS | FILE_READ_ATTRIBUTES | |
76 | 0 | FILE_WRITE_ATTRIBUTES, |
77 | 0 | FILE_ATTRIBUTE_NORMAL, |
78 | 0 | FILE_SHARE_NONE, |
79 | 0 | FILE_CREATE, |
80 | 0 | FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT | |
81 | 0 | FILE_NON_DIRECTORY_FILE, |
82 | 0 | SMB2_IMPERSONATION_IMPERSONATION, |
83 | 0 | 0); |
84 | 0 | if (tevent_req_nomem(subreq, req)) { |
85 | 0 | return tevent_req_post(req, ev); |
86 | 0 | } |
87 | 0 | tevent_req_set_callback(subreq, cli_create_reparse_point_opened, req); |
88 | 0 | return req; |
89 | 0 | } |
90 | | |
91 | | static void cli_create_reparse_point_opened(struct tevent_req *subreq) |
92 | 0 | { |
93 | 0 | struct tevent_req *req = |
94 | 0 | tevent_req_callback_data(subreq, struct tevent_req); |
95 | 0 | struct cli_create_reparse_point_state *state = |
96 | 0 | tevent_req_data(req, struct cli_create_reparse_point_state); |
97 | 0 | NTSTATUS status; |
98 | |
|
99 | 0 | status = cli_ntcreate_recv(subreq, &state->fnum, NULL); |
100 | 0 | TALLOC_FREE(subreq); |
101 | 0 | if (tevent_req_nterror(req, status)) { |
102 | 0 | return; |
103 | 0 | } |
104 | | |
105 | 0 | subreq = cli_fsctl_send(state, |
106 | 0 | state->ev, |
107 | 0 | state->cli, |
108 | 0 | state->fnum, |
109 | 0 | FSCTL_SET_REPARSE_POINT, |
110 | 0 | &state->reparse_blob, |
111 | 0 | 0); |
112 | 0 | if (tevent_req_nomem(subreq, req)) { |
113 | 0 | return; |
114 | 0 | } |
115 | 0 | tevent_req_set_callback(subreq, cli_create_reparse_point_done, req); |
116 | 0 | } |
117 | | |
118 | | static void cli_create_reparse_point_done(struct tevent_req *subreq) |
119 | 0 | { |
120 | 0 | struct tevent_req *req = tevent_req_callback_data( |
121 | 0 | subreq, struct tevent_req); |
122 | 0 | struct cli_create_reparse_point_state *state = |
123 | 0 | tevent_req_data(req, struct cli_create_reparse_point_state); |
124 | |
|
125 | 0 | state->set_reparse_status = cli_fsctl_recv(subreq, NULL, NULL); |
126 | 0 | TALLOC_FREE(subreq); |
127 | |
|
128 | 0 | if (NT_STATUS_IS_OK(state->set_reparse_status)) { |
129 | 0 | subreq = cli_close_send(state, |
130 | 0 | state->ev, |
131 | 0 | state->cli, |
132 | 0 | state->fnum, |
133 | 0 | 0); |
134 | 0 | if (tevent_req_nomem(subreq, req)) { |
135 | 0 | return; |
136 | 0 | } |
137 | 0 | tevent_req_set_callback(subreq, |
138 | 0 | cli_create_reparse_point_closed, |
139 | 0 | req); |
140 | 0 | return; |
141 | 0 | } |
142 | 0 | subreq = cli_nt_delete_on_close_send( |
143 | 0 | state, state->ev, state->cli, state->fnum, true); |
144 | 0 | if (tevent_req_nomem(subreq, req)) { |
145 | 0 | return; |
146 | 0 | } |
147 | 0 | tevent_req_set_callback(subreq, |
148 | 0 | cli_create_reparse_point_doc_done, |
149 | 0 | req); |
150 | 0 | } |
151 | | |
152 | | static void cli_create_reparse_point_doc_done(struct tevent_req *subreq) |
153 | 0 | { |
154 | 0 | struct tevent_req *req = tevent_req_callback_data( |
155 | 0 | subreq, struct tevent_req); |
156 | 0 | struct cli_create_reparse_point_state *state = |
157 | 0 | tevent_req_data(req, struct cli_create_reparse_point_state); |
158 | | |
159 | | /* |
160 | | * Ignore status, we can't do much anyway in case of failure |
161 | | */ |
162 | |
|
163 | 0 | (void)cli_nt_delete_on_close_recv(subreq); |
164 | 0 | TALLOC_FREE(subreq); |
165 | |
|
166 | 0 | subreq = cli_close_send(state, state->ev, state->cli, state->fnum, 0); |
167 | 0 | if (tevent_req_nomem(subreq, req)) { |
168 | 0 | return; |
169 | 0 | } |
170 | 0 | tevent_req_set_callback(subreq, cli_create_reparse_point_closed, req); |
171 | 0 | } |
172 | | |
173 | | static void cli_create_reparse_point_closed(struct tevent_req *subreq) |
174 | 0 | { |
175 | 0 | struct tevent_req *req = tevent_req_callback_data( |
176 | 0 | subreq, struct tevent_req); |
177 | 0 | struct cli_create_reparse_point_state *state = |
178 | 0 | tevent_req_data(req, struct cli_create_reparse_point_state); |
179 | 0 | NTSTATUS status; |
180 | |
|
181 | 0 | status = cli_close_recv(subreq); |
182 | 0 | TALLOC_FREE(subreq); |
183 | |
|
184 | 0 | if (tevent_req_nterror(req, status)) { |
185 | 0 | return; |
186 | 0 | } |
187 | 0 | if (tevent_req_nterror(req, state->set_reparse_status)) { |
188 | 0 | return; |
189 | 0 | } |
190 | 0 | tevent_req_done(req); |
191 | 0 | } |
192 | | |
193 | | NTSTATUS cli_create_reparse_point_recv(struct tevent_req *req) |
194 | 0 | { |
195 | 0 | return tevent_req_simple_recv_ntstatus(req); |
196 | 0 | } |
197 | | |
198 | | struct cli_symlink_state { |
199 | | uint8_t dummy; |
200 | | }; |
201 | | |
202 | | static void cli_symlink_done(struct tevent_req *subreq); |
203 | | |
204 | | struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx, |
205 | | struct tevent_context *ev, |
206 | | struct cli_state *cli, |
207 | | const char *link_target, |
208 | | const char *newpath, |
209 | | uint32_t flags) |
210 | 0 | { |
211 | 0 | struct tevent_req *req = NULL, *subreq = NULL; |
212 | 0 | struct cli_symlink_state *state = NULL; |
213 | 0 | struct reparse_data_buffer reparse_buf = { |
214 | 0 | .tag = IO_REPARSE_TAG_SYMLINK, |
215 | 0 | .parsed.lnk.substitute_name = |
216 | 0 | discard_const_p(char, link_target), |
217 | 0 | .parsed.lnk.print_name = discard_const_p(char, link_target), |
218 | 0 | .parsed.lnk.flags = flags, |
219 | 0 | }; |
220 | 0 | uint8_t *buf; |
221 | 0 | ssize_t buflen; |
222 | |
|
223 | 0 | req = tevent_req_create(mem_ctx, &state, struct cli_symlink_state); |
224 | 0 | if (req == NULL) { |
225 | 0 | return NULL; |
226 | 0 | } |
227 | | |
228 | 0 | buflen = reparse_data_buffer_marshall(&reparse_buf, NULL, 0); |
229 | 0 | if (buflen == -1) { |
230 | 0 | tevent_req_oom(req); |
231 | 0 | return tevent_req_post(req, ev); |
232 | 0 | } |
233 | | |
234 | 0 | buf = talloc_array(state, uint8_t, buflen); |
235 | 0 | if (tevent_req_nomem(buf, req)) { |
236 | 0 | return tevent_req_post(req, ev); |
237 | 0 | } |
238 | | |
239 | 0 | buflen = reparse_data_buffer_marshall(&reparse_buf, buf, buflen); |
240 | 0 | if (buflen != talloc_array_length(buf)) { |
241 | 0 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); |
242 | 0 | return tevent_req_post(req, ev); |
243 | 0 | } |
244 | | |
245 | 0 | subreq = cli_create_reparse_point_send(state, |
246 | 0 | ev, |
247 | 0 | cli, |
248 | 0 | newpath, |
249 | 0 | (DATA_BLOB){ |
250 | 0 | .data = buf, |
251 | 0 | .length = buflen, |
252 | 0 | }); |
253 | 0 | if (tevent_req_nomem(subreq, req)) { |
254 | 0 | return tevent_req_post(req, ev); |
255 | 0 | } |
256 | 0 | tevent_req_set_callback(subreq, cli_symlink_done, req); |
257 | 0 | return req; |
258 | 0 | } |
259 | | |
260 | | static void cli_symlink_done(struct tevent_req *subreq) |
261 | 0 | { |
262 | 0 | NTSTATUS status = cli_symlink_recv(subreq); |
263 | 0 | tevent_req_simple_finish_ntstatus(subreq, status); |
264 | 0 | } |
265 | | |
266 | | NTSTATUS cli_symlink_recv(struct tevent_req *req) |
267 | 0 | { |
268 | 0 | return tevent_req_simple_recv_ntstatus(req); |
269 | 0 | } |
270 | | |
271 | | NTSTATUS cli_symlink(struct cli_state *cli, const char *link_target, |
272 | | const char *newname, uint32_t flags) |
273 | 0 | { |
274 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
275 | 0 | struct tevent_context *ev; |
276 | 0 | struct tevent_req *req; |
277 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
278 | |
|
279 | 0 | if (smbXcli_conn_has_async_calls(cli->conn)) { |
280 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
281 | 0 | goto fail; |
282 | 0 | } |
283 | 0 | ev = samba_tevent_context_init(frame); |
284 | 0 | if (ev == NULL) { |
285 | 0 | goto fail; |
286 | 0 | } |
287 | 0 | req = cli_symlink_send(frame, ev, cli, link_target, newname, flags); |
288 | 0 | if (req == NULL) { |
289 | 0 | goto fail; |
290 | 0 | } |
291 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
292 | 0 | goto fail; |
293 | 0 | } |
294 | 0 | status = cli_symlink_recv(req); |
295 | 0 | fail: |
296 | 0 | TALLOC_FREE(frame); |
297 | 0 | return status; |
298 | 0 | } |
299 | | |
300 | | struct cli_get_reparse_data_state { |
301 | | struct tevent_context *ev; |
302 | | struct cli_state *cli; |
303 | | uint16_t fnum; |
304 | | |
305 | | NTSTATUS get_reparse_status; |
306 | | uint8_t *data; |
307 | | uint32_t datalen; |
308 | | }; |
309 | | |
310 | | static void cli_get_reparse_data_opened(struct tevent_req *subreq); |
311 | | static void cli_get_reparse_data_done(struct tevent_req *subreq); |
312 | | static void cli_get_reparse_data_closed(struct tevent_req *subreq); |
313 | | |
314 | | struct tevent_req *cli_get_reparse_data_send(TALLOC_CTX *mem_ctx, |
315 | | struct tevent_context *ev, |
316 | | struct cli_state *cli, |
317 | | const char *fname) |
318 | 0 | { |
319 | 0 | struct tevent_req *req = NULL, *subreq = NULL; |
320 | 0 | struct cli_get_reparse_data_state *state = NULL; |
321 | |
|
322 | 0 | req = tevent_req_create(mem_ctx, |
323 | 0 | &state, |
324 | 0 | struct cli_get_reparse_data_state); |
325 | 0 | if (req == NULL) { |
326 | 0 | return NULL; |
327 | 0 | } |
328 | 0 | state->ev = ev; |
329 | 0 | state->cli = cli; |
330 | |
|
331 | 0 | subreq = cli_ntcreate_send(state, |
332 | 0 | ev, |
333 | 0 | cli, |
334 | 0 | fname, |
335 | 0 | 0, |
336 | 0 | FILE_READ_ATTRIBUTES | FILE_READ_EA, |
337 | 0 | 0, |
338 | 0 | FILE_SHARE_READ | FILE_SHARE_WRITE | |
339 | 0 | FILE_SHARE_DELETE, |
340 | 0 | FILE_OPEN, |
341 | 0 | FILE_OPEN_REPARSE_POINT, |
342 | 0 | SMB2_IMPERSONATION_IMPERSONATION, |
343 | 0 | 0); |
344 | 0 | if (tevent_req_nomem(subreq, req)) { |
345 | 0 | return tevent_req_post(req, ev); |
346 | 0 | } |
347 | 0 | tevent_req_set_callback(subreq, cli_get_reparse_data_opened, req); |
348 | 0 | return req; |
349 | 0 | } |
350 | | |
351 | | static void cli_get_reparse_data_opened(struct tevent_req *subreq) |
352 | 0 | { |
353 | 0 | struct tevent_req *req = |
354 | 0 | tevent_req_callback_data(subreq, struct tevent_req); |
355 | 0 | struct cli_get_reparse_data_state *state = |
356 | 0 | tevent_req_data(req, struct cli_get_reparse_data_state); |
357 | 0 | NTSTATUS status; |
358 | |
|
359 | 0 | status = cli_ntcreate_recv(subreq, &state->fnum, NULL); |
360 | 0 | TALLOC_FREE(subreq); |
361 | 0 | if (tevent_req_nterror(req, status)) { |
362 | 0 | return; |
363 | 0 | } |
364 | | |
365 | 0 | subreq = cli_fsctl_send(state, |
366 | 0 | state->ev, |
367 | 0 | state->cli, |
368 | 0 | state->fnum, |
369 | 0 | FSCTL_GET_REPARSE_POINT, |
370 | 0 | NULL, |
371 | 0 | 65536); |
372 | |
|
373 | 0 | if (tevent_req_nomem(subreq, req)) { |
374 | 0 | return; |
375 | 0 | } |
376 | 0 | tevent_req_set_callback(subreq, cli_get_reparse_data_done, req); |
377 | 0 | } |
378 | | |
379 | | static void cli_get_reparse_data_done(struct tevent_req *subreq) |
380 | 0 | { |
381 | 0 | struct tevent_req *req = |
382 | 0 | tevent_req_callback_data(subreq, struct tevent_req); |
383 | 0 | struct cli_get_reparse_data_state *state = |
384 | 0 | tevent_req_data(req, struct cli_get_reparse_data_state); |
385 | 0 | DATA_BLOB out = { |
386 | 0 | .data = NULL, |
387 | 0 | }; |
388 | |
|
389 | 0 | state->get_reparse_status = cli_fsctl_recv(subreq, state, &out); |
390 | 0 | TALLOC_FREE(subreq); |
391 | |
|
392 | 0 | if (NT_STATUS_IS_OK(state->get_reparse_status)) { |
393 | 0 | state->data = out.data; |
394 | 0 | state->datalen = out.length; |
395 | 0 | } |
396 | |
|
397 | 0 | subreq = cli_close_send(state, state->ev, state->cli, state->fnum, 0); |
398 | 0 | if (tevent_req_nomem(subreq, req)) { |
399 | 0 | return; |
400 | 0 | } |
401 | 0 | tevent_req_set_callback(subreq, cli_get_reparse_data_closed, req); |
402 | 0 | } |
403 | | |
404 | | static void cli_get_reparse_data_closed(struct tevent_req *subreq) |
405 | 0 | { |
406 | 0 | struct tevent_req *req = |
407 | 0 | tevent_req_callback_data(subreq, struct tevent_req); |
408 | 0 | struct cli_get_reparse_data_state *state = |
409 | 0 | tevent_req_data(req, struct cli_get_reparse_data_state); |
410 | 0 | NTSTATUS status; |
411 | |
|
412 | 0 | status = cli_close_recv(subreq); |
413 | 0 | TALLOC_FREE(subreq); |
414 | 0 | if (tevent_req_nterror(req, status)) { |
415 | 0 | return; |
416 | 0 | } |
417 | 0 | if (tevent_req_nterror(req, state->get_reparse_status)) { |
418 | 0 | return; |
419 | 0 | } |
420 | 0 | tevent_req_done(req); |
421 | 0 | } |
422 | | |
423 | | NTSTATUS cli_get_reparse_data_recv(struct tevent_req *req, |
424 | | TALLOC_CTX *mem_ctx, |
425 | | uint8_t **_data, |
426 | | uint32_t *_datalen) |
427 | 0 | { |
428 | 0 | struct cli_get_reparse_data_state *state = |
429 | 0 | tevent_req_data(req, struct cli_get_reparse_data_state); |
430 | 0 | NTSTATUS status; |
431 | |
|
432 | 0 | if (tevent_req_is_nterror(req, &status)) { |
433 | 0 | return status; |
434 | 0 | } |
435 | | |
436 | 0 | *_data = talloc_move(mem_ctx, &state->data); |
437 | 0 | *_datalen = state->datalen; |
438 | |
|
439 | 0 | tevent_req_received(req); |
440 | |
|
441 | 0 | return NT_STATUS_OK; |
442 | 0 | } |
443 | | |
444 | | NTSTATUS cli_get_reparse_data(struct cli_state *cli, |
445 | | const char *fname, |
446 | | TALLOC_CTX *mem_ctx, |
447 | | uint8_t **_data, |
448 | | uint32_t *_datalen) |
449 | 0 | { |
450 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
451 | 0 | struct tevent_context *ev; |
452 | 0 | struct tevent_req *req; |
453 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
454 | |
|
455 | 0 | if (smbXcli_conn_has_async_calls(cli->conn)) { |
456 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
457 | 0 | goto fail; |
458 | 0 | } |
459 | 0 | ev = samba_tevent_context_init(frame); |
460 | 0 | if (ev == NULL) { |
461 | 0 | goto fail; |
462 | 0 | } |
463 | 0 | req = cli_get_reparse_data_send(frame, ev, cli, fname); |
464 | 0 | if (req == NULL) { |
465 | 0 | goto fail; |
466 | 0 | } |
467 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
468 | 0 | goto fail; |
469 | 0 | } |
470 | 0 | status = cli_get_reparse_data_recv(req, mem_ctx, _data, _datalen); |
471 | 0 | fail: |
472 | 0 | TALLOC_FREE(frame); |
473 | 0 | return status; |
474 | 0 | } |
475 | | |
476 | | struct cli_readlink_state { |
477 | | struct tevent_context *ev; |
478 | | struct cli_state *cli; |
479 | | uint16_t fnum; |
480 | | |
481 | | uint16_t setup[4]; |
482 | | uint8_t *data; |
483 | | uint32_t num_data; |
484 | | char *target; |
485 | | }; |
486 | | |
487 | | static void cli_readlink_posix1_done(struct tevent_req *subreq); |
488 | | static void cli_readlink_got_reparse_data(struct tevent_req *subreq); |
489 | | |
490 | | struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx, |
491 | | struct tevent_context *ev, |
492 | | struct cli_state *cli, |
493 | | const char *fname) |
494 | 0 | { |
495 | 0 | struct tevent_req *req, *subreq; |
496 | 0 | struct cli_readlink_state *state; |
497 | |
|
498 | 0 | req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state); |
499 | 0 | if (req == NULL) { |
500 | 0 | return NULL; |
501 | 0 | } |
502 | 0 | state->ev = ev; |
503 | 0 | state->cli = cli; |
504 | |
|
505 | 0 | if (cli->requested_posix_capabilities != 0) { |
506 | | /* |
507 | | * Only happens for negotiated SMB1 posix caps |
508 | | */ |
509 | 0 | subreq = cli_posix_readlink_send(state, ev, cli, fname); |
510 | 0 | if (tevent_req_nomem(subreq, req)) { |
511 | 0 | return tevent_req_post(req, ev); |
512 | 0 | } |
513 | 0 | tevent_req_set_callback(subreq, cli_readlink_posix1_done, req); |
514 | 0 | return req; |
515 | 0 | } |
516 | | |
517 | 0 | subreq = cli_get_reparse_data_send(state, ev, cli, fname); |
518 | 0 | if (tevent_req_nomem(subreq, req)) { |
519 | 0 | return tevent_req_post(req, ev); |
520 | 0 | } |
521 | 0 | tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req); |
522 | 0 | return req; |
523 | 0 | } |
524 | | |
525 | | static void cli_readlink_posix1_done(struct tevent_req *subreq) |
526 | 0 | { |
527 | 0 | struct tevent_req *req = tevent_req_callback_data( |
528 | 0 | subreq, struct tevent_req); |
529 | 0 | struct cli_readlink_state *state = tevent_req_data( |
530 | 0 | req, struct cli_readlink_state); |
531 | 0 | NTSTATUS status; |
532 | |
|
533 | 0 | status = cli_posix_readlink_recv(subreq, state, &state->target); |
534 | 0 | TALLOC_FREE(subreq); |
535 | 0 | if (tevent_req_nterror(req, status)) { |
536 | 0 | return; |
537 | 0 | } |
538 | 0 | tevent_req_done(req); |
539 | 0 | } |
540 | | |
541 | | static void cli_readlink_got_reparse_data(struct tevent_req *subreq) |
542 | 0 | { |
543 | 0 | struct tevent_req *req = tevent_req_callback_data( |
544 | 0 | subreq, struct tevent_req); |
545 | 0 | struct cli_readlink_state *state = tevent_req_data( |
546 | 0 | req, struct cli_readlink_state); |
547 | 0 | NTSTATUS status; |
548 | |
|
549 | 0 | status = cli_get_reparse_data_recv(subreq, |
550 | 0 | state, |
551 | 0 | &state->data, |
552 | 0 | &state->num_data); |
553 | 0 | TALLOC_FREE(subreq); |
554 | 0 | if (tevent_req_nterror(req, status)) { |
555 | 0 | return; |
556 | 0 | } |
557 | 0 | tevent_req_done(req); |
558 | 0 | } |
559 | | |
560 | | NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, |
561 | | char **psubstitute_name, char **pprint_name, |
562 | | uint32_t *pflags) |
563 | 0 | { |
564 | 0 | struct cli_readlink_state *state = tevent_req_data( |
565 | 0 | req, struct cli_readlink_state); |
566 | 0 | struct reparse_data_buffer buf = { |
567 | 0 | .tag = 0, |
568 | 0 | }; |
569 | 0 | NTSTATUS status; |
570 | |
|
571 | 0 | if (tevent_req_is_nterror(req, &status)) { |
572 | 0 | return status; |
573 | 0 | } |
574 | | |
575 | 0 | if (state->target != NULL) { |
576 | | /* |
577 | | * SMB1 posix version |
578 | | */ |
579 | 0 | if (psubstitute_name != NULL) { |
580 | 0 | *psubstitute_name = talloc_move( |
581 | 0 | mem_ctx, &state->target); |
582 | 0 | } |
583 | 0 | if (pprint_name != NULL) { |
584 | 0 | *pprint_name = NULL; |
585 | 0 | } |
586 | 0 | if (pflags != NULL) { |
587 | 0 | *pflags = 0; |
588 | 0 | } |
589 | 0 | return NT_STATUS_OK; |
590 | 0 | } |
591 | | |
592 | 0 | status = reparse_data_buffer_parse(state, |
593 | 0 | &buf, |
594 | 0 | state->data, |
595 | 0 | state->num_data); |
596 | 0 | if (!NT_STATUS_IS_OK(status)) { |
597 | 0 | return NT_STATUS_INVALID_NETWORK_RESPONSE; |
598 | 0 | } |
599 | 0 | if (buf.tag != IO_REPARSE_TAG_SYMLINK) { |
600 | 0 | return NT_STATUS_INVALID_NETWORK_RESPONSE; |
601 | 0 | } |
602 | | |
603 | 0 | if (psubstitute_name != NULL) { |
604 | 0 | *psubstitute_name = |
605 | 0 | talloc_move(mem_ctx, &buf.parsed.lnk.substitute_name); |
606 | 0 | } |
607 | |
|
608 | 0 | if (pprint_name != NULL) { |
609 | 0 | *pprint_name = |
610 | 0 | talloc_move(mem_ctx, &buf.parsed.lnk.print_name); |
611 | 0 | } |
612 | |
|
613 | 0 | if (pflags != NULL) { |
614 | 0 | *pflags = buf.parsed.lnk.flags; |
615 | 0 | } |
616 | |
|
617 | 0 | tevent_req_received(req); |
618 | |
|
619 | 0 | return NT_STATUS_OK; |
620 | 0 | } |
621 | | |
622 | | NTSTATUS cli_readlink(struct cli_state *cli, const char *fname, |
623 | | TALLOC_CTX *mem_ctx, char **psubstitute_name, |
624 | | char **pprint_name, uint32_t *pflags) |
625 | 0 | { |
626 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
627 | 0 | struct tevent_context *ev; |
628 | 0 | struct tevent_req *req; |
629 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
630 | |
|
631 | 0 | if (smbXcli_conn_has_async_calls(cli->conn)) { |
632 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
633 | 0 | goto fail; |
634 | 0 | } |
635 | 0 | ev = samba_tevent_context_init(frame); |
636 | 0 | if (ev == NULL) { |
637 | 0 | goto fail; |
638 | 0 | } |
639 | 0 | req = cli_readlink_send(frame, ev, cli, fname); |
640 | 0 | if (req == NULL) { |
641 | 0 | goto fail; |
642 | 0 | } |
643 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
644 | 0 | goto fail; |
645 | 0 | } |
646 | 0 | status = cli_readlink_recv(req, mem_ctx, psubstitute_name, |
647 | 0 | pprint_name, pflags); |
648 | 0 | fail: |
649 | 0 | TALLOC_FREE(frame); |
650 | 0 | return status; |
651 | 0 | } |