libcli:smb: add defines for SMB2.2 global capabilities
[ira/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
30 struct cli_symlink_state {
31         struct tevent_context *ev;
32         struct cli_state *cli;
33         const char *oldpath;
34         const char *newpath;
35         uint32_t flags;
36
37         uint16_t fnum;
38
39         uint16_t setup[4];
40         NTSTATUS set_reparse_status;
41 };
42
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);
47
48 struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx,
49                                     struct tevent_context *ev,
50                                     struct cli_state *cli,
51                                     const char *oldpath,
52                                     const char *newpath,
53                                     uint32_t flags)
54 {
55         struct tevent_req *req, *subreq;
56         struct cli_symlink_state *state;
57
58         req = tevent_req_create(mem_ctx, &state, struct cli_symlink_state);
59         if (req == NULL) {
60                 return NULL;
61         }
62         state->ev = ev;
63         state->cli = cli;
64         state->oldpath = oldpath;
65         state->newpath = newpath;
66         state->flags = flags;
67
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);
77         }
78         tevent_req_set_callback(subreq, cli_symlink_create_done, req);
79         return req;
80 }
81
82 static void cli_symlink_create_done(struct tevent_req *subreq)
83 {
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);
88         uint8_t *data;
89         size_t data_len;
90         NTSTATUS status;
91
92         status = cli_ntcreate_recv(subreq, &state->fnum);
93         TALLOC_FREE(subreq);
94         if (tevent_req_nterror(req, status)) {
95                 return;
96         }
97
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 */
102
103         if (!symlink_reparse_buffer_marshall(
104                     state->newpath, NULL, state->flags, state,
105                     &data, &data_len)) {
106                 tevent_req_oom(req);
107                 return;
108         }
109
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)) {
117                 return;
118         }
119         tevent_req_set_callback(subreq, cli_symlink_set_reparse_done, req);
120 }
121
122 static void cli_symlink_set_reparse_done(struct tevent_req *subreq)
123 {
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);
128
129         state->set_reparse_status = cli_trans_recv(
130                 subreq, NULL, NULL,
131                 NULL, 0, NULL,  /* rsetup */
132                 NULL, 0, NULL,  /* rparam */
133                 NULL, 0, NULL); /* rdata */
134         TALLOC_FREE(subreq);
135
136         if (NT_STATUS_IS_OK(state->set_reparse_status)) {
137                 subreq = cli_close_send(state, state->ev, state->cli,
138                                         state->fnum);
139                 if (tevent_req_nomem(subreq, req)) {
140                         return;
141                 }
142                 tevent_req_set_callback(subreq, cli_symlink_close_done, req);
143                 return;
144         }
145         subreq = cli_nt_delete_on_close_send(
146                 state, state->ev, state->cli, state->fnum, true);
147         if (tevent_req_nomem(subreq, req)) {
148                 return;
149         }
150         tevent_req_set_callback(subreq, cli_symlink_delete_on_close_done, req);
151 }
152
153 static void cli_symlink_delete_on_close_done(struct tevent_req *subreq)
154 {
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);
159
160         /*
161          * Ignore status, we can't do much anyway in case of failure
162          */
163
164         (void)cli_nt_delete_on_close_recv(subreq);
165         TALLOC_FREE(subreq);
166
167         subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
168         if (tevent_req_nomem(subreq, req)) {
169                 return;
170         }
171         tevent_req_set_callback(subreq, cli_symlink_close_done, req);
172 }
173
174 static void cli_symlink_close_done(struct tevent_req *subreq)
175 {
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);
180         NTSTATUS status;
181
182         status = cli_close_recv(subreq);
183         TALLOC_FREE(subreq);
184
185         if (tevent_req_nterror(req, status)) {
186                 return;
187         }
188         if (tevent_req_nterror(req, state->set_reparse_status)) {
189                 return;
190         }
191         tevent_req_done(req);
192 }
193
194 NTSTATUS cli_symlink_recv(struct tevent_req *req)
195 {
196         return tevent_req_simple_recv_ntstatus(req);
197 }
198
199 NTSTATUS cli_symlink(struct cli_state *cli, const char *oldname,
200                      const char *newname, uint32_t flags)
201 {
202         TALLOC_CTX *frame = talloc_stackframe();
203         struct event_context *ev;
204         struct tevent_req *req;
205         NTSTATUS status = NT_STATUS_NO_MEMORY;
206
207         if (cli_has_async_calls(cli)) {
208                 status = NT_STATUS_INVALID_PARAMETER;
209                 goto fail;
210         }
211         ev = event_context_init(frame);
212         if (ev == NULL) {
213                 goto fail;
214         }
215         req = cli_symlink_send(frame, ev, cli, oldname, newname, flags);
216         if (req == NULL) {
217                 goto fail;
218         }
219         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
220                 goto fail;
221         }
222         status = cli_symlink_recv(req);
223  fail:
224         TALLOC_FREE(frame);
225         return status;
226 }
227
228 struct cli_readlink_state {
229         struct tevent_context *ev;
230         struct cli_state *cli;
231         uint16_t fnum;
232
233         uint16_t setup[4];
234         NTSTATUS get_reparse_status;
235         uint8_t *data;
236         uint32_t num_data;
237 };
238
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);
242
243 struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx,
244                                      struct tevent_context *ev,
245                                      struct cli_state *cli,
246                                      const char *fname)
247 {
248         struct tevent_req *req, *subreq;
249         struct cli_readlink_state *state;
250
251         req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state);
252         if (req == NULL) {
253                 return NULL;
254         }
255         state->ev = ev;
256         state->cli = cli;
257
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);
264         }
265         tevent_req_set_callback(subreq, cli_readlink_opened, req);
266         return req;
267 }
268
269 static void cli_readlink_opened(struct tevent_req *subreq)
270 {
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);
275         NTSTATUS status;
276
277         status = cli_ntcreate_recv(subreq, &state->fnum);
278         TALLOC_FREE(subreq);
279         if (tevent_req_nterror(req, status)) {
280                 return;
281         }
282
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 */
287
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)) {
295                 return;
296         }
297         tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req);
298 }
299
300 static void cli_readlink_got_reparse_data(struct tevent_req *subreq)
301 {
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);
306
307         state->get_reparse_status = cli_trans_recv(
308                 subreq, state, NULL,
309                 NULL, 0, NULL,  /* rsetup */
310                 NULL, 0, NULL,  /* rparam */
311                 &state->data, 20, &state->num_data); /* rdata */
312         TALLOC_FREE(subreq);
313
314         subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
315         if (tevent_req_nomem(subreq, req)) {
316                 return;
317         }
318         tevent_req_set_callback(subreq, cli_readlink_closed, req);
319 }
320
321 static void cli_readlink_closed(struct tevent_req *subreq)
322 {
323         struct tevent_req *req = tevent_req_callback_data(
324                 subreq, struct tevent_req);
325         NTSTATUS status;
326
327         status = cli_close_recv(subreq);
328         TALLOC_FREE(subreq);
329         if (tevent_req_nterror(req, status)) {
330                 return;
331         }
332         tevent_req_done(req);
333 }
334
335 NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
336                            char **psubstitute_name, char **pprint_name,
337                            uint32_t *pflags)
338 {
339         struct cli_readlink_state *state = tevent_req_data(
340                 req, struct cli_readlink_state);
341         NTSTATUS status;
342         char *substitute_name;
343         char *print_name;
344         uint32_t flags;
345
346         if (tevent_req_is_nterror(req, &status)) {
347                 return status;
348         }
349
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;
354         }
355
356         if (psubstitute_name != NULL) {
357                 *psubstitute_name = talloc_move(mem_ctx, &substitute_name);
358         }
359         TALLOC_FREE(substitute_name);
360
361         if (pprint_name != NULL) {
362                 *pprint_name = talloc_move(mem_ctx, &print_name);
363         }
364         TALLOC_FREE(print_name);
365
366         if (pflags != NULL) {
367                 *pflags = flags;
368         }
369         return NT_STATUS_OK;
370 }
371
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)
375 {
376         TALLOC_CTX *frame = talloc_stackframe();
377         struct event_context *ev;
378         struct tevent_req *req;
379         NTSTATUS status = NT_STATUS_NO_MEMORY;
380
381         if (cli_has_async_calls(cli)) {
382                 status = NT_STATUS_INVALID_PARAMETER;
383                 goto fail;
384         }
385         ev = event_context_init(frame);
386         if (ev == NULL) {
387                 goto fail;
388         }
389         req = cli_readlink_send(frame, ev, cli, fname);
390         if (req == NULL) {
391                 goto fail;
392         }
393         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
394                 goto fail;
395         }
396         status = cli_readlink_recv(req, mem_ctx, psubstitute_name,
397                                    pprint_name, pflags);
398  fail:
399         TALLOC_FREE(frame);
400         return status;
401 }