ctdb-scripts: Ignore shellcheck SC2181 warning (use of $?)
[metze/samba/wip.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 *oldpath;
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 *oldpath,
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->oldpath = oldpath;
66         state->newpath = newpath;
67         state->flags = flags;
68
69         subreq = cli_ntcreate_send(
70                 state, ev, cli, state->oldpath, 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         uint8_t *data;
90         size_t data_len;
91         NTSTATUS status;
92
93         status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
94         TALLOC_FREE(subreq);
95         if (tevent_req_nterror(req, status)) {
96                 return;
97         }
98
99         SIVAL(state->setup, 0, FSCTL_SET_REPARSE_POINT);
100         SSVAL(state->setup, 4, state->fnum);
101         SCVAL(state->setup, 6, 1); /* IsFcntl */
102         SCVAL(state->setup, 7, 0); /* IsFlags */
103
104         if (!symlink_reparse_buffer_marshall(
105                     state->newpath, NULL, state->flags, state,
106                     &data, &data_len)) {
107                 tevent_req_oom(req);
108                 return;
109         }
110
111         subreq = cli_trans_send(state, state->ev, state->cli, 0, SMBnttrans,
112                                 NULL, -1, /* name, fid */
113                                 NT_TRANSACT_IOCTL, 0,
114                                 state->setup, 4, 0, /* setup */
115                                 NULL, 0, 0,         /* param */
116                                 data, data_len, 0); /* data */
117         if (tevent_req_nomem(subreq, req)) {
118                 return;
119         }
120         tevent_req_set_callback(subreq, cli_symlink_set_reparse_done, req);
121 }
122
123 static void cli_symlink_set_reparse_done(struct tevent_req *subreq)
124 {
125         struct tevent_req *req = tevent_req_callback_data(
126                 subreq, struct tevent_req);
127         struct cli_symlink_state *state = tevent_req_data(
128                 req, struct cli_symlink_state);
129
130         state->set_reparse_status = cli_trans_recv(
131                 subreq, NULL, NULL,
132                 NULL, 0, NULL,  /* rsetup */
133                 NULL, 0, NULL,  /* rparam */
134                 NULL, 0, NULL); /* rdata */
135         TALLOC_FREE(subreq);
136
137         if (NT_STATUS_IS_OK(state->set_reparse_status)) {
138                 subreq = cli_close_send(state, state->ev, state->cli,
139                                         state->fnum);
140                 if (tevent_req_nomem(subreq, req)) {
141                         return;
142                 }
143                 tevent_req_set_callback(subreq, cli_symlink_close_done, req);
144                 return;
145         }
146         subreq = cli_nt_delete_on_close_send(
147                 state, state->ev, state->cli, state->fnum, true);
148         if (tevent_req_nomem(subreq, req)) {
149                 return;
150         }
151         tevent_req_set_callback(subreq, cli_symlink_delete_on_close_done, req);
152 }
153
154 static void cli_symlink_delete_on_close_done(struct tevent_req *subreq)
155 {
156         struct tevent_req *req = tevent_req_callback_data(
157                 subreq, struct tevent_req);
158         struct cli_symlink_state *state = tevent_req_data(
159                 req, struct cli_symlink_state);
160
161         /*
162          * Ignore status, we can't do much anyway in case of failure
163          */
164
165         (void)cli_nt_delete_on_close_recv(subreq);
166         TALLOC_FREE(subreq);
167
168         subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
169         if (tevent_req_nomem(subreq, req)) {
170                 return;
171         }
172         tevent_req_set_callback(subreq, cli_symlink_close_done, req);
173 }
174
175 static void cli_symlink_close_done(struct tevent_req *subreq)
176 {
177         struct tevent_req *req = tevent_req_callback_data(
178                 subreq, struct tevent_req);
179         struct cli_symlink_state *state = tevent_req_data(
180                 req, struct cli_symlink_state);
181         NTSTATUS status;
182
183         status = cli_close_recv(subreq);
184         TALLOC_FREE(subreq);
185
186         if (tevent_req_nterror(req, status)) {
187                 return;
188         }
189         if (tevent_req_nterror(req, state->set_reparse_status)) {
190                 return;
191         }
192         tevent_req_done(req);
193 }
194
195 NTSTATUS cli_symlink_recv(struct tevent_req *req)
196 {
197         return tevent_req_simple_recv_ntstatus(req);
198 }
199
200 NTSTATUS cli_symlink(struct cli_state *cli, const char *oldname,
201                      const char *newname, uint32_t flags)
202 {
203         TALLOC_CTX *frame = talloc_stackframe();
204         struct tevent_context *ev;
205         struct tevent_req *req;
206         NTSTATUS status = NT_STATUS_NO_MEMORY;
207
208         if (smbXcli_conn_has_async_calls(cli->conn)) {
209                 status = NT_STATUS_INVALID_PARAMETER;
210                 goto fail;
211         }
212         ev = samba_tevent_context_init(frame);
213         if (ev == NULL) {
214                 goto fail;
215         }
216         req = cli_symlink_send(frame, ev, cli, oldname, newname, flags);
217         if (req == NULL) {
218                 goto fail;
219         }
220         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
221                 goto fail;
222         }
223         status = cli_symlink_recv(req);
224  fail:
225         TALLOC_FREE(frame);
226         return status;
227 }
228
229 struct cli_readlink_state {
230         struct tevent_context *ev;
231         struct cli_state *cli;
232         uint16_t fnum;
233
234         uint16_t setup[4];
235         NTSTATUS get_reparse_status;
236         uint8_t *data;
237         uint32_t num_data;
238 };
239
240 static void cli_readlink_opened(struct tevent_req *subreq);
241 static void cli_readlink_got_reparse_data(struct tevent_req *subreq);
242 static void cli_readlink_closed(struct tevent_req *subreq);
243
244 struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx,
245                                      struct tevent_context *ev,
246                                      struct cli_state *cli,
247                                      const char *fname)
248 {
249         struct tevent_req *req, *subreq;
250         struct cli_readlink_state *state;
251
252         req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state);
253         if (req == NULL) {
254                 return NULL;
255         }
256         state->ev = ev;
257         state->cli = cli;
258
259         subreq = cli_ntcreate_send(
260                 state, ev, cli, fname, 0, FILE_READ_ATTRIBUTES | FILE_READ_EA,
261                 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
262                 FILE_OPEN, FILE_OPEN_REPARSE_POINT, 0);
263         if (tevent_req_nomem(subreq, req)) {
264                 return tevent_req_post(req, ev);
265         }
266         tevent_req_set_callback(subreq, cli_readlink_opened, req);
267         return req;
268 }
269
270 static void cli_readlink_opened(struct tevent_req *subreq)
271 {
272         struct tevent_req *req = tevent_req_callback_data(
273                 subreq, struct tevent_req);
274         struct cli_readlink_state *state = tevent_req_data(
275                 req, struct cli_readlink_state);
276         NTSTATUS status;
277
278         status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
279         TALLOC_FREE(subreq);
280         if (tevent_req_nterror(req, status)) {
281                 return;
282         }
283
284         SIVAL(state->setup, 0, FSCTL_GET_REPARSE_POINT);
285         SSVAL(state->setup, 4, state->fnum);
286         SCVAL(state->setup, 6, 1); /* IsFcntl */
287         SCVAL(state->setup, 7, 0); /* IsFlags */
288
289         subreq = cli_trans_send(state, state->ev, state->cli, 0, SMBnttrans,
290                                 NULL, -1, /* name, fid */
291                                 NT_TRANSACT_IOCTL, 0,
292                                 state->setup, 4, 0, /* setup */
293                                 NULL, 0, 0,         /* param */
294                                 NULL, 0, 16384); /* data */
295         if (tevent_req_nomem(subreq, req)) {
296                 return;
297         }
298         tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req);
299 }
300
301 static void cli_readlink_got_reparse_data(struct tevent_req *subreq)
302 {
303         struct tevent_req *req = tevent_req_callback_data(
304                 subreq, struct tevent_req);
305         struct cli_readlink_state *state = tevent_req_data(
306                 req, struct cli_readlink_state);
307
308         state->get_reparse_status = cli_trans_recv(
309                 subreq, state, NULL,
310                 NULL, 0, NULL,  /* rsetup */
311                 NULL, 0, NULL,  /* rparam */
312                 &state->data, 20, &state->num_data); /* rdata */
313         TALLOC_FREE(subreq);
314
315         subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
316         if (tevent_req_nomem(subreq, req)) {
317                 return;
318         }
319         tevent_req_set_callback(subreq, cli_readlink_closed, req);
320 }
321
322 static void cli_readlink_closed(struct tevent_req *subreq)
323 {
324         struct tevent_req *req = tevent_req_callback_data(
325                 subreq, struct tevent_req);
326         NTSTATUS status;
327
328         status = cli_close_recv(subreq);
329         TALLOC_FREE(subreq);
330         if (tevent_req_nterror(req, status)) {
331                 return;
332         }
333         tevent_req_done(req);
334 }
335
336 NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
337                            char **psubstitute_name, char **pprint_name,
338                            uint32_t *pflags)
339 {
340         struct cli_readlink_state *state = tevent_req_data(
341                 req, struct cli_readlink_state);
342         NTSTATUS status;
343         char *substitute_name;
344         char *print_name;
345         uint32_t flags;
346
347         if (tevent_req_is_nterror(req, &status)) {
348                 return status;
349         }
350
351         if (!symlink_reparse_buffer_parse(state->data, state->num_data,
352                                           talloc_tos(), &substitute_name,
353                                           &print_name, &flags)) {
354                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
355         }
356
357         if (psubstitute_name != NULL) {
358                 *psubstitute_name = talloc_move(mem_ctx, &substitute_name);
359         }
360         TALLOC_FREE(substitute_name);
361
362         if (pprint_name != NULL) {
363                 *pprint_name = talloc_move(mem_ctx, &print_name);
364         }
365         TALLOC_FREE(print_name);
366
367         if (pflags != NULL) {
368                 *pflags = flags;
369         }
370         return NT_STATUS_OK;
371 }
372
373 NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
374                        TALLOC_CTX *mem_ctx, char **psubstitute_name,
375                       char **pprint_name, uint32_t *pflags)
376 {
377         TALLOC_CTX *frame = talloc_stackframe();
378         struct tevent_context *ev;
379         struct tevent_req *req;
380         NTSTATUS status = NT_STATUS_NO_MEMORY;
381
382         if (smbXcli_conn_has_async_calls(cli->conn)) {
383                 status = NT_STATUS_INVALID_PARAMETER;
384                 goto fail;
385         }
386         ev = samba_tevent_context_init(frame);
387         if (ev == NULL) {
388                 goto fail;
389         }
390         req = cli_readlink_send(frame, ev, cli, fname);
391         if (req == NULL) {
392                 goto fail;
393         }
394         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
395                 goto fail;
396         }
397         status = cli_readlink_recv(req, mem_ctx, psubstitute_name,
398                                    pprint_name, pflags);
399  fail:
400         TALLOC_FREE(frame);
401         return status;
402 }