torture: Run the cleanup2 test against 2 nodes
[vlendec/samba-autobuild/.git] / source3 / torture / test_cleanup.c
1 /*
2    Unix SMB/CIFS implementation.
3    Test cleanup behaviour
4    Copyright (C) Volker Lendecke 2011
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "locking/proto.h"
22 #include "torture/proto.h"
23 #include "system/filesys.h"
24 #include "system/select.h"
25 #include "libsmb/libsmb.h"
26 #include "libcli/smb/smbXcli_base.h"
27 #include "libcli/security/security.h"
28 #include "librpc/gen_ndr/open_files.h"
29
30 bool run_cleanup1(int dummy)
31 {
32         struct cli_state *cli;
33         const char *fname = "\\cleanup1";
34         uint16_t fnum;
35         NTSTATUS status;
36
37         printf("CLEANUP1: Checking that a conflicting share mode is cleaned "
38                "up\n");
39
40         if (!torture_open_connection(&cli, 0)) {
41                 return false;
42         }
43         status = cli_openx(cli, fname, O_RDWR|O_CREAT, DENY_ALL, &fnum);
44         if (!NT_STATUS_IS_OK(status)) {
45                 printf("open of %s failed (%s)\n", fname, nt_errstr(status));
46                 return false;
47         }
48         status = smbXcli_conn_samba_suicide(cli->conn, 1);
49         if (!NT_STATUS_IS_OK(status)) {
50                 printf("smbXcli_conn_samba_suicide failed: %s\n",
51                        nt_errstr(status));
52                 return false;
53         }
54
55         if (!torture_open_connection(&cli, 1)) {
56                 return false;
57         }
58         status = cli_ntcreate(
59                 cli, fname, 0,
60                 FILE_GENERIC_READ|FILE_GENERIC_WRITE|DELETE_ACCESS,
61                 FILE_ATTRIBUTE_NORMAL,
62                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
63                 FILE_OPEN, FILE_DELETE_ON_CLOSE, 0, &fnum, NULL);
64         if (!NT_STATUS_IS_OK(status)) {
65                 printf("2nd open of %s failed (%s)\n", fname,
66                        nt_errstr(status));
67                 return false;
68         }
69         cli_close(cli, fnum);
70
71         torture_close_connection(cli);
72         return NT_STATUS_IS_OK(status);
73 }
74
75 bool run_cleanup2(int dummy)
76 {
77         struct cli_state *cli1, *cli2, *cli3;
78         const char *fname = "\\cleanup2";
79         uint16_t fnum1, fnum2, fnum3;
80         NTSTATUS status;
81         char buf;
82
83         printf("CLEANUP2: Checking that a conflicting brlock is cleaned up\n");
84
85         if (!torture_open_connection(&cli1, 0)) {
86                 return false;
87         }
88         status = cli_ntcreate(
89                 cli1, fname, 0, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
90                 FILE_ATTRIBUTE_NORMAL,
91                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
92                 FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
93         if (!NT_STATUS_IS_OK(status)) {
94                 printf("open of %s failed (%s)\n", fname, nt_errstr(status));
95                 return false;
96         }
97         status = cli_lock32(cli1, fnum1, 0, 1, 0, WRITE_LOCK);
98         if (!NT_STATUS_IS_OK(status)) {
99                 printf("lock failed (%s)\n", nt_errstr(status));
100                 return false;
101         }
102
103         if (!torture_open_connection(&cli3, 1)) {
104                 return false;
105         }
106         status = cli_ntcreate(
107                 cli3, fname, 0, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
108                 FILE_ATTRIBUTE_NORMAL,
109                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
110                 FILE_OVERWRITE_IF, 0, 0, &fnum3, NULL);
111         if (!NT_STATUS_IS_OK(status)) {
112                 printf("open of %s failed (%s)\n", fname, nt_errstr(status));
113                 return false;
114         }
115         status = cli_lock32(cli3, fnum3, 1, 1, 0, WRITE_LOCK);
116         if (!NT_STATUS_IS_OK(status)) {
117                 printf("lock failed (%s)\n", nt_errstr(status));
118                 return false;
119         }
120
121         status = cli_lock32(cli1, fnum1, 2, 1, 0, WRITE_LOCK);
122         if (!NT_STATUS_IS_OK(status)) {
123                 printf("lock failed (%s)\n", nt_errstr(status));
124                 return false;
125         }
126
127         /*
128          * Check the file is indeed locked
129          */
130         if (!torture_open_connection(&cli2, 1)) {
131                 return false;
132         }
133         status = cli_ntcreate(
134                 cli2, fname, 0, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
135                 FILE_ATTRIBUTE_NORMAL,
136                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
137                 FILE_OPEN, 0, 0, &fnum2, NULL);
138         if (!NT_STATUS_IS_OK(status)) {
139                 printf("open of %s failed (%s)\n", fname, nt_errstr(status));
140                 return false;
141         }
142         buf = 'x';
143         status = cli_smbwrite(cli2, fnum2, &buf, 0, 1, NULL);
144         if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
145                 printf("write succeeded\n");
146                 return false;
147         }
148
149         /*
150          * Kill the lock holder
151          */
152         status = smbXcli_conn_samba_suicide(cli1->conn, 1);
153         if (!NT_STATUS_IS_OK(status)) {
154                 printf("smbXcli_conn_samba_suicide failed: %s\n",
155                        nt_errstr(status));
156                 return false;
157         }
158
159         /*
160          * Right now we don't clean up immediately. Re-open the 2nd connection.
161          */
162 #if 1
163         cli_shutdown(cli2);
164         if (!torture_open_connection(&cli2, 0)) {
165                 return false;
166         }
167         status = cli_ntcreate(
168                 cli2, fname, 0, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
169                 FILE_ATTRIBUTE_NORMAL,
170                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
171                 FILE_OPEN, 0, 0, &fnum2, NULL);
172         if (!NT_STATUS_IS_OK(status)) {
173                 printf("open of %s failed (%s)\n", fname, nt_errstr(status));
174                 return false;
175         }
176 #endif
177         status = cli_smbwrite(cli2, fnum2, &buf, 0, 1, NULL);
178         if (!NT_STATUS_IS_OK(status)) {
179                 printf("write failed: %s\n", nt_errstr(status));
180                 return false;
181         }
182         return true;
183 }
184
185 static bool create_stale_share_mode_entry(const char *fname,
186                                           struct file_id *p_id)
187 {
188         struct cli_state *cli;
189         uint16_t fnum;
190         NTSTATUS status;
191         SMB_STRUCT_STAT sbuf;
192         struct file_id id;
193
194         if (!torture_open_connection(&cli, 0)) {
195                 return false;
196         }
197
198         status = torture_setup_unix_extensions(cli);
199         if (!NT_STATUS_IS_OK(status)) {
200                 printf("torture_setup_unix_extensions failed: %s\n",
201                        nt_errstr(status));
202                 return false;
203         }
204         status = cli_openx(cli, fname, O_RDWR|O_CREAT, DENY_ALL, &fnum);
205         if (!NT_STATUS_IS_OK(status)) {
206                 printf("open of %s failed (%s)\n", fname, nt_errstr(status));
207                 return false;
208         }
209         status = cli_posix_stat(cli, fname, &sbuf);
210         if (!NT_STATUS_IS_OK(status)) {
211                 printf("cli_posix_stat failed: %s\n", nt_errstr(status));
212                 return false;
213         }
214         status = smbXcli_conn_samba_suicide(cli->conn, 1);
215         if (!NT_STATUS_IS_OK(status)) {
216                 printf("smbXcli_conn_samba_suicide failed: %s\n",
217                        nt_errstr(status));
218                 return false;
219         }
220
221         id.devid = sbuf.st_ex_rdev;
222         id.inode = sbuf.st_ex_ino;
223         id.extid = 0;
224
225         poll(NULL, 0, 1000);
226
227         *p_id = id;
228         return true;
229 }
230
231 static bool corrupt_dummy(struct share_mode_data *d)
232 {
233         return true;
234 }
235
236 static bool invalidate_sharemode(struct share_mode_data *d)
237 {
238         d->share_modes[0].op_type =
239                 OPLOCK_EXCLUSIVE|OPLOCK_BATCH|OPLOCK_LEVEL_II;
240         d->modified = true;
241         return true;
242 }
243
244 static bool duplicate_entry(struct share_mode_data *d, int i)
245 {
246         struct share_mode_entry *tmp;
247
248         if (i >= d->num_share_modes) {
249                 return false;
250         }
251
252         tmp = talloc_realloc(d, d->share_modes, struct share_mode_entry,
253                              d->num_share_modes + 1);
254         if (tmp == NULL) {
255                 return false;
256         }
257         d->share_modes = tmp;
258         d->num_share_modes += 1;
259         d->share_modes[d->num_share_modes-1] = d->share_modes[i];
260         d->modified = true;
261         return true;
262 }
263
264 static bool create_duplicate_batch(struct share_mode_data *d)
265 {
266         if (d->num_share_modes != 1) {
267                 return false;
268         }
269         d->share_modes[0].op_type = OPLOCK_BATCH;
270         if (!duplicate_entry(d, 0)) {
271                 return false;
272         }
273         return true;
274 }
275
276 struct corruption_fns {
277         bool (*fn)(struct share_mode_data *d);
278         const char *descr;
279 };
280
281 bool run_cleanup3(int dummy)
282 {
283         struct cli_state *cli;
284         const char *fname = "cleanup3";
285         uint16_t fnum;
286         NTSTATUS status;
287         struct share_mode_lock *lck;
288         struct file_id id;
289         size_t i;
290
291         struct corruption_fns fns[] = {
292                 { corrupt_dummy, "no corruption" },
293                 { invalidate_sharemode, "invalidate_sharemode" },
294                 { create_duplicate_batch, "create_duplicate_batch" },
295         };
296
297         printf("CLEANUP3: Checking that a share mode is cleaned up on "
298                "conflict\n");
299
300         for (i=0; i<ARRAY_SIZE(fns); i++) {
301
302                 printf("testing %s\n", fns[i].descr);
303
304                 if (!create_stale_share_mode_entry(fname, &id)) {
305                         printf("create_stale_entry failed\n");
306                         return false;
307                 }
308
309                 printf("%d %d %d\n", (int)id.devid, (int)id.inode,
310                        (int)id.extid);
311
312                 if (!locking_init()) {
313                         printf("locking_init failed\n");
314                         return false;
315                 }
316                 lck = get_existing_share_mode_lock(talloc_tos(), id);
317                 if (lck == NULL) {
318                         printf("get_existing_share_mode_lock failed\n");
319                         return false;
320                 }
321                 if (lck->data->num_share_modes != 1) {
322                         printf("get_existing_share_mode_lock did clean up\n");
323                         return false;
324                 }
325
326                 fns[i].fn(lck->data);
327
328                 TALLOC_FREE(lck);
329
330                 if (!torture_open_connection(&cli, 0)) {
331                         return false;
332                 }
333                 status = cli_openx(cli, fname, O_RDWR|O_CREAT, DENY_ALL,
334                                    &fnum);
335                 if (!NT_STATUS_IS_OK(status)) {
336                         printf("open of %s failed (%s)\n", fname,
337                                nt_errstr(status));
338                         return false;
339                 }
340                 lck = get_existing_share_mode_lock(talloc_tos(), id);
341                 if (lck == NULL) {
342                         printf("get_existing_share_mode_lock failed\n");
343                         return false;
344                 }
345                 if (lck->data->num_share_modes != 1) {
346                         printf("conflicting open did not clean up\n");
347                         return false;
348                 }
349                 TALLOC_FREE(lck);
350
351                 torture_close_connection(cli);
352         }
353
354         return true;
355 }
356
357 bool run_cleanup4(int dummy)
358 {
359         struct cli_state *cli1, *cli2;
360         const char *fname = "\\cleanup4";
361         uint16_t fnum1, fnum2;
362         NTSTATUS status;
363
364         printf("CLEANUP4: Checking that a conflicting share mode is cleaned "
365                "up\n");
366
367         if (!torture_open_connection(&cli1, 0)) {
368                 return false;
369         }
370         if (!torture_open_connection(&cli2, 0)) {
371                 return false;
372         }
373
374         status = cli_ntcreate(
375                 cli1, fname, 0,
376                 FILE_GENERIC_READ|DELETE_ACCESS,
377                 FILE_ATTRIBUTE_NORMAL,
378                 FILE_SHARE_READ|FILE_SHARE_DELETE,
379                 FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
380         if (!NT_STATUS_IS_OK(status)) {
381                 printf("creating file failed: %s\n",
382                        nt_errstr(status));
383                 return false;
384         }
385
386         status = cli_ntcreate(
387                 cli2, fname, 0,
388                 FILE_GENERIC_READ|DELETE_ACCESS,
389                 FILE_ATTRIBUTE_NORMAL,
390                 FILE_SHARE_READ|FILE_SHARE_DELETE,
391                 FILE_OPEN, 0, 0, &fnum2, NULL);
392         if (!NT_STATUS_IS_OK(status)) {
393                 printf("opening file 1st time failed: %s\n",
394                        nt_errstr(status));
395                 return false;
396         }
397
398         status = smbXcli_conn_samba_suicide(cli1->conn, 1);
399         if (!NT_STATUS_IS_OK(status)) {
400                 printf("smbXcli_conn_samba_suicide failed: %s\n",
401                        nt_errstr(status));
402                 return false;
403         }
404
405         /*
406          * The next open will conflict with both opens above. The first open
407          * above will be correctly cleaned up. A bug in smbd iterating over
408          * the share mode array made it skip the share conflict check for the
409          * second open. Trigger this bug.
410          */
411
412         status = cli_ntcreate(
413                 cli2, fname, 0,
414                 FILE_GENERIC_WRITE|DELETE_ACCESS,
415                 FILE_ATTRIBUTE_NORMAL,
416                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
417                 FILE_OPEN, 0, 0, &fnum2, NULL);
418         if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
419                 printf("opening file 2nd time returned: %s\n",
420                        nt_errstr(status));
421                 return false;
422         }
423
424         return true;
425 }