lib/util: Standardize use of st_[acm]time ns
[samba.git] / source4 / torture / libsmbclient / libsmbclient.c
1 /*
2    Unix SMB/CIFS implementation.
3    SMB torture tester
4    Copyright (C) Guenther Deschner 2010
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/dir.h"
22 #include "torture/smbtorture.h"
23 #include "auth/credentials/credentials.h"
24 #include "lib/cmdline/popt_common.h"
25 #include <libsmbclient.h>
26 #include "torture/libsmbclient/proto.h"
27 #include "lib/param/loadparm.h"
28 #include "lib/param/param_global.h"
29 #include "dynconfig.h"
30 #include "lib/util/time.h"
31
32 /* test string to compare with when debug_callback is called */
33 #define TEST_STRING "smbc_setLogCallback test"
34
35 /* Dummy log callback function */
36 static void debug_callback(void *private_ptr, int level, const char *msg)
37 {
38         bool *found = private_ptr;
39         if (strstr(msg, TEST_STRING) != NULL) {
40                 *found = true;
41         }
42         return;
43 }
44
45 static void auth_callback(const char *srv,
46                           const char *shr,
47                           char *wg, int wglen,
48                           char *un, int unlen,
49                           char *pw, int pwlen)
50 {
51         const char *workgroup =
52                 cli_credentials_get_domain(popt_get_cmdline_credentials());
53         const char *username =
54                 cli_credentials_get_username(popt_get_cmdline_credentials());
55         const char *password =
56                 cli_credentials_get_password(popt_get_cmdline_credentials());
57         ssize_t ret;
58
59         if (workgroup != NULL) {
60                 ret = strlcpy(wg, workgroup, wglen);
61                 if (ret >= wglen) {
62                         abort();
63                 }
64         }
65
66         if (username != NULL) {
67                 ret = strlcpy(un, username, unlen);
68                 if (ret >= unlen) {
69                         abort();
70                 }
71         }
72
73         if (password != NULL) {
74                 ret = strlcpy(pw, password, pwlen);
75                 if (ret >= pwlen) {
76                         abort();
77                 }
78         }
79 };
80
81 bool torture_libsmbclient_init_context(struct torture_context *tctx,
82                                        SMBCCTX **ctx_p)
83 {
84         const char *workgroup =
85                 cli_credentials_get_domain(popt_get_cmdline_credentials());
86         const char *username =
87                 cli_credentials_get_username(popt_get_cmdline_credentials());
88         const char *client_proto =
89                 torture_setting_string(tctx, "clientprotocol", NULL);
90         SMBCCTX *ctx = NULL;
91         SMBCCTX *p = NULL;
92         bool ok = true;
93         int dbglevel = DEBUGLEVEL;
94
95         ctx = smbc_new_context();
96         torture_assert_not_null_goto(tctx,
97                                      ctx,
98                                      ok,
99                                      out,
100                                      "Failed to create new context");
101
102         p = smbc_init_context(ctx);
103         torture_assert_not_null_goto(tctx,
104                                      p,
105                                      ok,
106                                      out,
107                                      "Failed to initialize context");
108
109         smbc_setDebug(ctx, dbglevel);
110         smbc_setOptionDebugToStderr(ctx, 1);
111
112         if (workgroup != NULL) {
113                 smbc_setWorkgroup(ctx, workgroup);
114         }
115         if (username != NULL) {
116                 smbc_setUser(ctx, username);
117         }
118
119         smbc_setFunctionAuthData(ctx, auth_callback);
120
121         if (client_proto != NULL) {
122                 smbc_setOptionProtocols(ctx, client_proto, client_proto);
123         }
124
125         *ctx_p = ctx;
126
127 out:
128         if (!ok) {
129                 smbc_free_context(ctx, 1);
130         }
131
132         return ok;
133 }
134
135 static bool torture_libsmbclient_version(struct torture_context *tctx)
136 {
137         torture_comment(tctx, "Testing smbc_version\n");
138
139         torture_assert(tctx, smbc_version(), "failed to get version");
140
141         return true;
142 }
143
144 static bool torture_libsmbclient_initialize(struct torture_context *tctx)
145 {
146         SMBCCTX *ctx;
147         bool ret = false;
148
149         torture_comment(tctx, "Testing smbc_new_context\n");
150
151         ctx = smbc_new_context();
152         torture_assert(tctx, ctx, "failed to get new context");
153
154         torture_comment(tctx, "Testing smbc_init_context\n");
155
156         torture_assert(tctx, smbc_init_context(ctx), "failed to init context");
157
158         smbc_setLogCallback(ctx, &ret, debug_callback);
159         DEBUG(0, (TEST_STRING"\n"));
160         torture_assert(tctx, ret, "Failed debug_callback not called");
161         ret = false;
162         smbc_setLogCallback(ctx, NULL, NULL);
163         DEBUG(0, (TEST_STRING"\n"));
164         torture_assert(tctx, !ret, "Failed debug_callback called");
165
166         smbc_free_context(ctx, 1);
167
168         return true;
169 }
170
171 static bool torture_libsmbclient_setConfiguration(struct torture_context *tctx)
172 {
173         SMBCCTX *ctx;
174         struct loadparm_global *global_config = NULL;
175         const char *new_smb_conf = torture_setting_string(tctx,
176                                 "replace_smbconf",
177                                 "");
178
179         ctx = smbc_new_context();
180         torture_assert_not_null(tctx, ctx, "failed to get new context");
181
182         torture_assert_not_null(
183                 tctx, smbc_init_context(ctx), "failed to init context");
184
185         torture_comment(tctx, "Testing smbc_setConfiguration - new file %s\n",
186                 new_smb_conf);
187
188         global_config = get_globals();
189         torture_assert(tctx, global_config, "Global Config is NULL");
190
191         /* check configuration before smbc_setConfiguration call */
192         torture_comment(tctx, "'workgroup' before setConfiguration %s\n",
193                         global_config->workgroup);
194         torture_comment(tctx, "'client min protocol' before "
195                         "setConfiguration %d\n",
196                         global_config->client_min_protocol);
197         torture_comment(tctx, "'client max protocol' before "
198                         "setConfiguration %d\n",
199                         global_config->_client_max_protocol);
200         torture_comment(tctx, "'client signing' before setConfiguration %d\n",
201                         global_config->client_signing);
202         torture_comment(tctx, "'deadtime' before setConfiguration %d\n",
203                         global_config->deadtime);
204
205         torture_assert_int_equal(tctx, smbc_setConfiguration(ctx, new_smb_conf),
206                         0, "setConfiguration conf file not found");
207
208         /* verify configuration */
209         torture_assert_str_equal(tctx, global_config->workgroup,
210                         "NEW_WORKGROUP",
211                         "smbc_setConfiguration failed, "
212                         "'workgroup' not updated");
213         torture_assert_int_equal(tctx, global_config->client_min_protocol, 7,
214                         "smbc_setConfiguration failed, 'client min protocol' "
215                         "not updated");
216         torture_assert_int_equal(tctx, global_config->_client_max_protocol, 13,
217                         "smbc_setConfiguration failed, 'client max protocol' "
218                         "not updated");
219         torture_assert_int_equal(tctx, global_config->client_signing, 1,
220                         "smbc_setConfiguration failed, 'client signing' "
221                         "not updated");
222         torture_assert_int_equal(tctx, global_config->deadtime, 5,
223                         "smbc_setConfiguration failed, 'deadtime' not updated");
224
225         /* Restore configuration to default */
226         smbc_setConfiguration(ctx, get_dyn_CONFIGFILE());
227
228         smbc_free_context(ctx, 1);
229
230         return true;
231 }
232
233 static bool test_opendir(struct torture_context *tctx,
234                          SMBCCTX *ctx,
235                          const char *fname,
236                          bool expect_success)
237 {
238         int handle, ret;
239
240         torture_comment(tctx, "Testing smbc_opendir(%s)\n", fname);
241
242         handle = smbc_opendir(fname);
243         if (!expect_success) {
244                 return true;
245         }
246         if (handle < 0) {
247                 torture_fail(tctx, talloc_asprintf(tctx, "failed to obain file handle for '%s'", fname));
248         }
249
250         ret = smbc_closedir(handle);
251         torture_assert_int_equal(tctx, ret, 0,
252                 talloc_asprintf(tctx, "failed to close file handle for '%s'", fname));
253
254         return true;
255 }
256
257 static bool torture_libsmbclient_opendir(struct torture_context *tctx)
258 {
259         size_t i;
260         SMBCCTX *ctx;
261         bool ret = true;
262         const char *bad_urls[] = {
263                 "",
264                 NULL,
265                 "smb",
266                 "smb:",
267                 "smb:/",
268                 "smb:///",
269                 "bms://",
270                 ":",
271                 ":/",
272                 "://",
273                 ":///",
274                 "/",
275                 "//",
276                 "///"
277         };
278         const char *good_urls[] = {
279                 "smb://",
280                 "smb://WORKGROUP",
281                 "smb://WORKGROUP/"
282         };
283
284         torture_assert(tctx, torture_libsmbclient_init_context(tctx, &ctx), "");
285         smbc_set_context(ctx);
286
287         for (i=0; i < ARRAY_SIZE(bad_urls); i++) {
288                 ret &= test_opendir(tctx, ctx, bad_urls[i], false);
289         }
290         for (i=0; i < ARRAY_SIZE(good_urls); i++) {
291                 ret &= test_opendir(tctx, ctx, good_urls[i], true);
292         }
293
294         smbc_free_context(ctx, 1);
295
296         return ret;
297 }
298
299 static bool torture_libsmbclient_readdirplus(struct torture_context *tctx)
300 {
301         SMBCCTX *ctx;
302         int ret = -1;
303         int dhandle = -1;
304         int fhandle = -1;
305         bool found = false;
306         const char *filename = NULL;
307         const char *smburl = torture_setting_string(tctx, "smburl", NULL);
308
309         if (smburl == NULL) {
310                 torture_fail(tctx,
311                         "option --option=torture:smburl="
312                         "smb://user:password@server/share missing\n");
313         }
314
315         torture_assert(tctx, torture_libsmbclient_init_context(tctx, &ctx), "");
316         smbc_set_context(ctx);
317
318         filename = talloc_asprintf(tctx,
319                                 "%s/test_readdirplus.txt",
320                                 smburl);
321         if (filename == NULL) {
322                 torture_fail(tctx,
323                         "talloc fail\n");
324         }
325         /* Ensure the file doesn't exist. */
326         smbc_unlink(filename);
327
328         /* Create it. */
329         fhandle = smbc_creat(filename, 0666);
330         if (fhandle < 0) {
331                 torture_fail(tctx,
332                         talloc_asprintf(tctx,
333                                 "failed to create file '%s': %s",
334                                 filename,
335                                 strerror(errno)));
336         }
337         ret = smbc_close(fhandle);
338         torture_assert_int_equal(tctx,
339                 ret,
340                 0,
341                 talloc_asprintf(tctx,
342                         "failed to close handle for '%s'",
343                         filename));
344
345         dhandle = smbc_opendir(smburl);
346         if (dhandle < 0) {
347                 int saved_errno = errno;
348                 smbc_unlink(filename);
349                 torture_fail(tctx,
350                         talloc_asprintf(tctx,
351                                 "failed to obtain "
352                                 "directory handle for '%s' : %s",
353                                 smburl,
354                                 strerror(saved_errno)));
355         }
356
357         /* Readdirplus to ensure we see the new file. */
358         for (;;) {
359                 const struct libsmb_file_info *exstat =
360                         smbc_readdirplus(dhandle);
361                 if (exstat == NULL) {
362                         break;
363                 }
364                 if (strcmp(exstat->name, "test_readdirplus.txt") == 0) {
365                         found = true;
366                         break;
367                 }
368         }
369
370         /* Remove it again. */
371         smbc_unlink(filename);
372         ret = smbc_closedir(dhandle);
373         torture_assert_int_equal(tctx,
374                 ret,
375                 0,
376                 talloc_asprintf(tctx,
377                         "failed to close directory handle for '%s'",
378                         smburl));
379
380         smbc_free_context(ctx, 1);
381
382         if (!found) {
383                 torture_fail(tctx,
384                         talloc_asprintf(tctx,
385                                 "failed to find file '%s'",
386                                 filename));
387         }
388
389         return true;
390 }
391
392 static bool torture_libsmbclient_readdirplus_seek(struct torture_context *tctx)
393 {
394         SMBCCTX *ctx;
395         int ret = -1;
396         int dhandle = -1;
397         int fhandle = -1;
398         const char *dname = NULL;
399         const char *full_filename[100] = {0};
400         const char *filename[100] = {0};
401         const struct libsmb_file_info *direntries[102] = {0};
402         unsigned int i = 0;
403         const char *smburl = torture_setting_string(tctx, "smburl", NULL);
404         bool success = false;
405         off_t telldir_50 = (off_t)-1;
406         off_t telldir_20 = (off_t)-1;
407         size_t getdentries_size = 0;
408         struct smbc_dirent *getdentries = NULL;
409         struct smbc_dirent *dirent_20 = NULL;
410         const struct libsmb_file_info *direntries_20 = NULL;
411         const struct libsmb_file_info *direntriesplus_20 = NULL;
412         const char *plus2_stat_path = NULL;
413         struct stat st = {0};
414         struct stat st2 = {0};
415
416         torture_assert_not_null(
417                 tctx,
418                 smburl,
419                 "option --option=torture:smburl="
420                 "smb://user:password@server/share missing\n");
421
422         DEBUG(0,("torture_libsmbclient_readdirplus_seek start\n"));
423
424         torture_assert(tctx, torture_libsmbclient_init_context(tctx, &ctx), "");
425         smbc_set_context(ctx);
426
427         dname = talloc_asprintf(tctx,
428                                 "%s/rd_seek",
429                                 smburl);
430         torture_assert_not_null_goto(
431                 tctx, dname, success, done, "talloc fail\n");
432
433         /* Ensure the files don't exist. */
434         for (i = 0; i < 100; i++) {
435                 filename[i] = talloc_asprintf(tctx,
436                                 "test_readdirplus_%u.txt",
437                                 i);
438                 torture_assert_not_null_goto(
439                         tctx, filename[i], success, done, "talloc fail");
440                 full_filename[i] = talloc_asprintf(tctx,
441                                 "%s/%s",
442                                 dname,
443                                 filename[i]);
444                 torture_assert_not_null_goto(
445                         tctx, full_filename[i], success, done, "talloc fail");
446                 (void)smbc_unlink(full_filename[i]);
447         }
448         /* Ensure the directory doesn't exist. */
449         (void)smbc_rmdir(dname);
450
451         /* Create containing directory. */
452         ret = smbc_mkdir(dname, 0777);
453         torture_assert_goto(
454                 tctx,
455                 ret == 0,
456                 success,
457                 done,
458                 talloc_asprintf(tctx,
459                                 "failed to create directory '%s': %s",
460                                 dname,
461                                 strerror(errno)));
462
463         DEBUG(0,("torture_libsmbclient_readdirplus_seek create\n"));
464
465         /* Create them. */
466         for (i = 0; i < 100; i++) {
467                 fhandle = smbc_creat(full_filename[i], 0666);
468                 if (fhandle < 0) {
469                         torture_fail_goto(tctx,
470                                 done,
471                                 talloc_asprintf(tctx,
472                                         "failed to create file '%s': %s",
473                                         full_filename[i],
474                                         strerror(errno)));
475                 }
476                 ret = smbc_close(fhandle);
477                 torture_assert_int_equal_goto(tctx,
478                         ret,
479                         0,
480                         success,
481                         done,
482                         talloc_asprintf(tctx,
483                                 "failed to close handle for '%s'",
484                                 full_filename[i]));
485         }
486
487         DEBUG(0,("torture_libsmbclient_readdirplus_seek enum\n"));
488
489         /* Now enumerate the directory. */
490         dhandle = smbc_opendir(dname);
491         torture_assert_goto(
492                 tctx,
493                 dhandle >= 0,
494                 success,
495                 done,
496                 talloc_asprintf(tctx,
497                                 "failed to obtain "
498                                 "directory handle for '%s' : %s",
499                                 dname,
500                                 strerror(errno)));
501
502         /* Read all the files. 100 we created plus . and .. */
503         for (i = 0; i < 102; i++) {
504                 bool found = false;
505                 unsigned int j;
506
507                 direntries[i] = smbc_readdirplus(dhandle);
508                 if (direntries[i] == NULL) {
509                         break;
510                 }
511
512                 /* Store at offset 50. */
513                 if (i == 50) {
514                         telldir_50 = smbc_telldir(dhandle);
515                         torture_assert_goto(
516                                 tctx,
517                                 telldir_50 != (off_t)-1,
518                                 success,
519                                 done,
520                                 talloc_asprintf(tctx,
521                                                 "telldir failed file %s\n",
522                                                 direntries[i]->name));
523                 }
524
525                 if (ISDOT(direntries[i]->name)) {
526                         continue;
527                 }
528                 if (ISDOTDOT(direntries[i]->name)) {
529                         continue;
530                 }
531
532                 /* Ensure all our files exist. */
533                 for (j = 0; j < 100; j++) {
534                         if (strcmp(direntries[i]->name,
535                                 filename[j]) == 0) {
536                                 found = true;
537                         }
538                 }
539                 torture_assert_goto(
540                         tctx,
541                         found,
542                         success,
543                         done,
544                         talloc_asprintf(tctx,
545                                         "failed to find file %s\n",
546                                         direntries[i]->name));
547         }
548
549         /*
550          * We're seeking on in-memory lists here, so
551          * whilst the handle is open we really should
552          * get the same files back in the same order.
553          */
554
555         ret = smbc_lseekdir(dhandle, telldir_50);
556         torture_assert_int_equal_goto(tctx,
557                 ret,
558                 0,
559                 success,
560                 done,
561                 talloc_asprintf(tctx,
562                         "failed to seek (50) directory handle for '%s'",
563                         dname));
564
565         DEBUG(0,("torture_libsmbclient_readdirplus_seek seek\n"));
566
567         for (i = 51; i < 102; i++) {
568                 const struct libsmb_file_info *entry =
569                                 smbc_readdirplus(dhandle);
570                 torture_assert_goto(
571                         tctx,
572                         entry == direntries[i],
573                         success,
574                         done,
575                         talloc_asprintf(tctx,
576                                         "after seek - failed to find "
577                                         "file %s - got %s\n",
578                                         direntries[i]->name,
579                                         entry->name));
580         }
581
582         /* Seek back to the start. */
583         ret = smbc_lseekdir(dhandle, 0);
584         torture_assert_int_equal_goto(tctx,
585                 ret,
586                 0,
587                 success,
588                 done,
589                 talloc_asprintf(tctx,
590                         "failed to seek directory handle to start for '%s'",
591                         dname));
592
593         /*
594          * Mix getdents/readdir/readdirplus with lseek to ensure
595          * we get the same result.
596          */
597
598         /* Allocate the space for 20 entries.
599          * Tricky as we need to allocate 20 struct smbc_dirent's + space
600          * for the name lengths.
601          */
602         getdentries_size = 20 * (sizeof(struct smbc_dirent) +
603                                 strlen("test_readdirplus_1000.txt") + 1);
604
605         getdentries = (struct smbc_dirent *)talloc_array_size(tctx,
606                                                 getdentries_size,
607                                                 1);
608         torture_assert_not_null_goto(
609                 tctx,
610                 getdentries,
611                 success,
612                 done,
613                 "talloc fail");
614
615         ret = smbc_getdents(dhandle, getdentries, getdentries_size);
616         torture_assert_goto(tctx,
617                 (ret != -1),
618                 success,
619                 done,
620                 talloc_asprintf(tctx,
621                         "smbd_getdents(1) for '%s' failed\n",
622                         dname));
623
624         telldir_20 = smbc_telldir(dhandle);
625         torture_assert_goto(
626                 tctx,
627                 telldir_20 != (off_t)-1,
628                 success,
629                 done,
630                 "telldir (20) failed\n");
631
632         /* Read another 20. */
633         ret = smbc_getdents(dhandle, getdentries, getdentries_size);
634         torture_assert_goto(tctx,
635                 (ret != -1),
636                 success,
637                 done,
638                 talloc_asprintf(tctx,
639                         "smbd_getdents(2) for '%s' failed\n",
640                         dname));
641
642         /* Seek back to 20. */
643         ret = smbc_lseekdir(dhandle, telldir_20);
644         torture_assert_int_equal_goto(tctx,
645                 ret,
646                 0,
647                 success,
648                 done,
649                 talloc_asprintf(tctx,
650                         "failed to seek (20) directory handle for '%s'",
651                         dname));
652
653         /* Read with readdir. */
654         dirent_20 = smbc_readdir(dhandle);
655         torture_assert_not_null_goto(
656                 tctx,
657                 dirent_20,
658                 success,
659                 done,
660                 "smbc_readdir (20) failed\n");
661
662         /* Ensure the getdents and readdir names are the same. */
663         ret = strcmp(dirent_20->name, getdentries[0].name);
664         torture_assert_goto(
665                 tctx,
666                 ret == 0,
667                 success,
668                 done,
669                 talloc_asprintf(tctx,
670                                 "after seek (20) readdir name missmatch "
671                                 "file %s - got %s\n",
672                                 dirent_20->name,
673                                 getdentries[0].name));
674
675         /* Seek back to 20. */
676         ret = smbc_lseekdir(dhandle, telldir_20);
677         torture_assert_int_equal_goto(tctx,
678                 ret,
679                 0,
680                 success,
681                 done,
682                 talloc_asprintf(tctx,
683                         "failed to seek (20) directory handle for '%s'",
684                         dname));
685         /* Read with readdirplus. */
686         direntries_20 = smbc_readdirplus(dhandle);
687         torture_assert_not_null_goto(
688                 tctx,
689                 direntries_20,
690                 success,
691                 done,
692                 "smbc_readdirplus (20) failed\n");
693
694         /* Ensure the readdirplus and readdir names are the same. */
695         ret = strcmp(dirent_20->name, direntries_20->name);
696         torture_assert_goto(
697                 tctx,
698                 ret == 0,
699                 success,
700                 done,
701                 talloc_asprintf(tctx,
702                                 "after seek (20) readdirplus name missmatch "
703                                 "file %s - got %s\n",
704                                 dirent_20->name,
705                                 direntries_20->name));
706
707         /* Seek back to 20. */
708         ret = smbc_lseekdir(dhandle, telldir_20);
709         torture_assert_int_equal_goto(tctx,
710                 ret,
711                 0,
712                 success,
713                 done,
714                 talloc_asprintf(tctx,
715                         "failed to seek (20) directory handle for '%s'",
716                         dname));
717
718         /* Read with readdirplus2. */
719         direntriesplus_20 = smbc_readdirplus2(dhandle, &st2);
720         torture_assert_not_null_goto(
721                 tctx,
722                 direntriesplus_20,
723                 success,
724                 done,
725                 "smbc_readdirplus2 (20) failed\n");
726
727         /* Ensure the readdirplus2 and readdirplus names are the same. */
728         ret = strcmp(direntries_20->name, direntriesplus_20->name);
729         torture_assert_goto(
730                 tctx,
731                 ret == 0,
732                 success,
733                 done,
734                 talloc_asprintf(tctx,
735                                 "after seek (20) readdirplus2 name missmatch "
736                                 "file %s - got %s\n",
737                                 dirent_20->name,
738                                 direntries_20->name));
739
740         /* Ensure doing stat gets the same data. */
741         plus2_stat_path = talloc_asprintf(tctx,
742                                 "%s/%s",
743                                 dname,
744                                 direntriesplus_20->name);
745         torture_assert_not_null_goto(
746                 tctx,
747                 plus2_stat_path,
748                 success,
749                 done,
750                 "talloc fail\n");
751
752         ret = smbc_stat(plus2_stat_path, &st);
753         torture_assert_int_equal_goto(tctx,
754                 ret,
755                 0,
756                 success,
757                 done,
758                 talloc_asprintf(tctx,
759                         "failed to stat file '%s'",
760                         plus2_stat_path));
761
762         torture_assert_int_equal(tctx,
763                 st.st_ino,
764                 st2.st_ino,
765                 talloc_asprintf(tctx,
766                         "file %s mismatched ino value "
767                         "stat got %"PRIx64" readdirplus2 got %"PRIx64"" ,
768                         plus2_stat_path,
769                         (uint64_t)st.st_ino,
770                         (uint64_t)st2.st_ino));
771
772         torture_assert_int_equal(tctx,
773                 st.st_dev,
774                 st2.st_dev,
775                 talloc_asprintf(tctx,
776                         "file %s mismatched dev value "
777                         "stat got %"PRIx64" readdirplus2 got %"PRIx64"" ,
778                         plus2_stat_path,
779                         (uint64_t)st.st_dev,
780                         (uint64_t)st2.st_dev));
781
782         ret = smbc_closedir(dhandle);
783         torture_assert_int_equal(tctx,
784                 ret,
785                 0,
786                 talloc_asprintf(tctx,
787                         "failed to close directory handle for '%s'",
788                         dname));
789
790         dhandle = -1;
791         success = true;
792
793   done:
794
795         /* Clean up. */
796         if (dhandle != -1) {
797                 smbc_closedir(dhandle);
798         }
799         for (i = 0; i < 100; i++) {
800                 if (full_filename[i] != NULL) {
801                         smbc_unlink(full_filename[i]);
802                 }
803         }
804         if (dname != NULL) {
805                 smbc_rmdir(dname);
806         }
807
808         smbc_free_context(ctx, 1);
809
810         return success;
811 }
812
813 #ifndef SMBC_FILE_MODE
814 #define SMBC_FILE_MODE (S_IFREG | 0444)
815 #endif
816
817 static bool torture_libsmbclient_readdirplus2(struct torture_context *tctx)
818 {
819         SMBCCTX *ctx = NULL;
820         int dhandle = -1;
821         int fhandle = -1;
822         bool found = false;
823         bool success = false;
824         const char *filename = NULL;
825         struct stat st2 = {0};
826         struct stat st = {0};
827         int ret;
828         const char *smburl = torture_setting_string(tctx, "smburl", NULL);
829
830         if (smburl == NULL) {
831                 torture_fail(tctx,
832                         "option --option=torture:smburl="
833                         "smb://user:password@server/share missing\n");
834         }
835
836         torture_assert_goto(tctx, torture_libsmbclient_init_context(tctx, &ctx), success, done, "");
837         smbc_set_context(ctx);
838
839         filename = talloc_asprintf(tctx,
840                         "%s/test_readdirplus.txt",
841                         smburl);
842         if (filename == NULL) {
843                 torture_fail_goto(tctx, done, "talloc fail\n");
844         }
845
846         /* Ensure the file doesn't exist. */
847         smbc_unlink(filename);
848
849         /* Create it. */
850         fhandle = smbc_creat(filename, 0666);
851         if (fhandle < 0) {
852                 torture_fail_goto(tctx,
853                         done,
854                         talloc_asprintf(tctx,
855                                 "failed to create file '%s': %s",
856                                 filename,
857                                 strerror(errno)));
858         }
859         ret = smbc_close(fhandle);
860         torture_assert_int_equal_goto(tctx,
861                 ret,
862                 0,
863                 success,
864                 done,
865                 talloc_asprintf(tctx,
866                         "failed to close handle for '%s'",
867                         filename));
868
869         dhandle = smbc_opendir(smburl);
870         if (dhandle < 0) {
871                 int saved_errno = errno;
872                 smbc_unlink(filename);
873                 torture_fail_goto(tctx,
874                         done,
875                         talloc_asprintf(tctx,
876                                 "failed to obtain "
877                                 "directory handle for '%s' : %s",
878                                 smburl,
879                                 strerror(saved_errno)));
880         }
881
882         /* readdirplus2 to ensure we see the new file. */
883         for (;;) {
884                 const struct libsmb_file_info *exstat =
885                         smbc_readdirplus2(dhandle, &st2);
886                 if (exstat == NULL) {
887                         break;
888                 }
889
890                 if (strcmp(exstat->name, "test_readdirplus.txt") == 0) {
891                         found = true;
892                         break;
893                 }
894         }
895
896         if (!found) {
897                 smbc_unlink(filename);
898                 torture_fail_goto(tctx,
899                         done,
900                         talloc_asprintf(tctx,
901                                 "failed to find file '%s'",
902                                 filename));
903         }
904
905         /* Ensure mode is as expected. */
906         /*
907          * New file gets SMBC_FILE_MODE plus
908          * archive bit -> S_IXUSR
909          * !READONLY -> S_IWUSR.
910          */
911         torture_assert_int_equal_goto(tctx,
912                 st2.st_mode,
913                 SMBC_FILE_MODE|S_IXUSR|S_IWUSR,
914                 success,
915                 done,
916                 talloc_asprintf(tctx,
917                         "file %s st_mode should be 0%o, got 0%o'",
918                         filename,
919                         SMBC_FILE_MODE|S_IXUSR|S_IWUSR,
920                         (unsigned int)st2.st_mode));
921
922         /* Ensure smbc_stat() gets the same data. */
923         ret = smbc_stat(filename, &st);
924         torture_assert_int_equal_goto(tctx,
925                 ret,
926                 0,
927                 success,
928                 done,
929                 talloc_asprintf(tctx,
930                         "failed to stat file '%s'",
931                         filename));
932
933         torture_assert_int_equal_goto(tctx,
934                 st2.st_ino,
935                 st.st_ino,
936                 success,
937                 done,
938                 talloc_asprintf(tctx,
939                         "filename '%s' ino missmatch. "
940                         "From smbc_readdirplus2 = %"PRIx64" "
941                         "From smbc_stat = %"PRIx64"",
942                         filename,
943                         (uint64_t)st2.st_ino,
944                         (uint64_t)st.st_ino));
945
946
947         /* Remove it again. */
948         smbc_unlink(filename);
949         ret = smbc_closedir(dhandle);
950         torture_assert_int_equal_goto(tctx,
951                 ret,
952                 0,
953                 success,
954                 done,
955                 talloc_asprintf(tctx,
956                         "failed to close directory handle for '%s'",
957                         filename));
958         success = true;
959
960   done:
961         smbc_free_context(ctx, 1);
962         return success;
963 }
964
965 bool torture_libsmbclient_configuration(struct torture_context *tctx)
966 {
967         SMBCCTX *ctx;
968         bool ok = true;
969
970         ctx = smbc_new_context();
971         torture_assert(tctx, ctx, "failed to get new context");
972         torture_assert(tctx, smbc_init_context(ctx), "failed to init context");
973
974         torture_comment(tctx, "Testing smbc_(set|get)Debug\n");
975         smbc_setDebug(ctx, DEBUGLEVEL);
976         torture_assert_int_equal_goto(tctx,
977                                       smbc_getDebug(ctx),
978                                       DEBUGLEVEL,
979                                       ok,
980                                       done,
981                                       "failed to set DEBUGLEVEL");
982
983         torture_comment(tctx, "Testing smbc_(set|get)NetbiosName\n");
984         smbc_setNetbiosName(ctx, discard_const("torture_netbios"));
985         torture_assert_str_equal_goto(tctx,
986                                       smbc_getNetbiosName(ctx),
987                                       "torture_netbios",
988                                       ok,
989                                       done,
990                                       "failed to set NetbiosName");
991
992         torture_comment(tctx, "Testing smbc_(set|get)Workgroup\n");
993         smbc_setWorkgroup(ctx, discard_const("torture_workgroup"));
994         torture_assert_str_equal_goto(tctx,
995                                       smbc_getWorkgroup(ctx),
996                                       "torture_workgroup",
997                                       ok,
998                                       done,
999                                       "failed to set Workgroup");
1000
1001         torture_comment(tctx, "Testing smbc_(set|get)User\n");
1002         smbc_setUser(ctx, "torture_user");
1003         torture_assert_str_equal_goto(tctx,
1004                                       smbc_getUser(ctx),
1005                                       "torture_user",
1006                                       ok,
1007                                       done,
1008                                       "failed to set User");
1009
1010         torture_comment(tctx, "Testing smbc_(set|get)Timeout\n");
1011         smbc_setTimeout(ctx, 12345);
1012         torture_assert_int_equal_goto(tctx,
1013                                       smbc_getTimeout(ctx),
1014                                       12345,
1015                                       ok,
1016                                       done,
1017                                       "failed to set Timeout");
1018
1019 done:
1020         smbc_free_context(ctx, 1);
1021
1022         return ok;
1023 }
1024
1025 bool torture_libsmbclient_options(struct torture_context *tctx)
1026 {
1027         SMBCCTX *ctx;
1028         bool ok = true;
1029
1030         ctx = smbc_new_context();
1031         torture_assert(tctx, ctx, "failed to get new context");
1032         torture_assert(tctx, smbc_init_context(ctx), "failed to init context");
1033
1034         torture_comment(tctx, "Testing smbc_(set|get)OptionDebugToStderr\n");
1035         smbc_setOptionDebugToStderr(ctx, true);
1036         torture_assert_goto(tctx,
1037                             smbc_getOptionDebugToStderr(ctx),
1038                             ok,
1039                             done,
1040                             "failed to set OptionDebugToStderr");
1041
1042         torture_comment(tctx, "Testing smbc_(set|get)OptionFullTimeNames\n");
1043         smbc_setOptionFullTimeNames(ctx, true);
1044         torture_assert_goto(tctx,
1045                             smbc_getOptionFullTimeNames(ctx),
1046                             ok,
1047                             done,
1048                             "failed to set OptionFullTimeNames");
1049
1050         torture_comment(tctx, "Testing smbc_(set|get)OptionOpenShareMode\n");
1051         smbc_setOptionOpenShareMode(ctx, SMBC_SHAREMODE_DENY_ALL);
1052         torture_assert_int_equal_goto(tctx,
1053                                       smbc_getOptionOpenShareMode(ctx),
1054                                       SMBC_SHAREMODE_DENY_ALL,
1055                                       ok,
1056                                       done,
1057                                       "failed to set OptionOpenShareMode");
1058
1059         torture_comment(tctx, "Testing smbc_(set|get)OptionUserData\n");
1060         smbc_setOptionUserData(ctx, (void *)discard_const("torture_user_data"));
1061         torture_assert_str_equal_goto(tctx,
1062                                       (const char*)smbc_getOptionUserData(ctx),
1063                                       "torture_user_data",
1064                                       ok,
1065                                       done,
1066                                       "failed to set OptionUserData");
1067
1068         torture_comment(tctx,
1069                         "Testing smbc_(set|get)OptionSmbEncryptionLevel\n");
1070         smbc_setOptionSmbEncryptionLevel(ctx, SMBC_ENCRYPTLEVEL_REQUEST);
1071         torture_assert_int_equal_goto(tctx,
1072                                       smbc_getOptionSmbEncryptionLevel(ctx),
1073                                       SMBC_ENCRYPTLEVEL_REQUEST,
1074                                       ok,
1075                                       done,
1076                                       "failed to set OptionSmbEncryptionLevel");
1077
1078         torture_comment(tctx, "Testing smbc_(set|get)OptionCaseSensitive\n");
1079         smbc_setOptionCaseSensitive(ctx, false);
1080         torture_assert_goto(tctx,
1081                             !smbc_getOptionCaseSensitive(ctx),
1082                             ok,
1083                             done,
1084                             "failed to set OptionCaseSensitive");
1085
1086         torture_comment(tctx,
1087                         "Testing smbc_(set|get)OptionBrowseMaxLmbCount\n");
1088         smbc_setOptionBrowseMaxLmbCount(ctx, 2);
1089         torture_assert_int_equal_goto(tctx,
1090                                       smbc_getOptionBrowseMaxLmbCount(ctx),
1091                                       2,
1092                                       ok,
1093                                       done,
1094                                       "failed to set OptionBrowseMaxLmbCount");
1095
1096         torture_comment(tctx,
1097                        "Testing smbc_(set|get)OptionUrlEncodeReaddirEntries\n");
1098         smbc_setOptionUrlEncodeReaddirEntries(ctx, true);
1099         torture_assert_goto(tctx,
1100                             smbc_getOptionUrlEncodeReaddirEntries(ctx),
1101                             ok,
1102                             done,
1103                             "failed to set OptionUrlEncodeReaddirEntries");
1104
1105         torture_comment(tctx,
1106                         "Testing smbc_(set|get)OptionOneSharePerServer\n");
1107         smbc_setOptionOneSharePerServer(ctx, true);
1108         torture_assert_goto(tctx,
1109                             smbc_getOptionOneSharePerServer(ctx),
1110                             ok,
1111                             done,
1112                             "failed to set OptionOneSharePerServer");
1113
1114         torture_comment(tctx, "Testing smbc_(set|get)OptionUseKerberos\n");
1115         smbc_setOptionUseKerberos(ctx, false);
1116         torture_assert_goto(tctx,
1117                             !smbc_getOptionUseKerberos(ctx),
1118                             ok,
1119                             done,
1120                             "failed to set OptionUseKerberos");
1121
1122         torture_comment(tctx,
1123                         "Testing smbc_(set|get)OptionFallbackAfterKerberos\n");
1124         smbc_setOptionFallbackAfterKerberos(ctx, false);
1125         torture_assert_goto(tctx,
1126                             !smbc_getOptionFallbackAfterKerberos(ctx),
1127                             ok,
1128                             done,
1129                             "failed to set OptionFallbackAfterKerberos");
1130
1131         torture_comment(tctx,
1132                         "Testing smbc_(set|get)OptionNoAutoAnonymousLogin\n");
1133         smbc_setOptionNoAutoAnonymousLogin(ctx, true);
1134         torture_assert_goto(tctx,
1135                             smbc_getOptionNoAutoAnonymousLogin(ctx),
1136                             ok,
1137                             done,
1138                             "failed to set OptionNoAutoAnonymousLogin");
1139
1140         torture_comment(tctx, "Testing smbc_(set|get)OptionUseCCache\n");
1141         smbc_setOptionUseCCache(ctx, true);
1142         torture_assert_goto(tctx,
1143                             smbc_getOptionUseCCache(ctx),
1144                             ok,
1145                             done,
1146                             "failed to set OptionUseCCache");
1147
1148 done:
1149         smbc_free_context(ctx, 1);
1150
1151         return ok;
1152 }
1153
1154 static bool torture_libsmbclient_list_shares(struct torture_context *tctx)
1155 {
1156         const char *smburl = torture_setting_string(tctx, "smburl", NULL);
1157         struct smbc_dirent *dirent = NULL;
1158         SMBCCTX *ctx = NULL;
1159         int dhandle = -1;
1160         bool ipc_share_found = false;
1161         bool ok = true;
1162
1163         if (smburl == NULL) {
1164                 torture_fail(tctx,
1165                              "option --option=torture:smburl="
1166                              "smb://user:password@server missing\n");
1167         }
1168
1169         ok = torture_libsmbclient_init_context(tctx, &ctx);
1170         torture_assert_goto(tctx,
1171                             ok,
1172                             ok,
1173                             out,
1174                             "Failed to init context");
1175         smbc_set_context(ctx);
1176
1177         torture_comment(tctx, "Listing: %s\n", smburl);
1178         dhandle = smbc_opendir(smburl);
1179         torture_assert_int_not_equal_goto(tctx,
1180                                           dhandle,
1181                                           -1,
1182                                           ok,
1183                                           out,
1184                                           "Failed to open smburl");
1185
1186         while((dirent = smbc_readdir(dhandle)) != NULL) {
1187                 torture_comment(tctx, "DIR: %s\n", dirent->name);
1188                 torture_assert_not_null_goto(tctx,
1189                                              dirent->name,
1190                                              ok,
1191                                              out,
1192                                              "Failed to read name");
1193
1194                 if (strequal(dirent->name, "IPC$")) {
1195                         ipc_share_found = true;
1196                 }
1197         }
1198
1199         torture_assert_goto(tctx,
1200                             ipc_share_found,
1201                             ok,
1202                             out,
1203                             "Failed to list IPC$ share");
1204
1205 out:
1206         smbc_closedir(dhandle);
1207         return ok;
1208 }
1209
1210 static bool torture_libsmbclient_utimes(struct torture_context *tctx)
1211 {
1212         const char *smburl = torture_setting_string(tctx, "smburl", NULL);
1213         SMBCCTX *ctx = NULL;
1214         struct stat st;
1215         int fhandle, ret;
1216         struct timeval tbuf[2];
1217         bool ok;
1218
1219         if (smburl == NULL) {
1220                 torture_fail(tctx,
1221                              "option --option=torture:smburl="
1222                              "smb://user:password@server missing\n");
1223         }
1224
1225         ok = torture_libsmbclient_init_context(tctx, &ctx);
1226         torture_assert(tctx, ok, "Failed to init context");
1227         smbc_set_context(ctx);
1228
1229         fhandle = smbc_open(smburl, O_RDWR|O_CREAT, 0644);
1230         torture_assert_int_not_equal(tctx, fhandle, -1, "smbc_open failed");
1231
1232         ret = smbc_fstat(fhandle, &st);
1233         torture_assert_int_not_equal(tctx, ret, -1, "smbc_fstat failed");
1234
1235         tbuf[0] = convert_timespec_to_timeval(get_atimespec(&st));
1236         tbuf[1] = convert_timespec_to_timeval(get_mtimespec(&st));
1237
1238         tbuf[1] = timeval_add(&tbuf[1], 0, 100000); /* 100 msec */
1239
1240         ret = smbc_utimes(smburl, tbuf);
1241         torture_assert_int_not_equal(tctx, ret, -1, "smbc_utimes failed");
1242
1243         ret = smbc_fstat(fhandle, &st);
1244         torture_assert_int_not_equal(tctx, ret, -1, "smbc_fstat failed");
1245
1246         torture_assert_int_equal(
1247                 tctx,
1248                 get_mtimensec(&st) / 1000,
1249                 tbuf[1].tv_usec,
1250                 "smbc_utimes did not update msec");
1251
1252         smbc_close(fhandle);
1253         smbc_unlink(smburl);
1254         return true;
1255 }
1256
1257 NTSTATUS torture_libsmbclient_init(TALLOC_CTX *ctx)
1258 {
1259         struct torture_suite *suite;
1260
1261         suite = torture_suite_create(ctx, "libsmbclient");
1262
1263         torture_suite_add_simple_test(suite, "version", torture_libsmbclient_version);
1264         torture_suite_add_simple_test(suite, "initialize", torture_libsmbclient_initialize);
1265         torture_suite_add_simple_test(suite, "configuration", torture_libsmbclient_configuration);
1266         torture_suite_add_simple_test(suite, "setConfiguration", torture_libsmbclient_setConfiguration);
1267         torture_suite_add_simple_test(suite, "options", torture_libsmbclient_options);
1268         torture_suite_add_simple_test(suite, "opendir", torture_libsmbclient_opendir);
1269         torture_suite_add_simple_test(suite, "list_shares", torture_libsmbclient_list_shares);
1270         torture_suite_add_simple_test(suite, "readdirplus",
1271                 torture_libsmbclient_readdirplus);
1272         torture_suite_add_simple_test(suite, "readdirplus_seek",
1273                 torture_libsmbclient_readdirplus_seek);
1274         torture_suite_add_simple_test(suite, "readdirplus2",
1275                 torture_libsmbclient_readdirplus2);
1276         torture_suite_add_simple_test(
1277                 suite, "utimes", torture_libsmbclient_utimes);
1278
1279         suite->description = talloc_strdup(suite, "libsmbclient interface tests");
1280
1281         torture_register_suite(ctx, suite);
1282
1283         return NT_STATUS_OK;
1284 }