s3/torture: add POSIX-READLINK test
[samba.git] / source3 / torture / test_posix.c
1 /*
2    Unix SMB/CIFS implementation.
3    Copyright (C) Ralph Boehme 2020
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "includes.h"
20 #include "torture/proto.h"
21 #include "libcli/security/security.h"
22 #include "libsmb/libsmb.h"
23 #include "libsmb/clirap.h"
24 #include "libsmb/proto.h"
25 #include "../libcli/smb/smbXcli_base.h"
26
27 extern struct cli_credentials *torture_creds;
28 extern fstring host, workgroup, share, password, username, myname;
29
30 struct posix_test_entry {
31         const char *name;
32         const char *target;
33         const char *expected;
34         uint64_t returned_size;
35         bool ok;
36 };
37
38 static NTSTATUS posix_ls_fn(struct file_info *finfo,
39                             const char *name,
40                             void *_state)
41 {
42         struct posix_test_entry *state =
43                 (struct posix_test_entry *)_state;
44
45         for (; state->name != NULL; state++) {
46                 if (strequal(finfo->name, state->expected)) {
47                         state->ok = true;
48                         state->returned_size = finfo->size;
49                         break;
50                 }
51         }
52
53         return NT_STATUS_OK;
54 }
55
56 static void posix_test_entries_reset(struct posix_test_entry *state)
57 {
58         for (; state->name != NULL; state++) {
59                 state->ok = false;
60         }
61 }
62
63 static bool posix_test_entry_check(struct posix_test_entry *state,
64                                    const char *name,
65                                    bool expected,
66                                    uint64_t expected_size)
67 {
68         bool result = false;
69
70         for (; state->name != NULL; state++) {
71                 if (strequal(name, state->name)) {
72                         result = state->ok;
73                         break;
74                 }
75         }
76         if (state->name == NULL) {
77                 printf("test failed, unknown name: %s\n", name);
78                 return false;
79         }
80
81         if (expected == result) {
82                 return true;
83         }
84
85         printf("test failed, %s: %s\n",
86                expected ? "missing" : "unexpected",
87                name);
88
89         return false;
90 }
91
92 /*
93   Test non-POSIX vs POSIX ls * of symlinks
94  */
95 bool run_posix_ls_wildcard_test(int dummy)
96 {
97         TALLOC_CTX *frame = NULL;
98         struct cli_state *cli_unix = NULL;
99         struct cli_state *cli_win = NULL;
100         uint16_t fnum = (uint16_t)-1;
101         NTSTATUS status;
102         const char *file = "file";
103         const char *symlnk_dangling = "dangling";
104         const char *symlnk_dst_dangling = "xxxxxxx";
105         const char *symlnk_in_share = "symlnk_in_share";
106         const char *symlnk_dst_in_share = file;
107         const char *symlnk_outside_share = "symlnk_outside_share";
108         const char *symlnk_dst_outside_share = "/etc/passwd";
109         struct posix_test_entry state[] = {
110                 {
111                         .name = symlnk_dangling,
112                         .target = symlnk_dst_dangling,
113                         .expected = symlnk_dangling,
114                 }, {
115                         .name = symlnk_in_share,
116                         .target = symlnk_dst_in_share,
117                         .expected = symlnk_in_share,
118                 }, {
119                         .name = symlnk_outside_share,
120                         .target = symlnk_dst_outside_share,
121                         .expected = symlnk_outside_share,
122                 }, {
123                         .name = NULL,
124                 }
125         };
126         int i;
127         bool correct = false;
128
129         frame = talloc_stackframe();
130
131         printf("Starting POSIX-LS-WILDCARD test\n");
132
133         if (!torture_open_connection(&cli_unix, 0)) {
134                 TALLOC_FREE(frame);
135                 return false;
136         }
137
138         if (!torture_open_connection(&cli_win, 0)) {
139                 TALLOC_FREE(frame);
140                 return false;
141         }
142
143         torture_conn_set_sockopt(cli_unix);
144         torture_conn_set_sockopt(cli_win);
145
146         status = torture_setup_unix_extensions(cli_unix);
147         if (!NT_STATUS_IS_OK(status)) {
148                 TALLOC_FREE(frame);
149                 return false;
150         }
151
152         cli_posix_unlink(cli_unix, file);
153         cli_posix_unlink(cli_unix, symlnk_dangling);
154         cli_posix_unlink(cli_unix, symlnk_in_share);
155         cli_posix_unlink(cli_unix, symlnk_outside_share);
156
157         status = cli_posix_open(cli_unix,
158                                 file,
159                                 O_RDWR|O_CREAT,
160                                 0666,
161                                 &fnum);
162         if (!NT_STATUS_IS_OK(status)) {
163                 printf("cli_posix_open of %s failed error %s\n",
164                        file,
165                        nt_errstr(status));
166                 goto out;
167         }
168
169         status = cli_close(cli_unix, fnum);
170         if (!NT_STATUS_IS_OK(status)) {
171                 printf("cli_close failed %s\n", nt_errstr(status));
172                 goto out;
173         }
174         fnum = (uint16_t)-1;
175
176         for (i = 0; state[i].name != NULL; i++) {
177                 status = cli_posix_symlink(cli_unix,
178                                            state[i].target,
179                                            state[i].name);
180                 if (!NT_STATUS_IS_OK(status)) {
181                         printf("POSIX symlink of %s failed (%s)\n",
182                                symlnk_dangling, nt_errstr(status));
183                         goto out;
184                 }
185         }
186
187         printf("Doing Windows ls *\n");
188
189         status = cli_list(cli_win, "*", 0, posix_ls_fn, state);
190         if (!NT_STATUS_IS_OK(status)) {
191                 printf("cli_close failed %s\n", nt_errstr(status));
192                 goto out;
193         }
194
195         if (!posix_test_entry_check(state, symlnk_dangling, false, 0)) {
196                 goto out;
197         }
198         if (!posix_test_entry_check(state, symlnk_outside_share, false, 0)) {
199                 goto out;
200         }
201         if (!posix_test_entry_check(state, symlnk_in_share, true, 0)) {
202                 goto out;
203         }
204
205         posix_test_entries_reset(state);
206
207         printf("Doing POSIX ls *\n");
208
209         status = cli_list(cli_unix, "*", 0, posix_ls_fn, state);
210         if (!NT_STATUS_IS_OK(status)) {
211                 printf("cli_close failed %s\n", nt_errstr(status));
212                 goto out;
213         }
214
215         if (!posix_test_entry_check(state,
216                                     symlnk_dangling,
217                                     true,
218                                     strlen(symlnk_dst_dangling)))
219         {
220                 goto out;
221         }
222         if (!posix_test_entry_check(state,
223                                     symlnk_outside_share,
224                                     true,
225                                     strlen(symlnk_dst_outside_share)))
226         {
227                 goto out;
228         }
229         if (!posix_test_entry_check(state,
230                                     symlnk_in_share,
231                                     true,
232                                     strlen(symlnk_dst_in_share))) {
233                 goto out;
234         }
235
236         printf("POSIX-LS-WILDCARD test passed\n");
237         correct = true;
238
239 out:
240         cli_posix_unlink(cli_unix, file);
241         cli_posix_unlink(cli_unix, symlnk_dangling);
242         cli_posix_unlink(cli_unix, symlnk_in_share);
243         cli_posix_unlink(cli_unix, symlnk_outside_share);
244
245         if (!torture_close_connection(cli_unix)) {
246                 correct = false;
247         }
248         if (!torture_close_connection(cli_win)) {
249                 correct = false;
250         }
251
252         TALLOC_FREE(frame);
253         return correct;
254 }
255
256 /*
257   Test non-POSIX vs POSIX ls single of symlinks
258  */
259 bool run_posix_ls_single_test(int dummy)
260 {
261         TALLOC_CTX *frame = NULL;
262         struct cli_state *cli_unix = NULL;
263         struct cli_state *cli_win = NULL;
264         uint16_t fnum = (uint16_t)-1;
265         NTSTATUS status;
266         const char *file = "file";
267         const char *symlnk_dangling = "dangling";
268         const char *symlnk_dst_dangling = "xxxxxxx";
269         const char *symlnk_in_share = "symlnk_in_share";
270         const char *symlnk_dst_in_share = file;
271         const char *symlnk_outside_share = "symlnk_outside_share";
272         const char *symlnk_dst_outside_share = "/etc/passwd";
273         struct posix_test_entry state[] = {
274                 {
275                         .name = symlnk_dangling,
276                         .target = symlnk_dst_dangling,
277                         .expected = symlnk_dangling,
278                 }, {
279                         .name = symlnk_in_share,
280                         .target = symlnk_dst_in_share,
281                         .expected = symlnk_in_share,
282                 }, {
283                         .name = symlnk_outside_share,
284                         .target = symlnk_dst_outside_share,
285                         .expected = symlnk_outside_share,
286                 }, {
287                         .name = NULL,
288                 }
289         };
290         int i;
291         bool correct = false;
292
293         frame = talloc_stackframe();
294
295         printf("Starting POSIX-LS-SINGLE test\n");
296
297         if (!torture_open_connection(&cli_unix, 0)) {
298                 TALLOC_FREE(frame);
299                 return false;
300         }
301
302         if (!torture_init_connection(&cli_win)) {
303                 TALLOC_FREE(frame);
304                 return false;
305         }
306
307         status = smbXcli_negprot(cli_win->conn,
308                                  cli_win->timeout,
309                                  lp_client_min_protocol(),
310                                  lp_client_max_protocol());
311         if (!NT_STATUS_IS_OK(status)) {
312                 printf("smbXcli_negprot returned %s\n", nt_errstr(status));
313                 TALLOC_FREE(frame);
314                 return false;
315         }
316
317         status = cli_session_setup_creds(cli_win, torture_creds);
318         if (!NT_STATUS_IS_OK(status)) {
319                 printf("smb2cli_sesssetup returned %s\n", nt_errstr(status));
320                 TALLOC_FREE(frame);
321                 return false;
322         }
323
324         status = cli_tree_connect(cli_win, share, "?????", NULL);
325         if (!NT_STATUS_IS_OK(status)) {
326                 printf("cli_tree_connect returned %s\n", nt_errstr(status));
327                 TALLOC_FREE(frame);
328                 return false;
329         }
330         torture_conn_set_sockopt(cli_unix);
331         torture_conn_set_sockopt(cli_win);
332
333         status = torture_setup_unix_extensions(cli_unix);
334         if (!NT_STATUS_IS_OK(status)) {
335                 TALLOC_FREE(frame);
336                 return false;
337         }
338
339         cli_posix_unlink(cli_unix, file);
340         cli_posix_unlink(cli_unix, symlnk_dangling);
341         cli_posix_unlink(cli_unix, symlnk_in_share);
342         cli_posix_unlink(cli_unix, symlnk_outside_share);
343
344         status = cli_posix_open(cli_unix,
345                                 file,
346                                 O_RDWR|O_CREAT,
347                                 0666,
348                                 &fnum);
349         if (!NT_STATUS_IS_OK(status)) {
350                 printf("cli_posix_open of %s failed error %s\n",
351                        file,
352                        nt_errstr(status));
353                 goto out;
354         }
355
356         status = cli_close(cli_unix, fnum);
357         if (!NT_STATUS_IS_OK(status)) {
358                 printf("cli_close failed %s\n", nt_errstr(status));
359                 goto out;
360         }
361         fnum = (uint16_t)-1;
362
363         for (i = 0; state[i].name != NULL; i++) {
364                 status = cli_posix_symlink(cli_unix,
365                                            state[i].target,
366                                            state[i].name);
367                 if (!NT_STATUS_IS_OK(status)) {
368                         printf("POSIX symlink of %s failed (%s)\n",
369                                symlnk_dangling, nt_errstr(status));
370                         goto out;
371                 }
372         }
373
374         printf("Doing Windows ls single\n");
375
376         cli_list(cli_win, symlnk_dangling, 0, posix_ls_fn, state);
377         cli_list(cli_win, symlnk_outside_share, 0, posix_ls_fn, state);
378         cli_list(cli_win, symlnk_in_share, 0, posix_ls_fn, state);
379
380         if (!posix_test_entry_check(state, symlnk_dangling, false, 0)) {
381                 goto out;
382         }
383         if (!posix_test_entry_check(state, symlnk_outside_share, false, 0)) {
384                 goto out;
385         }
386         if (!posix_test_entry_check(state, symlnk_in_share, true, 0)) {
387                 goto out;
388         }
389
390         posix_test_entries_reset(state);
391
392         printf("Doing POSIX ls single\n");
393
394         cli_list(cli_unix, symlnk_dangling, 0, posix_ls_fn, state);
395         cli_list(cli_unix, symlnk_outside_share, 0, posix_ls_fn, state);
396         cli_list(cli_unix, symlnk_in_share, 0, posix_ls_fn, state);
397
398         if (!posix_test_entry_check(state,
399                                     symlnk_dangling,
400                                     true,
401                                     strlen(symlnk_dst_dangling)))
402         {
403                 goto out;
404         }
405         if (!posix_test_entry_check(state,
406                                     symlnk_outside_share,
407                                     true,
408                                     strlen(symlnk_dst_outside_share)))
409         {
410                 goto out;
411         }
412         if (!posix_test_entry_check(state,
413                                     symlnk_in_share,
414                                     true,
415                                     strlen(symlnk_dst_in_share))) {
416                 goto out;
417         }
418
419         printf("POSIX-LS-SINGLE test passed\n");
420         correct = true;
421
422 out:
423         cli_posix_unlink(cli_unix, file);
424         cli_posix_unlink(cli_unix, symlnk_dangling);
425         cli_posix_unlink(cli_unix, symlnk_in_share);
426         cli_posix_unlink(cli_unix, symlnk_outside_share);
427
428         if (!torture_close_connection(cli_unix)) {
429                 correct = false;
430         }
431         if (!torture_close_connection(cli_win)) {
432                 correct = false;
433         }
434
435         TALLOC_FREE(frame);
436         return correct;
437 }
438
439 /*
440   Test POSIX readlink of symlinks
441  */
442 bool run_posix_readlink_test(int dummy)
443 {
444         TALLOC_CTX *frame = NULL;
445         struct cli_state *cli_unix = NULL;
446         uint16_t fnum = (uint16_t)-1;
447         NTSTATUS status;
448         const char *file = "file";
449         const char *symlnk_dangling = "dangling";
450         const char *symlnk_dst_dangling = "xxxxxxx";
451         const char *symlnk_in_share = "symlnk_in_share";
452         const char *symlnk_dst_in_share = file;
453         const char *symlnk_outside_share = "symlnk_outside_share";
454         const char *symlnk_dst_outside_share = "/etc/passwd";
455         struct posix_test_entry state[] = {
456                 {
457                         .name = symlnk_dangling,
458                         .target = symlnk_dst_dangling,
459                         .expected = symlnk_dangling,
460                 }, {
461                         .name = symlnk_in_share,
462                         .target = symlnk_dst_in_share,
463                         .expected = symlnk_in_share,
464                 }, {
465                         .name = symlnk_outside_share,
466                         .target = symlnk_dst_outside_share,
467                         .expected = symlnk_outside_share,
468                 }, {
469                         .name = NULL,
470                 }
471         };
472         int i;
473         bool correct = false;
474
475         frame = talloc_stackframe();
476
477         printf("Starting POSIX-READLINK test\n");
478
479         if (!torture_open_connection(&cli_unix, 0)) {
480                 TALLOC_FREE(frame);
481                 return false;
482         }
483
484         torture_conn_set_sockopt(cli_unix);
485
486         status = torture_setup_unix_extensions(cli_unix);
487         if (!NT_STATUS_IS_OK(status)) {
488                 TALLOC_FREE(frame);
489                 return false;
490         }
491
492         cli_posix_unlink(cli_unix, file);
493         cli_posix_unlink(cli_unix, symlnk_dangling);
494         cli_posix_unlink(cli_unix, symlnk_in_share);
495         cli_posix_unlink(cli_unix, symlnk_outside_share);
496
497         status = cli_posix_open(cli_unix,
498                                 file,
499                                 O_RDWR|O_CREAT,
500                                 0666,
501                                 &fnum);
502         if (!NT_STATUS_IS_OK(status)) {
503                 printf("cli_posix_open of %s failed error %s\n",
504                        file,
505                        nt_errstr(status));
506                 goto out;
507         }
508
509         status = cli_close(cli_unix, fnum);
510         if (!NT_STATUS_IS_OK(status)) {
511                 printf("cli_close failed %s\n", nt_errstr(status));
512                 goto out;
513         }
514         fnum = (uint16_t)-1;
515
516         for (i = 0; state[i].name != NULL; i++) {
517                 status = cli_posix_symlink(cli_unix,
518                                            state[i].target,
519                                            state[i].name);
520                 if (!NT_STATUS_IS_OK(status)) {
521                         printf("POSIX symlink of %s failed (%s)\n",
522                                symlnk_dangling, nt_errstr(status));
523                         goto out;
524                 }
525         }
526
527         for (i = 0; state[i].name != NULL; i++) {
528                 char *target = NULL;
529
530                 status = cli_posix_readlink(cli_unix,
531                                             state[i].name,
532                                             talloc_tos(),
533                                             &target);
534                 if (!NT_STATUS_IS_OK(status)) {
535                         printf("POSIX readlink on %s failed (%s)\n",
536                                state[i].name, nt_errstr(status));
537                         goto out;
538                 }
539                 if (strequal(target, state[i].target)) {
540                         state[i].ok = true;
541                         state[i].returned_size = strlen(target);
542                 }
543         }
544
545         if (!posix_test_entry_check(state,
546                                     symlnk_dangling,
547                                     true,
548                                     strlen(symlnk_dst_dangling)))
549         {
550                 goto out;
551         }
552         if (!posix_test_entry_check(state,
553                                     symlnk_outside_share,
554                                     true,
555                                     strlen(symlnk_dst_outside_share)))
556         {
557                 goto out;
558         }
559         if (!posix_test_entry_check(state,
560                                     symlnk_in_share,
561                                     true,
562                                     strlen(symlnk_dst_in_share))) {
563                 goto out;
564         }
565
566         printf("POSIX-READLINK test passed\n");
567         correct = true;
568
569 out:
570         cli_posix_unlink(cli_unix, file);
571         cli_posix_unlink(cli_unix, symlnk_dangling);
572         cli_posix_unlink(cli_unix, symlnk_in_share);
573         cli_posix_unlink(cli_unix, symlnk_outside_share);
574
575         if (!torture_close_connection(cli_unix)) {
576                 correct = false;
577         }
578
579         TALLOC_FREE(frame);
580         return correct;
581 }