s3: piddir creation fix part 2.
[ira/wip.git] / source4 / torture / smb2 / durable_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 durable opens
5
6    Copyright (C) Stefan Metzmacher 2008
7    Copyright (C) Michael Adam 2011-2012
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "../libcli/smb/smbXcli_base.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
29 #include "../libcli/smb/smbXcli_base.h"
30
31 #define CHECK_VAL(v, correct) do { \
32         if ((v) != (correct)) { \
33                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
34                                 __location__, #v, (int)v, (int)correct); \
35                 ret = false; \
36         }} while (0)
37
38 #define CHECK_STATUS(status, correct) do { \
39         if (!NT_STATUS_EQUAL(status, correct)) { \
40                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
41                        nt_errstr(status), nt_errstr(correct)); \
42                 ret = false; \
43                 goto done; \
44         }} while (0)
45
46 #define CHECK_CREATED(__io, __created, __attribute)                     \
47         do {                                                            \
48                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
49                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
50                 CHECK_VAL((__io)->out.size, 0);                         \
51                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
52                 CHECK_VAL((__io)->out.reserved2, 0);                    \
53         } while(0)
54
55
56 /**
57  * basic durable_open test.
58  * durable state should only be granted when requested
59  * along with a batch oplock or a handle lease.
60  *
61  * This test tests durable open with all possible oplock types.
62  */
63
64 struct durable_open_vs_oplock {
65         const char *level;
66         const char *share_mode;
67         bool expected;
68 };
69
70 #define NUM_OPLOCK_TYPES 4
71 #define NUM_SHARE_MODES 8
72 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
73 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
74 {
75         { "", "", false },
76         { "", "R", false },
77         { "", "W", false },
78         { "", "D", false },
79         { "", "RD", false },
80         { "", "RW", false },
81         { "", "WD", false },
82         { "", "RWD", false },
83
84         { "s", "", false },
85         { "s", "R", false },
86         { "s", "W", false },
87         { "s", "D", false },
88         { "s", "RD", false },
89         { "s", "RW", false },
90         { "s", "WD", false },
91         { "s", "RWD", false },
92
93         { "x", "", false },
94         { "x", "R", false },
95         { "x", "W", false },
96         { "x", "D", false },
97         { "x", "RD", false },
98         { "x", "RW", false },
99         { "x", "WD", false },
100         { "x", "RWD", false },
101
102         { "b", "", true },
103         { "b", "R", true },
104         { "b", "W", true },
105         { "b", "D", true },
106         { "b", "RD", true },
107         { "b", "RW", true },
108         { "b", "WD", true },
109         { "b", "RWD", true },
110 };
111
112 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
113                                               struct smb2_tree *tree,
114                                               const char *fname,
115                                               struct durable_open_vs_oplock test)
116 {
117         NTSTATUS status;
118         TALLOC_CTX *mem_ctx = talloc_new(tctx);
119         struct smb2_handle _h;
120         struct smb2_handle *h = NULL;
121         bool ret = true;
122         struct smb2_create io;
123
124         smb2_util_unlink(tree, fname);
125
126         smb2_oplock_create_share(&io, fname,
127                                  smb2_util_share_access(test.share_mode),
128                                  smb2_util_oplock_level(test.level));
129         io.in.durable_open = true;
130
131         status = smb2_create(tree, mem_ctx, &io);
132         CHECK_STATUS(status, NT_STATUS_OK);
133         _h = io.out.file.handle;
134         h = &_h;
135         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
136         CHECK_VAL(io.out.durable_open, test.expected);
137         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
138
139 done:
140         if (h != NULL) {
141                 smb2_util_close(tree, *h);
142         }
143         smb2_util_unlink(tree, fname);
144         talloc_free(mem_ctx);
145
146         return ret;
147 }
148
149 bool test_durable_open_open_oplock(struct torture_context *tctx,
150                                    struct smb2_tree *tree)
151 {
152         TALLOC_CTX *mem_ctx = talloc_new(tctx);
153         char fname[256];
154         bool ret = true;
155         int i;
156
157         /* Choose a random name in case the state is left a little funky. */
158         snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
159
160         smb2_util_unlink(tree, fname);
161
162         /* test various oplock levels with durable open */
163
164         for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
165                 ret = test_one_durable_open_open_oplock(tctx,
166                                                         tree,
167                                                         fname,
168                                                         durable_open_vs_oplock_table[i]);
169                 if (ret == false) {
170                         goto done;
171                 }
172         }
173
174 done:
175         smb2_util_unlink(tree, fname);
176         talloc_free(tree);
177         talloc_free(mem_ctx);
178
179         return ret;
180 }
181
182 /**
183  * basic durable_open test.
184  * durable state should only be granted when requested
185  * along with a batch oplock or a handle lease.
186  *
187  * This test tests durable open with all valid lease types.
188  */
189
190 struct durable_open_vs_lease {
191         const char *type;
192         const char *share_mode;
193         bool expected;
194 };
195
196 #define NUM_LEASE_TYPES 5
197 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
198 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
199 {
200         { "", "", false },
201         { "", "R", false },
202         { "", "W", false },
203         { "", "D", false },
204         { "", "RW", false },
205         { "", "RD", false },
206         { "", "WD", false },
207         { "", "RWD", false },
208
209         { "R", "", false },
210         { "R", "R", false },
211         { "R", "W", false },
212         { "R", "D", false },
213         { "R", "RW", false },
214         { "R", "RD", false },
215         { "R", "DW", false },
216         { "R", "RWD", false },
217
218         { "RW", "", false },
219         { "RW", "R", false },
220         { "RW", "W", false },
221         { "RW", "D", false },
222         { "RW", "RW", false },
223         { "RW", "RD", false },
224         { "RW", "WD", false },
225         { "RW", "RWD", false },
226
227         { "RH", "", true },
228         { "RH", "R", true },
229         { "RH", "W", true },
230         { "RH", "D", true },
231         { "RH", "RW", true },
232         { "RH", "RD", true },
233         { "RH", "WD", true },
234         { "RH", "RWD", true },
235
236         { "RHW", "", true },
237         { "RHW", "R", true },
238         { "RHW", "W", true },
239         { "RHW", "D", true },
240         { "RHW", "RW", true },
241         { "RHW", "RD", true },
242         { "RHW", "WD", true },
243         { "RHW", "RWD", true },
244 };
245
246 static bool test_one_durable_open_open_lease(struct torture_context *tctx,
247                                              struct smb2_tree *tree,
248                                              const char *fname,
249                                              struct durable_open_vs_lease test)
250 {
251         NTSTATUS status;
252         TALLOC_CTX *mem_ctx = talloc_new(tctx);
253         struct smb2_handle _h;
254         struct smb2_handle *h = NULL;
255         bool ret = true;
256         struct smb2_create io;
257         struct smb2_lease ls;
258         uint64_t lease;
259         uint32_t caps;
260
261         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
262         if (!(caps & SMB2_CAP_LEASING)) {
263                 torture_skip(tctx, "leases are not supported");
264         }
265
266         smb2_util_unlink(tree, fname);
267
268         lease = random();
269
270         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
271                                 smb2_util_share_access(test.share_mode),
272                                 lease,
273                                 smb2_util_lease_state(test.type));
274         io.in.durable_open = true;
275
276         status = smb2_create(tree, mem_ctx, &io);
277         CHECK_STATUS(status, NT_STATUS_OK);
278         _h = io.out.file.handle;
279         h = &_h;
280         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
281         CHECK_VAL(io.out.durable_open, test.expected);
282         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
283         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
284         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
285         CHECK_VAL(io.out.lease_response.lease_state,
286                   smb2_util_lease_state(test.type));
287 done:
288         if (h != NULL) {
289                 smb2_util_close(tree, *h);
290         }
291         smb2_util_unlink(tree, fname);
292         talloc_free(mem_ctx);
293
294         return ret;
295 }
296
297 bool test_durable_open_open_lease(struct torture_context *tctx,
298                                   struct smb2_tree *tree)
299 {
300         TALLOC_CTX *mem_ctx = talloc_new(tctx);
301         char fname[256];
302         bool ret = true;
303         int i;
304         uint32_t caps;
305
306         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
307         if (!(caps & SMB2_CAP_LEASING)) {
308                 torture_skip(tctx, "leases are not supported");
309         }
310
311         /* Choose a random name in case the state is left a little funky. */
312         snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
313
314         smb2_util_unlink(tree, fname);
315
316
317         /* test various oplock levels with durable open */
318
319         for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
320                 ret = test_one_durable_open_open_lease(tctx,
321                                                        tree,
322                                                        fname,
323                                                        durable_open_vs_lease_table[i]);
324                 if (ret == false) {
325                         goto done;
326                 }
327         }
328
329 done:
330         smb2_util_unlink(tree, fname);
331         talloc_free(tree);
332         talloc_free(mem_ctx);
333
334         return ret;
335 }
336
337 /**
338  * basic test for doing a durable open
339  * and do a durable reopen on the same connection
340  * while the first open is still active (fails)
341  */
342 bool test_durable_open_reopen1(struct torture_context *tctx,
343                                struct smb2_tree *tree)
344 {
345         NTSTATUS status;
346         TALLOC_CTX *mem_ctx = talloc_new(tctx);
347         char fname[256];
348         struct smb2_handle _h;
349         struct smb2_handle *h = NULL;
350         struct smb2_create io1, io2;
351         bool ret = true;
352
353         /* Choose a random name in case the state is left a little funky. */
354         snprintf(fname, 256, "durable_open_reopen1_%s.dat",
355                  generate_random_str(tctx, 8));
356
357         smb2_util_unlink(tree, fname);
358
359         smb2_oplock_create_share(&io1, fname,
360                                  smb2_util_share_access(""),
361                                  smb2_util_oplock_level("b"));
362         io1.in.durable_open = true;
363
364         status = smb2_create(tree, mem_ctx, &io1);
365         CHECK_STATUS(status, NT_STATUS_OK);
366         _h = io1.out.file.handle;
367         h = &_h;
368         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
369         CHECK_VAL(io1.out.durable_open, true);
370         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
371
372         /* try a durable reconnect while the file is still open */
373         ZERO_STRUCT(io2);
374         io2.in.fname = fname;
375         io2.in.durable_handle = h;
376
377         status = smb2_create(tree, mem_ctx, &io2);
378         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
379
380 done:
381         if (h != NULL) {
382                 smb2_util_close(tree, *h);
383         }
384
385         smb2_util_unlink(tree, fname);
386
387         talloc_free(tree);
388
389         talloc_free(mem_ctx);
390
391         return ret;
392 }
393
394 /**
395  * basic test for doing a durable open
396  * tcp disconnect, reconnect, do a durable reopen (succeeds)
397  */
398 bool test_durable_open_reopen2(struct torture_context *tctx,
399                                struct smb2_tree *tree)
400 {
401         NTSTATUS status;
402         TALLOC_CTX *mem_ctx = talloc_new(tctx);
403         char fname[256];
404         struct smb2_handle _h;
405         struct smb2_handle *h = NULL;
406         struct smb2_create io1, io2;
407         bool ret = true;
408
409         /* Choose a random name in case the state is left a little funky. */
410         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
411                  generate_random_str(tctx, 8));
412
413         smb2_util_unlink(tree, fname);
414
415         smb2_oplock_create_share(&io1, fname,
416                                  smb2_util_share_access(""),
417                                  smb2_util_oplock_level("b"));
418         io1.in.durable_open = true;
419
420         status = smb2_create(tree, mem_ctx, &io1);
421         CHECK_STATUS(status, NT_STATUS_OK);
422         _h = io1.out.file.handle;
423         h = &_h;
424         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
425         CHECK_VAL(io1.out.durable_open, true);
426         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
427
428         /* disconnect, reconnect and then do durable reopen */
429         talloc_free(tree);
430         tree = NULL;
431
432         if (!torture_smb2_connection(tctx, &tree)) {
433                 torture_warning(tctx, "couldn't reconnect, bailing\n");
434                 ret = false;
435                 goto done;
436         }
437
438         ZERO_STRUCT(io2);
439         io2.in.fname = fname;
440         io2.in.durable_handle = h;
441         h = NULL;
442
443         status = smb2_create(tree, mem_ctx, &io2);
444         CHECK_STATUS(status, NT_STATUS_OK);
445         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
446         CHECK_VAL(io2.out.durable_open, true);
447         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
448         _h = io2.out.file.handle;
449         h = &_h;
450
451 done:
452         if (h != NULL) {
453                 smb2_util_close(tree, *h);
454         }
455
456         smb2_util_unlink(tree, fname);
457
458         talloc_free(tree);
459
460         talloc_free(mem_ctx);
461
462         return ret;
463 }
464
465 /**
466  * basic test for doing a durable open
467  * tcp disconnect, reconnect with a session reconnect and
468  * do a durable reopen (succeeds)
469  */
470 bool test_durable_open_reopen2a(struct torture_context *tctx,
471                                 struct smb2_tree *tree)
472 {
473         NTSTATUS status;
474         TALLOC_CTX *mem_ctx = talloc_new(tctx);
475         char fname[256];
476         struct smb2_handle _h;
477         struct smb2_handle *h = NULL;
478         struct smb2_create io1, io2;
479         uint64_t previous_session_id;
480         bool ret = true;
481
482         /* Choose a random name in case the state is left a little funky. */
483         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
484                  generate_random_str(tctx, 8));
485
486         smb2_util_unlink(tree, fname);
487
488         smb2_oplock_create_share(&io1, fname,
489                                  smb2_util_share_access(""),
490                                  smb2_util_oplock_level("b"));
491         io1.in.durable_open = true;
492
493         status = smb2_create(tree, mem_ctx, &io1);
494         CHECK_STATUS(status, NT_STATUS_OK);
495         _h = io1.out.file.handle;
496         h = &_h;
497         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
498         CHECK_VAL(io1.out.durable_open, true);
499         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
500
501         /* disconnect, reconnect and then do durable reopen */
502         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
503         talloc_free(tree);
504         tree = NULL;
505
506         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
507                 torture_warning(tctx, "couldn't reconnect, bailing\n");
508                 ret = false;
509                 goto done;
510         }
511
512         ZERO_STRUCT(io2);
513         io2.in.fname = fname;
514         io2.in.durable_handle = h;
515         h = NULL;
516
517         status = smb2_create(tree, mem_ctx, &io2);
518         CHECK_STATUS(status, NT_STATUS_OK);
519         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
520         CHECK_VAL(io2.out.durable_open, true);
521         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
522         _h = io2.out.file.handle;
523         h = &_h;
524
525 done:
526         if (h != NULL) {
527                 smb2_util_close(tree, *h);
528         }
529
530         smb2_util_unlink(tree, fname);
531
532         talloc_free(tree);
533
534         talloc_free(mem_ctx);
535
536         return ret;
537 }
538
539
540 /**
541  * basic test for doing a durable open:
542  * tdis, new tcon, try durable reopen (fails)
543  */
544 bool test_durable_open_reopen3(struct torture_context *tctx,
545                                struct smb2_tree *tree)
546 {
547         NTSTATUS status;
548         TALLOC_CTX *mem_ctx = talloc_new(tctx);
549         char fname[256];
550         struct smb2_handle _h;
551         struct smb2_handle *h = NULL;
552         struct smb2_create io1, io2;
553         bool ret = true;
554         struct smb2_tree *tree2;
555
556         /* Choose a random name in case the state is left a little funky. */
557         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
558                  generate_random_str(tctx, 8));
559
560         smb2_util_unlink(tree, fname);
561
562         smb2_oplock_create_share(&io1, fname,
563                                  smb2_util_share_access(""),
564                                  smb2_util_oplock_level("b"));
565         io1.in.durable_open = true;
566
567         status = smb2_create(tree, mem_ctx, &io1);
568         CHECK_STATUS(status, NT_STATUS_OK);
569         _h = io1.out.file.handle;
570         h = &_h;
571         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
572         CHECK_VAL(io1.out.durable_open, true);
573         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
574
575         /* disconnect, reconnect and then do durable reopen */
576         status = smb2_tdis(tree);
577         CHECK_STATUS(status, NT_STATUS_OK);
578
579         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
580                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
581                 ret = false;
582                 goto done;
583         }
584
585
586         ZERO_STRUCT(io2);
587         io2.in.fname = fname;
588         io2.in.durable_handle = h;
589
590         status = smb2_create(tree2, mem_ctx, &io2);
591         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
592
593 done:
594         if (h != NULL) {
595                 smb2_util_close(tree, *h);
596         }
597
598         smb2_util_unlink(tree2, fname);
599
600         talloc_free(tree);
601
602         talloc_free(mem_ctx);
603
604         return ret;
605 }
606
607 /**
608  * basic test for doing a durable open:
609  * logoff, create a new session, do a durable reopen (succeeds)
610  */
611 bool test_durable_open_reopen4(struct torture_context *tctx,
612                                struct smb2_tree *tree)
613 {
614         NTSTATUS status;
615         TALLOC_CTX *mem_ctx = talloc_new(tctx);
616         char fname[256];
617         struct smb2_handle _h;
618         struct smb2_handle *h = NULL;
619         struct smb2_create io1, io2;
620         bool ret = true;
621         struct smb2_transport *transport;
622         struct smb2_session *session2;
623         struct smb2_tree *tree2;
624
625         /* Choose a random name in case the state is left a little funky. */
626         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
627                  generate_random_str(tctx, 8));
628
629         smb2_util_unlink(tree, fname);
630
631         smb2_oplock_create_share(&io1, fname,
632                                  smb2_util_share_access(""),
633                                  smb2_util_oplock_level("b"));
634         io1.in.durable_open = true;
635         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
636
637         status = smb2_create(tree, mem_ctx, &io1);
638         CHECK_STATUS(status, NT_STATUS_OK);
639         _h = io1.out.file.handle;
640         h = &_h;
641         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
642         CHECK_VAL(io1.out.durable_open, true);
643         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
644
645         /*
646          * do a session logoff, establish a new session and tree
647          * connect on the same transport, and try a durable reopen
648          */
649         transport = tree->session->transport;
650         status = smb2_logoff(tree->session);
651         CHECK_STATUS(status, NT_STATUS_OK);
652
653         if (!torture_smb2_session_setup(tctx, transport,
654                                         0, /* previous_session_id */
655                                         mem_ctx, &session2))
656         {
657                 torture_warning(tctx, "session setup failed.\n");
658                 ret = false;
659                 goto done;
660         }
661
662         /*
663          * the session setup has talloc-stolen the transport,
664          * so we can safely free the old tree+session for clarity
665          */
666         TALLOC_FREE(tree);
667
668         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
669                 torture_warning(tctx, "tree connect failed.\n");
670                 ret = false;
671                 goto done;
672         }
673
674         ZERO_STRUCT(io2);
675         io2.in.fname = fname;
676         io2.in.durable_handle = h;
677         h = NULL;
678
679         status = smb2_create(tree2, mem_ctx, &io2);
680         CHECK_STATUS(status, NT_STATUS_OK);
681
682         _h = io2.out.file.handle;
683         h = &_h;
684         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
685         CHECK_VAL(io2.out.durable_open, true);
686         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
687
688 done:
689         if (h != NULL) {
690                 smb2_util_close(tree2, *h);
691         }
692
693         smb2_util_unlink(tree2, fname);
694
695         talloc_free(tree);
696
697         talloc_free(mem_ctx);
698
699         return ret;
700 }
701
702 /*
703    basic testing of SMB2 durable opens
704    regarding the position information on the handle
705 */
706 bool test_durable_open_file_position(struct torture_context *tctx,
707                                      struct smb2_tree *tree1,
708                                      struct smb2_tree *tree2)
709 {
710         TALLOC_CTX *mem_ctx = talloc_new(tctx);
711         struct smb2_handle h1, h2;
712         struct smb2_create io1, io2;
713         NTSTATUS status;
714         const char *fname = "durable_open_position.dat";
715         union smb_fileinfo qfinfo;
716         union smb_setfileinfo sfinfo;
717         bool ret = true;
718         uint64_t pos;
719
720         smb2_util_unlink(tree1, fname);
721
722         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
723         io1.in.durable_open = true;
724
725         status = smb2_create(tree1, mem_ctx, &io1);
726         CHECK_STATUS(status, NT_STATUS_OK);
727         h1 = io1.out.file.handle;
728         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
729         CHECK_VAL(io1.out.durable_open, true);
730         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
731
732         /* TODO: check extra blob content */
733
734         ZERO_STRUCT(qfinfo);
735         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
736         qfinfo.generic.in.file.handle = h1;
737         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
738         CHECK_STATUS(status, NT_STATUS_OK);
739         CHECK_VAL(qfinfo.position_information.out.position, 0);
740         pos = qfinfo.position_information.out.position;
741         torture_comment(tctx, "position: %llu\n",
742                         (unsigned long long)pos);
743
744         ZERO_STRUCT(sfinfo);
745         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
746         sfinfo.generic.in.file.handle = h1;
747         sfinfo.position_information.in.position = 0x1000;
748         status = smb2_setinfo_file(tree1, &sfinfo);
749         CHECK_STATUS(status, NT_STATUS_OK);
750
751         ZERO_STRUCT(qfinfo);
752         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
753         qfinfo.generic.in.file.handle = h1;
754         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
755         CHECK_STATUS(status, NT_STATUS_OK);
756         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
757         pos = qfinfo.position_information.out.position;
758         torture_comment(tctx, "position: %llu\n",
759                         (unsigned long long)pos);
760
761         talloc_free(tree1);
762         tree1 = NULL;
763
764         ZERO_STRUCT(qfinfo);
765         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
766         qfinfo.generic.in.file.handle = h1;
767         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
768         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
769
770         ZERO_STRUCT(io2);
771         io2.in.fname = fname;
772         io2.in.durable_handle = &h1;
773
774         status = smb2_create(tree2, mem_ctx, &io2);
775         CHECK_STATUS(status, NT_STATUS_OK);
776         CHECK_VAL(io2.out.durable_open, true);
777         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
778         CHECK_VAL(io2.out.reserved, 0x00);
779         CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
780         CHECK_VAL(io2.out.alloc_size, 0);
781         CHECK_VAL(io2.out.size, 0);
782         CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
783         CHECK_VAL(io2.out.reserved2, 0);
784
785         h2 = io2.out.file.handle;
786
787         ZERO_STRUCT(qfinfo);
788         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
789         qfinfo.generic.in.file.handle = h2;
790         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
791         CHECK_STATUS(status, NT_STATUS_OK);
792         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
793         pos = qfinfo.position_information.out.position;
794         torture_comment(tctx, "position: %llu\n",
795                         (unsigned long long)pos);
796
797         smb2_util_close(tree2, h2);
798
799         talloc_free(mem_ctx);
800
801         smb2_util_unlink(tree2, fname);
802 done:
803         talloc_free(tree1);
804         talloc_free(tree2);
805
806         return ret;
807 }
808
809 /*
810   Open, disconnect, oplock break, reconnect.
811 */
812 bool test_durable_open_oplock(struct torture_context *tctx,
813                               struct smb2_tree *tree1,
814                               struct smb2_tree *tree2)
815 {
816         TALLOC_CTX *mem_ctx = talloc_new(tctx);
817         struct smb2_create io1, io2;
818         struct smb2_handle h1, h2;
819         NTSTATUS status;
820         char fname[256];
821         bool ret = true;
822
823         /* Choose a random name in case the state is left a little funky. */
824         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
825
826         /* Clean slate */
827         smb2_util_unlink(tree1, fname);
828
829         /* Create with batch oplock */
830         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
831         io1.in.durable_open = true;
832
833         io2 = io1;
834         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
835
836         status = smb2_create(tree1, mem_ctx, &io1);
837         CHECK_STATUS(status, NT_STATUS_OK);
838         h1 = io1.out.file.handle;
839         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
840         CHECK_VAL(io1.out.durable_open, true);
841         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
842
843         /* Disconnect after getting the batch */
844         talloc_free(tree1);
845         tree1 = NULL;
846
847         /*
848          * Windows7 (build 7000) will break a batch oplock immediately if the
849          * original client is gone. (ZML: This seems like a bug. It should give
850          * some time for the client to reconnect!)
851          */
852         status = smb2_create(tree2, mem_ctx, &io2);
853         CHECK_STATUS(status, NT_STATUS_OK);
854         h2 = io2.out.file.handle;
855         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
856         CHECK_VAL(io2.out.durable_open, true);
857         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
858
859         /* What if tree1 tries to come back and reclaim? */
860         if (!torture_smb2_connection(tctx, &tree1)) {
861                 torture_warning(tctx, "couldn't reconnect, bailing\n");
862                 ret = false;
863                 goto done;
864         }
865
866         ZERO_STRUCT(io1);
867         io1.in.fname = fname;
868         io1.in.durable_handle = &h1;
869
870         status = smb2_create(tree1, mem_ctx, &io1);
871         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
872
873  done:
874         smb2_util_close(tree2, h2);
875         smb2_util_unlink(tree2, fname);
876
877         talloc_free(tree1);
878         talloc_free(tree2);
879
880         return ret;
881 }
882
883 /*
884   Open, disconnect, lease break, reconnect.
885 */
886 bool test_durable_open_lease(struct torture_context *tctx,
887                              struct smb2_tree *tree1,
888                              struct smb2_tree *tree2)
889 {
890         TALLOC_CTX *mem_ctx = talloc_new(tctx);
891         struct smb2_create io1, io2;
892         struct smb2_lease ls1, ls2;
893         struct smb2_handle h1, h2;
894         NTSTATUS status;
895         char fname[256];
896         bool ret = true;
897         uint64_t lease1, lease2;
898         uint32_t caps;
899
900         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
901         if (!(caps & SMB2_CAP_LEASING)) {
902                 torture_skip(tctx, "leases are not supported");
903         }
904
905         /*
906          * Choose a random name and random lease in case the state is left a
907          * little funky.
908          */
909         lease1 = random();
910         lease2 = random();
911         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
912
913         /* Clean slate */
914         smb2_util_unlink(tree1, fname);
915
916         /* Create with lease */
917         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
918                           lease1, smb2_util_lease_state("RHW"));
919         io1.in.durable_open = true;
920
921         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
922                           lease2, smb2_util_lease_state("RHW"));
923         io2.in.durable_open = true;
924         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
925
926         status = smb2_create(tree1, mem_ctx, &io1);
927         CHECK_STATUS(status, NT_STATUS_OK);
928         h1 = io1.out.file.handle;
929         CHECK_VAL(io1.out.durable_open, true);
930         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
931
932         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
933         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
934         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
935         CHECK_VAL(io1.out.lease_response.lease_state,
936             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
937
938         /* Disconnect after getting the lease */
939         talloc_free(tree1);
940         tree1 = NULL;
941
942         /*
943          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
944          * even if the original client is gone. (ZML: This seems like a bug. It
945          * should give some time for the client to reconnect! And why RH?)
946          * 
947          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
948          * Test is adapted accordingly.
949          */
950         status = smb2_create(tree2, mem_ctx, &io2);
951         CHECK_STATUS(status, NT_STATUS_OK);
952         h2 = io2.out.file.handle;
953         CHECK_VAL(io2.out.durable_open, true);
954         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
955
956         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
957         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
958         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
959         CHECK_VAL(io2.out.lease_response.lease_state,
960             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
961
962         /* What if tree1 tries to come back and reclaim? */
963         if (!torture_smb2_connection(tctx, &tree1)) {
964                 torture_warning(tctx, "couldn't reconnect, bailing\n");
965                 ret = false;
966                 goto done;
967         }
968
969         ZERO_STRUCT(io1);
970         io1.in.fname = fname;
971         io1.in.durable_handle = &h1;
972         io1.in.lease_request = &ls1;
973
974         status = smb2_create(tree1, mem_ctx, &io1);
975         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
976
977  done:
978         smb2_util_close(tree2, h2);
979         smb2_util_unlink(tree2, fname);
980
981         talloc_free(tree1);
982         talloc_free(tree2);
983
984         return ret;
985 }
986
987 /*
988   Open, take BRL, disconnect, reconnect.
989 */
990 bool test_durable_open_lock(struct torture_context *tctx,
991                             struct smb2_tree *tree)
992 {
993         TALLOC_CTX *mem_ctx = talloc_new(tctx);
994         struct smb2_create io;
995         struct smb2_lease ls;
996         struct smb2_handle h;
997         struct smb2_lock lck;
998         struct smb2_lock_element el[2];
999         NTSTATUS status;
1000         char fname[256];
1001         bool ret = true;
1002         uint64_t lease;
1003         uint32_t caps;
1004
1005         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1006         if (!(caps & SMB2_CAP_LEASING)) {
1007                 torture_skip(tctx, "leases are not supported");
1008         }
1009
1010         /*
1011          * Choose a random name and random lease in case the state is left a
1012          * little funky.
1013          */
1014         lease = random();
1015         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
1016
1017         /* Clean slate */
1018         smb2_util_unlink(tree, fname);
1019
1020         /* Create with lease */
1021
1022         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1023                           smb2_util_lease_state("RWH"));
1024         io.in.durable_open              = true;
1025
1026         status = smb2_create(tree, mem_ctx, &io);
1027         CHECK_STATUS(status, NT_STATUS_OK);
1028         h = io.out.file.handle;
1029         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1030
1031         CHECK_VAL(io.out.durable_open, true);
1032         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1033         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1034         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1035         CHECK_VAL(io.out.lease_response.lease_state,
1036             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1037
1038         ZERO_STRUCT(lck);
1039         ZERO_STRUCT(el);
1040         lck.in.locks            = el;
1041         lck.in.lock_count       = 0x0001;
1042         lck.in.lock_sequence    = 0x00000000;
1043         lck.in.file.handle      = h;
1044         el[0].offset            = 0;
1045         el[0].length            = 1;
1046         el[0].reserved          = 0x00000000;
1047         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1048         status = smb2_lock(tree, &lck);
1049         CHECK_STATUS(status, NT_STATUS_OK);
1050
1051         /* Disconnect/Reconnect. */
1052         talloc_free(tree);
1053         tree = NULL;
1054
1055         if (!torture_smb2_connection(tctx, &tree)) {
1056                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1057                 ret = false;
1058                 goto done;
1059         }
1060
1061         ZERO_STRUCT(io);
1062         io.in.fname = fname;
1063         io.in.durable_handle = &h;
1064         io.in.lease_request = &ls;
1065
1066         status = smb2_create(tree, mem_ctx, &io);
1067         CHECK_STATUS(status, NT_STATUS_OK);
1068         h = io.out.file.handle;
1069
1070         lck.in.file.handle      = h;
1071         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1072         status = smb2_lock(tree, &lck);
1073         CHECK_STATUS(status, NT_STATUS_OK);
1074
1075  done:
1076         smb2_util_close(tree, h);
1077         smb2_util_unlink(tree, fname);
1078         talloc_free(tree);
1079
1080         return ret;
1081 }
1082
1083 /**
1084  * Open with a RH lease, disconnect, open in another tree, reconnect.
1085  *
1086  * This test actually demonstrates a minimum level of respect for the durable
1087  * open in the face of another open. As long as this test shows an inability to
1088  * reconnect after an open, the oplock/lease tests above will certainly
1089  * demonstrate an error on reconnect.
1090  */
1091 bool test_durable_open_open2_lease(struct torture_context *tctx,
1092                                   struct smb2_tree *tree1,
1093                                   struct smb2_tree *tree2)
1094 {
1095         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1096         struct smb2_create io1, io2;
1097         struct smb2_lease ls;
1098         struct smb2_handle h1, h2;
1099         NTSTATUS status;
1100         char fname[256];
1101         bool ret = true;
1102         uint64_t lease;
1103         uint32_t caps;
1104
1105         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1106         if (!(caps & SMB2_CAP_LEASING)) {
1107                 torture_skip(tctx, "leases are not supported");
1108         }
1109
1110         /*
1111          * Choose a random name and random lease in case the state is left a
1112          * little funky.
1113          */
1114         lease = random();
1115         snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
1116                  generate_random_str(tctx, 8));
1117
1118         /* Clean slate */
1119         smb2_util_unlink(tree1, fname);
1120
1121         /* Create with lease */
1122         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1123                                 smb2_util_share_access(""),
1124                                 lease,
1125                                 smb2_util_lease_state("RH"));
1126         io1.in.durable_open = true;
1127
1128         status = smb2_create(tree1, mem_ctx, &io1);
1129         CHECK_STATUS(status, NT_STATUS_OK);
1130         h1 = io1.out.file.handle;
1131         CHECK_VAL(io1.out.durable_open, true);
1132         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1133
1134         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1135         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1136         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1137         CHECK_VAL(io1.out.lease_response.lease_state,
1138                   smb2_util_lease_state("RH"));
1139
1140         /* Disconnect */
1141         talloc_free(tree1);
1142         tree1 = NULL;
1143
1144         /* Open the file in tree2 */
1145         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1146
1147         status = smb2_create(tree2, mem_ctx, &io2);
1148         CHECK_STATUS(status, NT_STATUS_OK);
1149         h2 = io2.out.file.handle;
1150         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1151
1152         /* Reconnect */
1153         if (!torture_smb2_connection(tctx, &tree1)) {
1154                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1155                 ret = false;
1156                 goto done;
1157         }
1158
1159         ZERO_STRUCT(io1);
1160         io1.in.fname = fname;
1161         io1.in.durable_handle = &h1;
1162         io1.in.lease_request = &ls;
1163
1164         /*
1165          * Windows7 (build 7000) will give away an open immediately if the
1166          * original client is gone. (ZML: This seems like a bug. It should give
1167          * some time for the client to reconnect!)
1168          */
1169         status = smb2_create(tree1, mem_ctx, &io1);
1170         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1171         h1 = io1.out.file.handle;
1172
1173  done:
1174         smb2_util_close(tree2, h2);
1175         smb2_util_unlink(tree2, fname);
1176         smb2_util_close(tree1, h1);
1177         smb2_util_unlink(tree1, fname);
1178
1179         talloc_free(tree1);
1180         talloc_free(tree2);
1181
1182         return ret;
1183 }
1184
1185 /**
1186  * Open with a batch oplock, disconnect, open in another tree, reconnect.
1187  *
1188  * This test actually demonstrates a minimum level of respect for the durable
1189  * open in the face of another open. As long as this test shows an inability to
1190  * reconnect after an open, the oplock/lease tests above will certainly
1191  * demonstrate an error on reconnect.
1192  */
1193 bool test_durable_open_open2_oplock(struct torture_context *tctx,
1194                                     struct smb2_tree *tree1,
1195                                     struct smb2_tree *tree2)
1196 {
1197         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1198         struct smb2_create io1, io2;
1199         struct smb2_handle h1, h2;
1200         NTSTATUS status;
1201         char fname[256];
1202         bool ret = true;
1203
1204         /*
1205          * Choose a random name and random lease in case the state is left a
1206          * little funky.
1207          */
1208         snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
1209                  generate_random_str(tctx, 8));
1210
1211         /* Clean slate */
1212         smb2_util_unlink(tree1, fname);
1213
1214         /* Create with batch oplock */
1215         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1216         io1.in.durable_open = true;
1217
1218         status = smb2_create(tree1, mem_ctx, &io1);
1219         CHECK_STATUS(status, NT_STATUS_OK);
1220         h1 = io1.out.file.handle;
1221         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1222         CHECK_VAL(io1.out.durable_open, true);
1223         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1224
1225         /* Disconnect */
1226         talloc_free(tree1);
1227         tree1 = NULL;
1228
1229         /* Open the file in tree2 */
1230         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1231
1232         status = smb2_create(tree2, mem_ctx, &io2);
1233         CHECK_STATUS(status, NT_STATUS_OK);
1234         h2 = io2.out.file.handle;
1235         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1236
1237         /* Reconnect */
1238         if (!torture_smb2_connection(tctx, &tree1)) {
1239                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1240                 ret = false;
1241                 goto done;
1242         }
1243
1244         ZERO_STRUCT(io1);
1245         io1.in.fname = fname;
1246         io1.in.durable_handle = &h1;
1247
1248         status = smb2_create(tree1, mem_ctx, &io1);
1249         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1250         h1 = io1.out.file.handle;
1251
1252  done:
1253         smb2_util_close(tree2, h2);
1254         smb2_util_unlink(tree2, fname);
1255         smb2_util_close(tree1, h1);
1256         smb2_util_unlink(tree1, fname);
1257
1258         talloc_free(tree1);
1259         talloc_free(tree2);
1260
1261         return ret;
1262 }
1263
1264 struct torture_suite *torture_smb2_durable_open_init(void)
1265 {
1266         struct torture_suite *suite =
1267             torture_suite_create(talloc_autofree_context(), "durable-open");
1268
1269         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
1270         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
1271         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1272         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1273         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1274         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1275         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1276         torture_suite_add_2smb2_test(suite, "file-position",
1277             test_durable_open_file_position);
1278         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1279         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1280         torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
1281         torture_suite_add_2smb2_test(suite, "open2-lease",
1282                                      test_durable_open_open2_lease);
1283         torture_suite_add_2smb2_test(suite, "open2-oplock",
1284                                      test_durable_open_open2_oplock);
1285
1286         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1287
1288         return suite;
1289 }