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"
30 struct cli_symlink_state {
31 struct tevent_context *ev;
32 struct cli_state *cli;
40 NTSTATUS set_reparse_status;
43 static void cli_symlink_create_done(struct tevent_req *subreq);
44 static void cli_symlink_set_reparse_done(struct tevent_req *subreq);
45 static void cli_symlink_delete_on_close_done(struct tevent_req *subreq);
46 static void cli_symlink_close_done(struct tevent_req *subreq);
48 struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx,
49 struct tevent_context *ev,
50 struct cli_state *cli,
55 struct tevent_req *req, *subreq;
56 struct cli_symlink_state *state;
58 req = tevent_req_create(mem_ctx, &state, struct cli_symlink_state);
64 state->oldpath = oldpath;
65 state->newpath = newpath;
68 subreq = cli_ntcreate_send(
69 state, ev, cli, state->oldpath, 0,
70 SYNCHRONIZE_ACCESS|DELETE_ACCESS|
71 FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
72 FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE, FILE_CREATE,
73 FILE_OPEN_REPARSE_POINT|FILE_SYNCHRONOUS_IO_NONALERT|
74 FILE_NON_DIRECTORY_FILE, 0);
75 if (tevent_req_nomem(subreq, req)) {
76 return tevent_req_post(req, ev);
78 tevent_req_set_callback(subreq, cli_symlink_create_done, req);
82 static void cli_symlink_create_done(struct tevent_req *subreq)
84 struct tevent_req *req = tevent_req_callback_data(
85 subreq, struct tevent_req);
86 struct cli_symlink_state *state = tevent_req_data(
87 req, struct cli_symlink_state);
92 status = cli_ntcreate_recv(subreq, &state->fnum);
94 if (tevent_req_nterror(req, status)) {
98 SIVAL(state->setup, 0, FSCTL_SET_REPARSE_POINT);
99 SSVAL(state->setup, 4, state->fnum);
100 SCVAL(state->setup, 6, 1); /* IsFcntl */
101 SCVAL(state->setup, 7, 0); /* IsFlags */
103 if (!symlink_reparse_buffer_marshall(
104 state->newpath, NULL, state->flags, state,
110 subreq = cli_trans_send(state, state->ev, state->cli, SMBnttrans,
111 NULL, -1, /* name, fid */
112 NT_TRANSACT_IOCTL, 0,
113 state->setup, 4, 0, /* setup */
114 NULL, 0, 0, /* param */
115 data, data_len, 0); /* data */
116 if (tevent_req_nomem(subreq, req)) {
119 tevent_req_set_callback(subreq, cli_symlink_set_reparse_done, req);
122 static void cli_symlink_set_reparse_done(struct tevent_req *subreq)
124 struct tevent_req *req = tevent_req_callback_data(
125 subreq, struct tevent_req);
126 struct cli_symlink_state *state = tevent_req_data(
127 req, struct cli_symlink_state);
129 state->set_reparse_status = cli_trans_recv(
131 NULL, 0, NULL, /* rsetup */
132 NULL, 0, NULL, /* rparam */
133 NULL, 0, NULL); /* rdata */
136 if (NT_STATUS_IS_OK(state->set_reparse_status)) {
137 subreq = cli_close_send(state, state->ev, state->cli,
139 if (tevent_req_nomem(subreq, req)) {
142 tevent_req_set_callback(subreq, cli_symlink_close_done, req);
145 subreq = cli_nt_delete_on_close_send(
146 state, state->ev, state->cli, state->fnum, true);
147 if (tevent_req_nomem(subreq, req)) {
150 tevent_req_set_callback(subreq, cli_symlink_delete_on_close_done, req);
153 static void cli_symlink_delete_on_close_done(struct tevent_req *subreq)
155 struct tevent_req *req = tevent_req_callback_data(
156 subreq, struct tevent_req);
157 struct cli_symlink_state *state = tevent_req_data(
158 req, struct cli_symlink_state);
161 * Ignore status, we can't do much anyway in case of failure
164 (void)cli_nt_delete_on_close_recv(subreq);
167 subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
168 if (tevent_req_nomem(subreq, req)) {
171 tevent_req_set_callback(subreq, cli_symlink_close_done, req);
174 static void cli_symlink_close_done(struct tevent_req *subreq)
176 struct tevent_req *req = tevent_req_callback_data(
177 subreq, struct tevent_req);
178 struct cli_symlink_state *state = tevent_req_data(
179 req, struct cli_symlink_state);
182 status = cli_close_recv(subreq);
185 if (tevent_req_nterror(req, status)) {
188 if (tevent_req_nterror(req, state->set_reparse_status)) {
191 tevent_req_done(req);
194 NTSTATUS cli_symlink_recv(struct tevent_req *req)
196 return tevent_req_simple_recv_ntstatus(req);
199 NTSTATUS cli_symlink(struct cli_state *cli, const char *oldname,
200 const char *newname, uint32_t flags)
202 TALLOC_CTX *frame = talloc_stackframe();
203 struct event_context *ev;
204 struct tevent_req *req;
205 NTSTATUS status = NT_STATUS_NO_MEMORY;
207 if (cli_has_async_calls(cli)) {
208 status = NT_STATUS_INVALID_PARAMETER;
211 ev = event_context_init(frame);
215 req = cli_symlink_send(frame, ev, cli, oldname, newname, flags);
219 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
222 status = cli_symlink_recv(req);
228 struct cli_readlink_state {
229 struct tevent_context *ev;
230 struct cli_state *cli;
234 NTSTATUS get_reparse_status;
239 static void cli_readlink_opened(struct tevent_req *subreq);
240 static void cli_readlink_got_reparse_data(struct tevent_req *subreq);
241 static void cli_readlink_closed(struct tevent_req *subreq);
243 struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx,
244 struct tevent_context *ev,
245 struct cli_state *cli,
248 struct tevent_req *req, *subreq;
249 struct cli_readlink_state *state;
251 req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state);
258 subreq = cli_ntcreate_send(
259 state, ev, cli, fname, 0, FILE_READ_ATTRIBUTES | FILE_READ_EA,
260 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
261 FILE_OPEN, FILE_OPEN_REPARSE_POINT, 0);
262 if (tevent_req_nomem(subreq, req)) {
263 return tevent_req_post(req, ev);
265 tevent_req_set_callback(subreq, cli_readlink_opened, req);
269 static void cli_readlink_opened(struct tevent_req *subreq)
271 struct tevent_req *req = tevent_req_callback_data(
272 subreq, struct tevent_req);
273 struct cli_readlink_state *state = tevent_req_data(
274 req, struct cli_readlink_state);
277 status = cli_ntcreate_recv(subreq, &state->fnum);
279 if (tevent_req_nterror(req, status)) {
283 SIVAL(state->setup, 0, FSCTL_GET_REPARSE_POINT);
284 SSVAL(state->setup, 4, state->fnum);
285 SCVAL(state->setup, 6, 1); /* IsFcntl */
286 SCVAL(state->setup, 7, 0); /* IsFlags */
288 subreq = cli_trans_send(state, state->ev, state->cli, SMBnttrans,
289 NULL, -1, /* name, fid */
290 NT_TRANSACT_IOCTL, 0,
291 state->setup, 4, 0, /* setup */
292 NULL, 0, 0, /* param */
293 NULL, 0, 16384); /* data */
294 if (tevent_req_nomem(subreq, req)) {
297 tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req);
300 static void cli_readlink_got_reparse_data(struct tevent_req *subreq)
302 struct tevent_req *req = tevent_req_callback_data(
303 subreq, struct tevent_req);
304 struct cli_readlink_state *state = tevent_req_data(
305 req, struct cli_readlink_state);
307 state->get_reparse_status = cli_trans_recv(
309 NULL, 0, NULL, /* rsetup */
310 NULL, 0, NULL, /* rparam */
311 &state->data, 20, &state->num_data); /* rdata */
314 subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
315 if (tevent_req_nomem(subreq, req)) {
318 tevent_req_set_callback(subreq, cli_readlink_closed, req);
321 static void cli_readlink_closed(struct tevent_req *subreq)
323 struct tevent_req *req = tevent_req_callback_data(
324 subreq, struct tevent_req);
327 status = cli_close_recv(subreq);
329 if (tevent_req_nterror(req, status)) {
332 tevent_req_done(req);
335 NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
336 char **psubstitute_name, char **pprint_name,
339 struct cli_readlink_state *state = tevent_req_data(
340 req, struct cli_readlink_state);
342 char *substitute_name;
346 if (tevent_req_is_nterror(req, &status)) {
350 if (!symlink_reparse_buffer_parse(state->data, state->num_data,
351 talloc_tos(), &substitute_name,
352 &print_name, &flags)) {
353 return NT_STATUS_INVALID_NETWORK_RESPONSE;
356 if (psubstitute_name != NULL) {
357 *psubstitute_name = talloc_move(mem_ctx, &substitute_name);
359 TALLOC_FREE(substitute_name);
361 if (pprint_name != NULL) {
362 *pprint_name = talloc_move(mem_ctx, &print_name);
364 TALLOC_FREE(print_name);
366 if (pflags != NULL) {
372 NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
373 TALLOC_CTX *mem_ctx, char **psubstitute_name,
374 char **pprint_name, uint32_t *pflags)
376 TALLOC_CTX *frame = talloc_stackframe();
377 struct event_context *ev;
378 struct tevent_req *req;
379 NTSTATUS status = NT_STATUS_NO_MEMORY;
381 if (cli_has_async_calls(cli)) {
382 status = NT_STATUS_INVALID_PARAMETER;
385 ev = event_context_init(frame);
389 req = cli_readlink_send(frame, ev, cli, fname);
393 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
396 status = cli_readlink_recv(req, mem_ctx, psubstitute_name,
397 pprint_name, pflags);