s3: client : Add extra return parameter to all client open calls.
[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;
78         const char *fname = "\\cleanup2";
79         uint16_t fnum1, fnum2;
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         /*
104          * Check the file is indeed locked
105          */
106         if (!torture_open_connection(&cli2, 0)) {
107                 return false;
108         }
109         status = cli_ntcreate(
110                 cli2, fname, 0, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
111                 FILE_ATTRIBUTE_NORMAL,
112                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
113                 FILE_OPEN, 0, 0, &fnum2, NULL);
114         if (!NT_STATUS_IS_OK(status)) {
115                 printf("open of %s failed (%s)\n", fname, nt_errstr(status));
116                 return false;
117         }
118         buf = 'x';
119         status = cli_smbwrite(cli2, fnum2, &buf, 0, 1, NULL);
120         if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
121                 printf("write succeeded\n");
122                 return false;
123         }
124
125         /*
126          * Kill the lock holder
127          */
128         status = smbXcli_conn_samba_suicide(cli1->conn, 1);
129         if (!NT_STATUS_IS_OK(status)) {
130                 printf("smbXcli_conn_samba_suicide failed: %s\n",
131                        nt_errstr(status));
132                 return false;
133         }
134
135         /*
136          * Right now we don't clean up immediately. Re-open the 2nd connection.
137          */
138 #if 1
139         cli_shutdown(cli2);
140         if (!torture_open_connection(&cli2, 0)) {
141                 return false;
142         }
143         status = cli_ntcreate(
144                 cli2, fname, 0, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
145                 FILE_ATTRIBUTE_NORMAL,
146                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
147                 FILE_OPEN, 0, 0, &fnum2, NULL);
148         if (!NT_STATUS_IS_OK(status)) {
149                 printf("open of %s failed (%s)\n", fname, nt_errstr(status));
150                 return false;
151         }
152 #endif
153         status = cli_smbwrite(cli2, fnum2, &buf, 0, 1, NULL);
154         if (!NT_STATUS_IS_OK(status)) {
155                 printf("write failed: %s\n", nt_errstr(status));
156                 return false;
157         }
158         return true;
159 }
160
161 static bool create_stale_share_mode_entry(const char *fname,
162                                           struct file_id *p_id)
163 {
164         struct cli_state *cli;
165         uint16_t fnum;
166         NTSTATUS status;
167         SMB_STRUCT_STAT sbuf;
168         struct file_id id;
169
170         if (!torture_open_connection(&cli, 0)) {
171                 return false;
172         }
173
174         status = torture_setup_unix_extensions(cli);
175         if (!NT_STATUS_IS_OK(status)) {
176                 printf("torture_setup_unix_extensions failed: %s\n",
177                        nt_errstr(status));
178                 return false;
179         }
180         status = cli_openx(cli, fname, O_RDWR|O_CREAT, DENY_ALL, &fnum);
181         if (!NT_STATUS_IS_OK(status)) {
182                 printf("open of %s failed (%s)\n", fname, nt_errstr(status));
183                 return false;
184         }
185         status = cli_posix_stat(cli, fname, &sbuf);
186         if (!NT_STATUS_IS_OK(status)) {
187                 printf("cli_posix_stat failed: %s\n", nt_errstr(status));
188                 return false;
189         }
190         status = smbXcli_conn_samba_suicide(cli->conn, 1);
191         if (!NT_STATUS_IS_OK(status)) {
192                 printf("smbXcli_conn_samba_suicide failed: %s\n",
193                        nt_errstr(status));
194                 return false;
195         }
196
197         id.devid = sbuf.st_ex_rdev;
198         id.inode = sbuf.st_ex_ino;
199         id.extid = 0;
200
201         poll(NULL, 0, 1000);
202
203         *p_id = id;
204         return true;
205 }
206
207 static bool corrupt_dummy(struct share_mode_data *d)
208 {
209         return true;
210 }
211
212 static bool invalidate_sharemode(struct share_mode_data *d)
213 {
214         d->share_modes[0].op_type =
215                 OPLOCK_EXCLUSIVE|OPLOCK_BATCH|OPLOCK_LEVEL_II;
216         d->modified = true;
217         return true;
218 }
219
220 static bool duplicate_entry(struct share_mode_data *d, int i)
221 {
222         struct share_mode_entry *tmp;
223
224         if (i >= d->num_share_modes) {
225                 return false;
226         }
227
228         tmp = talloc_realloc(d, d->share_modes, struct share_mode_entry,
229                              d->num_share_modes + 1);
230         if (tmp == NULL) {
231                 return false;
232         }
233         d->share_modes = tmp;
234         d->num_share_modes += 1;
235         d->share_modes[d->num_share_modes-1] = d->share_modes[i];
236         d->modified = true;
237         return true;
238 }
239
240 static bool create_duplicate_batch(struct share_mode_data *d)
241 {
242         if (d->num_share_modes != 1) {
243                 return false;
244         }
245         d->share_modes[0].op_type = OPLOCK_BATCH;
246         if (!duplicate_entry(d, 0)) {
247                 return false;
248         }
249         return true;
250 }
251
252 struct corruption_fns {
253         bool (*fn)(struct share_mode_data *d);
254         const char *descr;
255 };
256
257 bool run_cleanup3(int dummy)
258 {
259         struct cli_state *cli;
260         const char *fname = "cleanup3";
261         uint16_t fnum;
262         NTSTATUS status;
263         struct share_mode_lock *lck;
264         struct file_id id;
265         size_t i;
266
267         struct corruption_fns fns[] = {
268                 { corrupt_dummy, "no corruption" },
269                 { invalidate_sharemode, "invalidate_sharemode" },
270                 { create_duplicate_batch, "create_duplicate_batch" },
271         };
272
273         printf("CLEANUP3: Checking that a share mode is cleaned up on "
274                "conflict\n");
275
276         for (i=0; i<ARRAY_SIZE(fns); i++) {
277
278                 printf("testing %s\n", fns[i].descr);
279
280                 if (!create_stale_share_mode_entry(fname, &id)) {
281                         printf("create_stale_entry failed\n");
282                         return false;
283                 }
284
285                 printf("%d %d %d\n", (int)id.devid, (int)id.inode,
286                        (int)id.extid);
287
288                 if (!locking_init()) {
289                         printf("locking_init failed\n");
290                         return false;
291                 }
292                 lck = get_existing_share_mode_lock(talloc_tos(), id);
293                 if (lck == NULL) {
294                         printf("get_existing_share_mode_lock failed\n");
295                         return false;
296                 }
297                 if (lck->data->num_share_modes != 1) {
298                         printf("get_existing_share_mode_lock did clean up\n");
299                         return false;
300                 }
301
302                 fns[i].fn(lck->data);
303
304                 TALLOC_FREE(lck);
305
306                 if (!torture_open_connection(&cli, 0)) {
307                         return false;
308                 }
309                 status = cli_openx(cli, fname, O_RDWR|O_CREAT, DENY_ALL,
310                                    &fnum);
311                 if (!NT_STATUS_IS_OK(status)) {
312                         printf("open of %s failed (%s)\n", fname,
313                                nt_errstr(status));
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("conflicting open did not clean up\n");
323                         return false;
324                 }
325                 TALLOC_FREE(lck);
326
327                 torture_close_connection(cli);
328         }
329
330         return true;
331 }
332
333 bool run_cleanup4(int dummy)
334 {
335         struct cli_state *cli1, *cli2;
336         const char *fname = "\\cleanup4";
337         uint16_t fnum1, fnum2;
338         NTSTATUS status;
339
340         printf("CLEANUP4: Checking that a conflicting share mode is cleaned "
341                "up\n");
342
343         if (!torture_open_connection(&cli1, 0)) {
344                 return false;
345         }
346         if (!torture_open_connection(&cli2, 0)) {
347                 return false;
348         }
349
350         status = cli_ntcreate(
351                 cli1, fname, 0,
352                 FILE_GENERIC_READ|DELETE_ACCESS,
353                 FILE_ATTRIBUTE_NORMAL,
354                 FILE_SHARE_READ|FILE_SHARE_DELETE,
355                 FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
356         if (!NT_STATUS_IS_OK(status)) {
357                 printf("creating file failed: %s\n",
358                        nt_errstr(status));
359                 return false;
360         }
361
362         status = cli_ntcreate(
363                 cli2, fname, 0,
364                 FILE_GENERIC_READ|DELETE_ACCESS,
365                 FILE_ATTRIBUTE_NORMAL,
366                 FILE_SHARE_READ|FILE_SHARE_DELETE,
367                 FILE_OPEN, 0, 0, &fnum2, NULL);
368         if (!NT_STATUS_IS_OK(status)) {
369                 printf("opening file 1st time failed: %s\n",
370                        nt_errstr(status));
371                 return false;
372         }
373
374         status = smbXcli_conn_samba_suicide(cli1->conn, 1);
375         if (!NT_STATUS_IS_OK(status)) {
376                 printf("smbXcli_conn_samba_suicide failed: %s\n",
377                        nt_errstr(status));
378                 return false;
379         }
380
381         /*
382          * The next open will conflict with both opens above. The first open
383          * above will be correctly cleaned up. A bug in smbd iterating over
384          * the share mode array made it skip the share conflict check for the
385          * second open. Trigger this bug.
386          */
387
388         status = cli_ntcreate(
389                 cli2, fname, 0,
390                 FILE_GENERIC_WRITE|DELETE_ACCESS,
391                 FILE_ATTRIBUTE_NORMAL,
392                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
393                 FILE_OPEN, 0, 0, &fnum2, NULL);
394         if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
395                 printf("opening file 2nd time returned: %s\n",
396                        nt_errstr(status));
397                 return false;
398         }
399
400         return true;
401 }