Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : basic locking tests
5 :
6 : Copyright (C) Andrew Tridgell 2000-2004
7 : Copyright (C) Jeremy Allison 2000-2004
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/libcli.h"
25 : #include "torture/util.h"
26 : #include "system/time.h"
27 : #include "system/filesys.h"
28 : #include "torture/basic/proto.h"
29 :
30 : #define BASEDIR "\\locktest"
31 :
32 : /*
33 : This test checks for two things:
34 :
35 : 1) correct support for retaining locks over a close (ie. the server
36 : must not use posix semantics)
37 : 2) support for lock timeouts
38 : */
39 2 : static bool torture_locktest1(struct torture_context *tctx,
40 : struct smbcli_state *cli1,
41 : struct smbcli_state *cli2)
42 : {
43 2 : const char *fname = BASEDIR "\\lockt1.lck";
44 : int fnum1, fnum2, fnum3;
45 : time_t t1, t2;
46 : unsigned int lock_timeout;
47 :
48 2 : torture_assert(tctx, torture_setup_dir(cli1, BASEDIR),
49 : talloc_asprintf(tctx, "Unable to set up %s", BASEDIR));
50 :
51 2 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
52 2 : torture_assert(tctx, fnum1 != -1,
53 : talloc_asprintf(tctx,
54 : "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)));
55 2 : fnum2 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
56 2 : torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx,
57 : "open2 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)));
58 2 : fnum3 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
59 2 : torture_assert(tctx, fnum3 != -1, talloc_asprintf(tctx,
60 : "open3 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)));
61 :
62 2 : torture_assert_ntstatus_ok(tctx,
63 : smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK),
64 : talloc_asprintf(tctx, "lock1 failed (%s)", smbcli_errstr(cli1->tree)));
65 :
66 2 : torture_assert(tctx,
67 : !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
68 : "lock2 succeeded! This is a locking bug\n");
69 :
70 2 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
71 2 : NT_STATUS_LOCK_NOT_GRANTED)) return false;
72 :
73 2 : torture_assert(tctx,
74 : !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
75 : "lock2 succeeded! This is a locking bug\n");
76 :
77 2 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
78 2 : NT_STATUS_FILE_LOCK_CONFLICT)) return false;
79 :
80 2 : torture_assert_ntstatus_ok(tctx,
81 : smbcli_lock(cli1->tree, fnum1, 5, 9, 0, WRITE_LOCK),
82 : talloc_asprintf(tctx,
83 : "lock1 failed (%s)", smbcli_errstr(cli1->tree)));
84 :
85 2 : torture_assert(tctx,
86 : !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 5, 9, 0, WRITE_LOCK)),
87 : "lock2 succeeded! This is a locking bug");
88 :
89 2 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
90 2 : NT_STATUS_LOCK_NOT_GRANTED)) return false;
91 :
92 2 : torture_assert(tctx,
93 : !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
94 : "lock2 succeeded! This is a locking bug");
95 :
96 2 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
97 2 : NT_STATUS_LOCK_NOT_GRANTED)) return false;
98 :
99 2 : torture_assert(tctx,
100 : !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
101 : "lock2 succeeded! This is a locking bug");
102 :
103 2 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
104 2 : NT_STATUS_FILE_LOCK_CONFLICT)) return false;
105 :
106 2 : lock_timeout = (6 + (random() % 20));
107 2 : torture_comment(tctx, "Testing lock timeout with timeout=%u\n",
108 : lock_timeout);
109 2 : t1 = time_mono(NULL);
110 2 : torture_assert(tctx,
111 : !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, lock_timeout * 1000, WRITE_LOCK)),
112 : "lock3 succeeded! This is a locking bug\n");
113 :
114 2 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
115 2 : NT_STATUS_FILE_LOCK_CONFLICT)) return false;
116 2 : t2 = time_mono(NULL);
117 :
118 2 : if (t2 - t1 < 5) {
119 0 : torture_fail(tctx,
120 : "error: This server appears not to support timed lock requests");
121 : }
122 2 : torture_comment(tctx, "server slept for %u seconds for a %u second timeout\n",
123 : (unsigned int)(t2-t1), lock_timeout);
124 :
125 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum2),
126 : talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree)));
127 :
128 2 : torture_assert(tctx,
129 : !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
130 : "lock4 succeeded! This is a locking bug");
131 :
132 2 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
133 2 : NT_STATUS_FILE_LOCK_CONFLICT)) return false;
134 :
135 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
136 : talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli1->tree)));
137 :
138 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum3),
139 : talloc_asprintf(tctx, "close3 failed (%s)", smbcli_errstr(cli2->tree)));
140 :
141 2 : torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname),
142 : talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree)));
143 :
144 2 : return true;
145 : }
146 :
147 :
148 : /*
149 : This test checks that
150 :
151 : 1) the server supports multiple locking contexts on the one SMB
152 : connection, distinguished by PID.
153 :
154 : 2) the server correctly fails overlapping locks made by the same PID (this
155 : goes against POSIX behaviour, which is why it is tricky to implement)
156 :
157 : 3) the server denies unlock requests by an incorrect client PID
158 : */
159 2 : static bool torture_locktest2(struct torture_context *tctx,
160 : struct smbcli_state *cli)
161 : {
162 2 : const char *fname = BASEDIR "\\lockt2.lck";
163 : int fnum1, fnum2, fnum3;
164 :
165 2 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR),
166 : talloc_asprintf(tctx, "Unable to set up %s", BASEDIR));
167 :
168 2 : torture_comment(tctx, "Testing pid context\n");
169 :
170 2 : cli->session->pid = 1;
171 :
172 2 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
173 2 : torture_assert(tctx, fnum1 != -1,
174 : talloc_asprintf(tctx,
175 : "open of %s failed (%s)", fname, smbcli_errstr(cli->tree)));
176 :
177 2 : fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
178 2 : torture_assert(tctx, fnum2 != -1,
179 : talloc_asprintf(tctx, "open2 of %s failed (%s)",
180 : fname, smbcli_errstr(cli->tree)));
181 :
182 2 : cli->session->pid = 2;
183 :
184 2 : fnum3 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
185 2 : torture_assert(tctx, fnum3 != -1,
186 : talloc_asprintf(tctx,
187 : "open3 of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)));
188 :
189 2 : cli->session->pid = 1;
190 :
191 2 : torture_assert_ntstatus_ok(tctx,
192 : smbcli_lock(cli->tree, fnum1, 0, 4, 0, WRITE_LOCK),
193 : talloc_asprintf(tctx,
194 : "lock1 failed (%s)", smbcli_errstr(cli->tree)));
195 :
196 2 : torture_assert(tctx,
197 : !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum1, 0, 4, 0, WRITE_LOCK)),
198 : "WRITE lock1 succeeded! This is a locking bug");
199 :
200 2 : if (!check_error(__location__, cli, ERRDOS, ERRlock,
201 2 : NT_STATUS_LOCK_NOT_GRANTED)) return false;
202 :
203 2 : torture_assert(tctx,
204 : !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum2, 0, 4, 0, WRITE_LOCK)),
205 : "WRITE lock2 succeeded! This is a locking bug");
206 :
207 2 : if (!check_error(__location__, cli, ERRDOS, ERRlock,
208 2 : NT_STATUS_LOCK_NOT_GRANTED)) return false;
209 :
210 2 : torture_assert(tctx,
211 : !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum2, 0, 4, 0, READ_LOCK)),
212 : "READ lock2 succeeded! This is a locking bug");
213 :
214 2 : if (!check_error(__location__, cli, ERRDOS, ERRlock,
215 2 : NT_STATUS_FILE_LOCK_CONFLICT)) return false;
216 :
217 2 : torture_assert_ntstatus_ok(tctx,
218 : smbcli_lock(cli->tree, fnum1, 100, 4, 0, WRITE_LOCK),
219 : talloc_asprintf(tctx,
220 : "lock at 100 failed (%s)", smbcli_errstr(cli->tree)));
221 :
222 2 : cli->session->pid = 2;
223 :
224 2 : torture_assert(tctx,
225 : !NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 100, 4)),
226 : "unlock at 100 succeeded! This is a locking bug");
227 :
228 2 : torture_assert(tctx,
229 : !NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 0, 4)),
230 : "unlock1 succeeded! This is a locking bug");
231 :
232 2 : if (!check_error(__location__, cli,
233 : ERRDOS, ERRnotlocked,
234 2 : NT_STATUS_RANGE_NOT_LOCKED)) return false;
235 :
236 2 : torture_assert(tctx,
237 : !NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 0, 8)),
238 : "unlock2 succeeded! This is a locking bug");
239 :
240 2 : if (!check_error(__location__, cli,
241 : ERRDOS, ERRnotlocked,
242 2 : NT_STATUS_RANGE_NOT_LOCKED)) return false;
243 :
244 2 : torture_assert(tctx,
245 : !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum3, 0, 4, 0, WRITE_LOCK)),
246 : "lock3 succeeded! This is a locking bug");
247 :
248 2 : if (!check_error(__location__, cli, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false;
249 :
250 2 : cli->session->pid = 1;
251 :
252 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum1),
253 : talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli->tree)));
254 :
255 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum2),
256 : talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli->tree)));
257 :
258 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum3),
259 : talloc_asprintf(tctx, "close3 failed (%s)", smbcli_errstr(cli->tree)));
260 :
261 2 : return true;
262 : }
263 :
264 :
265 : /*
266 : This test checks that
267 :
268 : 1) the server supports the full offset range in lock requests
269 : */
270 2 : static bool torture_locktest3(struct torture_context *tctx,
271 : struct smbcli_state *cli1,
272 : struct smbcli_state *cli2)
273 : {
274 2 : const char *fname = BASEDIR "\\lockt3.lck";
275 : int fnum1, fnum2, i;
276 : uint32_t offset;
277 : extern int torture_numops;
278 :
279 : #define NEXT_OFFSET offset += (~(uint32_t)0) / torture_numops
280 :
281 2 : torture_comment(tctx, "Testing 32 bit offset ranges");
282 :
283 2 : torture_assert(tctx, torture_setup_dir(cli1, BASEDIR),
284 : talloc_asprintf(tctx, "Unable to set up %s", BASEDIR));
285 :
286 2 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
287 2 : torture_assert(tctx, fnum1 != -1,
288 : talloc_asprintf(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)));
289 2 : fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
290 2 : torture_assert(tctx, fnum2 != -1,
291 : talloc_asprintf(tctx, "open2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)));
292 :
293 2 : torture_comment(tctx, "Establishing %d locks\n", torture_numops);
294 :
295 22 : for (offset=i=0;i<torture_numops;i++) {
296 20 : NEXT_OFFSET;
297 20 : torture_assert_ntstatus_ok(tctx,
298 : smbcli_lock(cli1->tree, fnum1, offset-1, 1, 0, WRITE_LOCK),
299 : talloc_asprintf(tctx, "lock1 %d failed (%s)", i, smbcli_errstr(cli1->tree)));
300 :
301 20 : torture_assert_ntstatus_ok(tctx,
302 : smbcli_lock(cli2->tree, fnum2, offset-2, 1, 0, WRITE_LOCK),
303 : talloc_asprintf(tctx, "lock2 %d failed (%s)",
304 : i, smbcli_errstr(cli1->tree)));
305 : }
306 :
307 2 : torture_comment(tctx, "Testing %d locks\n", torture_numops);
308 :
309 22 : for (offset=i=0;i<torture_numops;i++) {
310 20 : NEXT_OFFSET;
311 :
312 20 : torture_assert(tctx,
313 : !NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, offset-2, 1, 0, WRITE_LOCK)),
314 : talloc_asprintf(tctx, "error: lock1 %d succeeded!", i));
315 :
316 20 : torture_assert(tctx,
317 : !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, offset-1, 1, 0, WRITE_LOCK)),
318 : talloc_asprintf(tctx, "error: lock2 %d succeeded!", i));
319 :
320 20 : torture_assert(tctx,
321 : !NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, offset-1, 1, 0, WRITE_LOCK)),
322 : talloc_asprintf(tctx, "error: lock3 %d succeeded!", i));
323 :
324 20 : torture_assert(tctx,
325 : !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, offset-2, 1, 0, WRITE_LOCK)),
326 : talloc_asprintf(tctx, "error: lock4 %d succeeded!", i));
327 : }
328 :
329 2 : torture_comment(tctx, "Removing %d locks\n", torture_numops);
330 :
331 22 : for (offset=i=0;i<torture_numops;i++) {
332 20 : NEXT_OFFSET;
333 :
334 20 : torture_assert_ntstatus_ok(tctx,
335 : smbcli_unlock(cli1->tree, fnum1, offset-1, 1),
336 : talloc_asprintf(tctx, "unlock1 %d failed (%s)",
337 : i,
338 : smbcli_errstr(cli1->tree)));
339 :
340 20 : torture_assert_ntstatus_ok(tctx,
341 : smbcli_unlock(cli2->tree, fnum2, offset-2, 1),
342 : talloc_asprintf(tctx, "unlock2 %d failed (%s)",
343 : i,
344 : smbcli_errstr(cli1->tree)));
345 : }
346 :
347 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
348 : talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree)));
349 :
350 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2),
351 : talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree)));
352 :
353 2 : torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname),
354 : talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree)));
355 :
356 2 : return true;
357 : }
358 :
359 : #define EXPECTED(ret, v) if ((ret) != (v)) { \
360 : torture_comment(tctx, "** "); correct = false; \
361 : }
362 :
363 : /*
364 : looks at overlapping locks
365 : */
366 2 : static bool torture_locktest4(struct torture_context *tctx,
367 : struct smbcli_state *cli1,
368 : struct smbcli_state *cli2)
369 : {
370 2 : const char *fname = BASEDIR "\\lockt4.lck";
371 : int fnum1, fnum2, f;
372 : bool ret;
373 : uint8_t buf[1000];
374 2 : bool correct = true;
375 :
376 2 : if (!torture_setup_dir(cli1, BASEDIR)) {
377 0 : return false;
378 : }
379 :
380 2 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
381 2 : fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
382 :
383 2 : memset(buf, 0, sizeof(buf));
384 :
385 2 : if (smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
386 0 : torture_comment(tctx, "Failed to create file\n");
387 0 : correct = false;
388 0 : goto fail;
389 : }
390 :
391 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK)) &&
392 2 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 2, 4, 0, WRITE_LOCK));
393 2 : EXPECTED(ret, false);
394 2 : torture_comment(tctx, "the same process %s set overlapping write locks\n", ret?"can":"cannot");
395 :
396 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 10, 4, 0, READ_LOCK)) &&
397 2 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 12, 4, 0, READ_LOCK));
398 2 : EXPECTED(ret, true);
399 2 : torture_comment(tctx, "the same process %s set overlapping read locks\n", ret?"can":"cannot");
400 :
401 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 20, 4, 0, WRITE_LOCK)) &&
402 2 : NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 22, 4, 0, WRITE_LOCK));
403 2 : EXPECTED(ret, false);
404 2 : torture_comment(tctx, "a different connection %s set overlapping write locks\n", ret?"can":"cannot");
405 :
406 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 30, 4, 0, READ_LOCK)) &&
407 2 : NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 32, 4, 0, READ_LOCK));
408 2 : EXPECTED(ret, true);
409 2 : torture_comment(tctx, "a different connection %s set overlapping read locks\n", ret?"can":"cannot");
410 :
411 4 : ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 40, 4, 0, WRITE_LOCK))) &&
412 2 : NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 42, 4, 0, WRITE_LOCK)));
413 2 : EXPECTED(ret, false);
414 2 : torture_comment(tctx, "a different pid %s set overlapping write locks\n", ret?"can":"cannot");
415 :
416 4 : ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 50, 4, 0, READ_LOCK))) &&
417 2 : NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 52, 4, 0, READ_LOCK)));
418 2 : EXPECTED(ret, true);
419 2 : torture_comment(tctx, "a different pid %s set overlapping read locks\n", ret?"can":"cannot");
420 :
421 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 60, 4, 0, READ_LOCK)) &&
422 2 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 60, 4, 0, READ_LOCK));
423 2 : EXPECTED(ret, true);
424 2 : torture_comment(tctx, "the same process %s set the same read lock twice\n", ret?"can":"cannot");
425 :
426 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 70, 4, 0, WRITE_LOCK)) &&
427 2 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 70, 4, 0, WRITE_LOCK));
428 2 : EXPECTED(ret, false);
429 2 : torture_comment(tctx, "the same process %s set the same write lock twice\n", ret?"can":"cannot");
430 :
431 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 80, 4, 0, READ_LOCK)) &&
432 2 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 80, 4, 0, WRITE_LOCK));
433 2 : EXPECTED(ret, false);
434 2 : torture_comment(tctx, "the same process %s overlay a read lock with a write lock\n", ret?"can":"cannot");
435 :
436 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 90, 4, 0, WRITE_LOCK)) &&
437 2 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 90, 4, 0, READ_LOCK));
438 2 : EXPECTED(ret, true);
439 2 : torture_comment(tctx, "the same process %s overlay a write lock with a read lock\n", ret?"can":"cannot");
440 :
441 4 : ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 100, 4, 0, WRITE_LOCK))) &&
442 2 : NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 100, 4, 0, READ_LOCK)));
443 2 : EXPECTED(ret, false);
444 2 : torture_comment(tctx, "a different pid %s overlay a write lock with a read lock\n", ret?"can":"cannot");
445 :
446 6 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 110, 4, 0, READ_LOCK)) &&
447 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 112, 4, 0, READ_LOCK)) &&
448 2 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 110, 6));
449 2 : EXPECTED(ret, false);
450 2 : torture_comment(tctx, "the same process %s coalesce read locks\n", ret?"can":"cannot");
451 :
452 :
453 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 120, 4, 0, WRITE_LOCK)) &&
454 2 : (smbcli_read(cli2->tree, fnum2, buf, 120, 4) == 4);
455 2 : EXPECTED(ret, false);
456 2 : torture_comment(tctx, "this server %s strict write locking\n", ret?"doesn't do":"does");
457 :
458 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 130, 4, 0, READ_LOCK)) &&
459 2 : (smbcli_write(cli2->tree, fnum2, 0, buf, 130, 4) == 4);
460 2 : EXPECTED(ret, false);
461 2 : torture_comment(tctx, "this server %s strict read locking\n", ret?"doesn't do":"does");
462 :
463 :
464 6 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 140, 4, 0, READ_LOCK)) &&
465 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 140, 4, 0, READ_LOCK)) &&
466 6 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 140, 4)) &&
467 2 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 140, 4));
468 2 : EXPECTED(ret, true);
469 2 : torture_comment(tctx, "this server %s do recursive read locking\n", ret?"does":"doesn't");
470 :
471 :
472 6 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 150, 4, 0, WRITE_LOCK)) &&
473 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 150, 4, 0, READ_LOCK)) &&
474 4 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 150, 4)) &&
475 4 : (smbcli_read(cli2->tree, fnum2, buf, 150, 4) == 4) &&
476 6 : !(smbcli_write(cli2->tree, fnum2, 0, buf, 150, 4) == 4) &&
477 2 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 150, 4));
478 2 : EXPECTED(ret, true);
479 2 : torture_comment(tctx, "this server %s do recursive lock overlays\n", ret?"does":"doesn't");
480 :
481 6 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 160, 4, 0, READ_LOCK)) &&
482 4 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 160, 4)) &&
483 6 : (smbcli_write(cli2->tree, fnum2, 0, buf, 160, 4) == 4) &&
484 2 : (smbcli_read(cli2->tree, fnum2, buf, 160, 4) == 4);
485 2 : EXPECTED(ret, true);
486 2 : torture_comment(tctx, "the same process %s remove a read lock using write locking\n", ret?"can":"cannot");
487 :
488 6 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 170, 4, 0, WRITE_LOCK)) &&
489 4 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 170, 4)) &&
490 6 : (smbcli_write(cli2->tree, fnum2, 0, buf, 170, 4) == 4) &&
491 2 : (smbcli_read(cli2->tree, fnum2, buf, 170, 4) == 4);
492 2 : EXPECTED(ret, true);
493 2 : torture_comment(tctx, "the same process %s remove a write lock using read locking\n", ret?"can":"cannot");
494 :
495 6 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 190, 4, 0, WRITE_LOCK)) &&
496 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 190, 4, 0, READ_LOCK)) &&
497 4 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 190, 4)) &&
498 6 : !(smbcli_write(cli2->tree, fnum2, 0, buf, 190, 4) == 4) &&
499 2 : (smbcli_read(cli2->tree, fnum2, buf, 190, 4) == 4);
500 2 : EXPECTED(ret, true);
501 2 : torture_comment(tctx, "the same process %s remove the first lock first\n", ret?"does":"doesn't");
502 :
503 2 : smbcli_close(cli1->tree, fnum1);
504 2 : smbcli_close(cli2->tree, fnum2);
505 2 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
506 2 : f = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
507 6 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 8, 0, READ_LOCK)) &&
508 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, f, 0, 1, 0, READ_LOCK)) &&
509 4 : NT_STATUS_IS_OK(smbcli_close(cli1->tree, fnum1)) &&
510 6 : ((fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE)) != -1) &&
511 2 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 7, 1, 0, WRITE_LOCK));
512 2 : smbcli_close(cli1->tree, f);
513 2 : smbcli_close(cli1->tree, fnum1);
514 2 : EXPECTED(ret, true);
515 2 : torture_comment(tctx, "the server %s have the NT byte range lock bug\n", !ret?"does":"doesn't");
516 :
517 2 : fail:
518 2 : smbcli_close(cli1->tree, fnum1);
519 2 : smbcli_close(cli2->tree, fnum2);
520 2 : smbcli_unlink(cli1->tree, fname);
521 :
522 2 : return correct;
523 : }
524 :
525 : /*
526 : looks at lock upgrade/downgrade.
527 : */
528 2 : static bool torture_locktest5(struct torture_context *tctx, struct smbcli_state *cli1,
529 : struct smbcli_state *cli2)
530 : {
531 2 : const char *fname = BASEDIR "\\lockt5.lck";
532 : int fnum1, fnum2, fnum3;
533 : bool ret;
534 : uint8_t buf[1000];
535 2 : bool correct = true;
536 :
537 2 : if (!torture_setup_dir(cli1, BASEDIR)) {
538 0 : return false;
539 : }
540 :
541 2 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
542 2 : fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
543 2 : fnum3 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
544 :
545 2 : memset(buf, 0, sizeof(buf));
546 :
547 2 : torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) == sizeof(buf),
548 : "Failed to create file");
549 :
550 : /* Check for NT bug... */
551 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 8, 0, READ_LOCK)) &&
552 2 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum3, 0, 1, 0, READ_LOCK));
553 2 : smbcli_close(cli1->tree, fnum1);
554 2 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
555 2 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 7, 1, 0, WRITE_LOCK));
556 2 : EXPECTED(ret, true);
557 2 : torture_comment(tctx, "this server %s the NT locking bug\n", ret ? "doesn't have" : "has");
558 2 : smbcli_close(cli1->tree, fnum1);
559 2 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
560 2 : smbcli_unlock(cli1->tree, fnum3, 0, 1);
561 :
562 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK)) &&
563 2 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 1, 1, 0, READ_LOCK));
564 2 : EXPECTED(ret, true);
565 2 : torture_comment(tctx, "the same process %s overlay a write with a read lock\n", ret?"can":"cannot");
566 :
567 2 : ret = NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, READ_LOCK));
568 2 : EXPECTED(ret, false);
569 :
570 2 : torture_comment(tctx, "a different process %s get a read lock on the first process lock stack\n", ret?"can":"cannot");
571 :
572 : /* Unlock the process 2 lock. */
573 2 : smbcli_unlock(cli2->tree, fnum2, 0, 4);
574 :
575 2 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum3, 0, 4, 0, READ_LOCK));
576 2 : EXPECTED(ret, false);
577 :
578 2 : torture_comment(tctx, "the same process on a different fnum %s get a read lock\n", ret?"can":"cannot");
579 :
580 : /* Unlock the process 1 fnum3 lock. */
581 2 : smbcli_unlock(cli1->tree, fnum3, 0, 4);
582 :
583 : /* Stack 2 more locks here. */
584 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, READ_LOCK)) &&
585 2 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, READ_LOCK));
586 :
587 2 : EXPECTED(ret, true);
588 2 : torture_comment(tctx, "the same process %s stack read locks\n", ret?"can":"cannot");
589 :
590 : /* Unlock the first process lock, then check this was the WRITE lock that was
591 : removed. */
592 :
593 4 : ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)) &&
594 2 : NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, READ_LOCK));
595 :
596 2 : EXPECTED(ret, true);
597 2 : torture_comment(tctx, "the first unlock removes the %s lock\n", ret?"WRITE":"READ");
598 :
599 : /* Unlock the process 2 lock. */
600 2 : smbcli_unlock(cli2->tree, fnum2, 0, 4);
601 :
602 : /* We should have 3 stacked locks here. Ensure we need to do 3 unlocks. */
603 :
604 6 : ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 1, 1)) &&
605 4 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)) &&
606 2 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4));
607 :
608 2 : EXPECTED(ret, true);
609 2 : torture_comment(tctx, "the same process %s unlock the stack of 3 locks\n", ret?"can":"cannot");
610 :
611 : /* Ensure the next unlock fails. */
612 2 : ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4));
613 2 : EXPECTED(ret, false);
614 2 : torture_comment(tctx, "the same process %s count the lock stack\n", !ret?"can":"cannot");
615 :
616 : /* Ensure connection 2 can get a write lock. */
617 2 : ret = NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, WRITE_LOCK));
618 2 : EXPECTED(ret, true);
619 :
620 2 : torture_comment(tctx, "a different process %s get a write lock on the unlocked stack\n", ret?"can":"cannot");
621 :
622 :
623 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
624 : talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree)));
625 :
626 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2),
627 : talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree)));
628 :
629 2 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum3),
630 : talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree)));
631 :
632 2 : torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname),
633 : talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree)));
634 :
635 2 : return correct;
636 : }
637 :
638 : /*
639 : tries the unusual lockingX locktype bits
640 : */
641 2 : static bool torture_locktest6(struct torture_context *tctx,
642 : struct smbcli_state *cli)
643 : {
644 2 : const char *fname[1] = { "\\lock6.txt" };
645 : int i;
646 : int fnum;
647 : NTSTATUS status;
648 :
649 2 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
650 :
651 4 : for (i=0;i<1;i++) {
652 2 : torture_comment(tctx, "Testing %s\n", fname[i]);
653 :
654 2 : smbcli_unlink(cli->tree, fname[i]);
655 :
656 2 : fnum = smbcli_open(cli->tree, fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
657 2 : status = smbcli_locktype(cli->tree, fnum, 0, 8, 0, LOCKING_ANDX_CHANGE_LOCKTYPE);
658 2 : smbcli_close(cli->tree, fnum);
659 2 : torture_comment(tctx, "CHANGE_LOCKTYPE gave %s\n", nt_errstr(status));
660 :
661 2 : fnum = smbcli_open(cli->tree, fname[i], O_RDWR, DENY_NONE);
662 2 : status = smbcli_locktype(cli->tree, fnum, 0, 8, 0, LOCKING_ANDX_CANCEL_LOCK);
663 2 : smbcli_close(cli->tree, fnum);
664 2 : torture_comment(tctx, "CANCEL_LOCK gave %s\n", nt_errstr(status));
665 :
666 2 : smbcli_unlink(cli->tree, fname[i]);
667 : }
668 :
669 2 : return true;
670 : }
671 :
672 2 : static bool torture_locktest7(struct torture_context *tctx,
673 : struct smbcli_state *cli1)
674 : {
675 2 : const char *fname = BASEDIR "\\lockt7.lck";
676 : int fnum1;
677 2 : int fnum2 = -1;
678 : size_t size;
679 : uint8_t buf[200];
680 2 : bool correct = false;
681 :
682 2 : torture_assert(tctx, torture_setup_dir(cli1, BASEDIR),
683 : talloc_asprintf(tctx, "Unable to set up %s", BASEDIR));
684 :
685 2 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
686 :
687 2 : memset(buf, 0, sizeof(buf));
688 :
689 2 : torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) == sizeof(buf),
690 : "Failed to create file");
691 :
692 2 : cli1->session->pid = 1;
693 :
694 2 : torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, 130, 4, 0, READ_LOCK),
695 : talloc_asprintf(tctx, "Unable to apply read lock on range 130:4, error was %s",
696 : smbcli_errstr(cli1->tree)));
697 :
698 2 : torture_comment(tctx, "pid1 successfully locked range 130:4 for READ\n");
699 :
700 2 : torture_assert(tctx, smbcli_read(cli1->tree, fnum1, buf, 130, 4) == 4,
701 : talloc_asprintf(tctx, "pid1 unable to read the range 130:4, error was %s)",
702 : smbcli_errstr(cli1->tree)));
703 :
704 2 : torture_comment(tctx, "pid1 successfully read the range 130:4\n");
705 :
706 2 : if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) {
707 2 : torture_comment(tctx, "pid1 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree));
708 2 : torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT,
709 : "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)");
710 : } else {
711 0 : torture_fail(tctx, "pid1 successfully wrote to the range 130:4 (should be denied)");
712 : }
713 :
714 2 : cli1->session->pid = 2;
715 :
716 2 : if (smbcli_read(cli1->tree, fnum1, buf, 130, 4) != 4) {
717 0 : torture_comment(tctx, "pid2 unable to read the range 130:4, error was %s\n", smbcli_errstr(cli1->tree));
718 : } else {
719 2 : torture_comment(tctx, "pid2 successfully read the range 130:4\n");
720 : }
721 :
722 2 : if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) {
723 2 : torture_comment(tctx, "pid2 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree));
724 2 : torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT,
725 : "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)");
726 : } else {
727 0 : torture_fail(tctx, "pid2 successfully wrote to the range 130:4 (should be denied)");
728 : }
729 :
730 2 : cli1->session->pid = 1;
731 2 : smbcli_unlock(cli1->tree, fnum1, 130, 4);
732 :
733 2 : torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, 130, 4, 0, WRITE_LOCK),
734 : talloc_asprintf(tctx, "Unable to apply write lock on range 130:4, error was %s",
735 : smbcli_errstr(cli1->tree)));
736 2 : torture_comment(tctx, "pid1 successfully locked range 130:4 for WRITE\n");
737 :
738 2 : torture_assert(tctx, smbcli_read(cli1->tree, fnum1, buf, 130, 4) == 4,
739 : talloc_asprintf(tctx, "pid1 unable to read the range 130:4, error was %s",
740 : smbcli_errstr(cli1->tree)));
741 2 : torture_comment(tctx, "pid1 successfully read the range 130:4\n");
742 :
743 2 : torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) == 4,
744 : talloc_asprintf(tctx, "pid1 unable to write to the range 130:4, error was %s",
745 : smbcli_errstr(cli1->tree)));
746 2 : torture_comment(tctx, "pid1 successfully wrote to the range 130:4\n");
747 :
748 2 : cli1->session->pid = 2;
749 :
750 2 : if (smbcli_read(cli1->tree, fnum1, buf, 130, 4) != 4) {
751 2 : torture_comment(tctx, "pid2 unable to read the range 130:4, error was %s\n",
752 : smbcli_errstr(cli1->tree));
753 2 : torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT,
754 : "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)");
755 : } else {
756 0 : torture_fail(tctx, "pid2 successfully read the range 130:4 (should be denied)");
757 : }
758 :
759 2 : if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) {
760 2 : torture_comment(tctx, "pid2 unable to write to the range 130:4, error was %s\n",
761 : smbcli_errstr(cli1->tree));
762 2 : if (!NT_STATUS_EQUAL(smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT)) {
763 0 : torture_comment(tctx, "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT) (%s)\n",
764 : __location__);
765 0 : goto fail;
766 : }
767 : } else {
768 0 : torture_comment(tctx, "pid2 successfully wrote to the range 130:4 (should be denied) (%s)\n",
769 : __location__);
770 0 : goto fail;
771 : }
772 :
773 2 : torture_comment(tctx, "Testing truncate of locked file.\n");
774 :
775 2 : fnum2 = smbcli_open(cli1->tree, fname, O_RDWR|O_TRUNC, DENY_NONE);
776 :
777 2 : torture_assert(tctx, fnum2 != -1, "Unable to truncate locked file");
778 :
779 2 : torture_comment(tctx, "Truncated locked file.\n");
780 :
781 2 : torture_assert_ntstatus_ok(tctx, smbcli_getatr(cli1->tree, fname, NULL, &size, NULL),
782 : talloc_asprintf(tctx, "getatr failed (%s)", smbcli_errstr(cli1->tree)));
783 :
784 2 : torture_assert(tctx, size == 0, talloc_asprintf(tctx, "Unable to truncate locked file. Size was %u", (unsigned)size));
785 :
786 2 : cli1->session->pid = 1;
787 :
788 2 : smbcli_unlock(cli1->tree, fnum1, 130, 4);
789 2 : correct = true;
790 :
791 2 : fail:
792 2 : smbcli_close(cli1->tree, fnum1);
793 2 : smbcli_close(cli1->tree, fnum2);
794 2 : smbcli_unlink(cli1->tree, fname);
795 :
796 2 : return correct;
797 : }
798 :
799 964 : struct torture_suite *torture_base_locktest(TALLOC_CTX *mem_ctx)
800 : {
801 964 : struct torture_suite *suite = torture_suite_create(mem_ctx, "lock");
802 964 : torture_suite_add_2smb_test(suite, "LOCK1", torture_locktest1);
803 964 : torture_suite_add_1smb_test(suite, "LOCK2", torture_locktest2);
804 964 : torture_suite_add_2smb_test(suite, "LOCK3", torture_locktest3);
805 964 : torture_suite_add_2smb_test(suite, "LOCK4", torture_locktest4);
806 964 : torture_suite_add_2smb_test(suite, "LOCK5", torture_locktest5);
807 964 : torture_suite_add_1smb_test(suite, "LOCK6", torture_locktest6);
808 964 : torture_suite_add_1smb_test(suite, "LOCK7", torture_locktest7);
809 :
810 964 : return suite;
811 : }
|