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 4 : static bool torture_locktest1(struct torture_context *tctx,
40 : struct smbcli_state *cli1,
41 : struct smbcli_state *cli2)
42 : {
43 4 : const char *fname = BASEDIR "\\lockt1.lck";
44 0 : int fnum1, fnum2, fnum3;
45 0 : time_t t1, t2;
46 0 : unsigned int lock_timeout;
47 :
48 4 : torture_assert(tctx, torture_setup_dir(cli1, BASEDIR),
49 : talloc_asprintf(tctx, "Unable to set up %s", BASEDIR));
50 :
51 4 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
52 4 : torture_assert(tctx, fnum1 != -1,
53 : talloc_asprintf(tctx,
54 : "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)));
55 4 : fnum2 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
56 4 : torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx,
57 : "open2 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)));
58 4 : fnum3 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
59 4 : torture_assert(tctx, fnum3 != -1, talloc_asprintf(tctx,
60 : "open3 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)));
61 :
62 4 : 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 4 : 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 4 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
71 4 : NT_STATUS_LOCK_NOT_GRANTED)) return false;
72 :
73 4 : 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 4 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
78 4 : NT_STATUS_FILE_LOCK_CONFLICT)) return false;
79 :
80 4 : 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 4 : 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 4 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
90 4 : NT_STATUS_LOCK_NOT_GRANTED)) return false;
91 :
92 4 : 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 4 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
97 4 : NT_STATUS_LOCK_NOT_GRANTED)) return false;
98 :
99 4 : 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 4 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
104 4 : NT_STATUS_FILE_LOCK_CONFLICT)) return false;
105 :
106 4 : lock_timeout = (6 + (random() % 20));
107 4 : torture_comment(tctx, "Testing lock timeout with timeout=%u\n",
108 : lock_timeout);
109 4 : t1 = time_mono(NULL);
110 4 : 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 4 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
115 4 : NT_STATUS_FILE_LOCK_CONFLICT)) return false;
116 4 : t2 = time_mono(NULL);
117 :
118 4 : if (t2 - t1 < 5) {
119 0 : torture_fail(tctx,
120 : "error: This server appears not to support timed lock requests");
121 : }
122 4 : torture_comment(tctx, "server slept for %u seconds for a %u second timeout\n",
123 : (unsigned int)(t2-t1), lock_timeout);
124 :
125 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum2),
126 : talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree)));
127 :
128 4 : 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 4 : if (!check_error(__location__, cli2, ERRDOS, ERRlock,
133 4 : NT_STATUS_FILE_LOCK_CONFLICT)) return false;
134 :
135 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
136 : talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli1->tree)));
137 :
138 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum3),
139 : talloc_asprintf(tctx, "close3 failed (%s)", smbcli_errstr(cli2->tree)));
140 :
141 4 : torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname),
142 : talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree)));
143 :
144 4 : 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 4 : static bool torture_locktest2(struct torture_context *tctx,
160 : struct smbcli_state *cli)
161 : {
162 4 : const char *fname = BASEDIR "\\lockt2.lck";
163 0 : int fnum1, fnum2, fnum3;
164 :
165 4 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR),
166 : talloc_asprintf(tctx, "Unable to set up %s", BASEDIR));
167 :
168 4 : torture_comment(tctx, "Testing pid context\n");
169 :
170 4 : cli->session->pid = 1;
171 :
172 4 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
173 4 : torture_assert(tctx, fnum1 != -1,
174 : talloc_asprintf(tctx,
175 : "open of %s failed (%s)", fname, smbcli_errstr(cli->tree)));
176 :
177 4 : fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
178 4 : torture_assert(tctx, fnum2 != -1,
179 : talloc_asprintf(tctx, "open2 of %s failed (%s)",
180 : fname, smbcli_errstr(cli->tree)));
181 :
182 4 : cli->session->pid = 2;
183 :
184 4 : fnum3 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
185 4 : torture_assert(tctx, fnum3 != -1,
186 : talloc_asprintf(tctx,
187 : "open3 of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)));
188 :
189 4 : cli->session->pid = 1;
190 :
191 4 : 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 4 : 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 4 : if (!check_error(__location__, cli, ERRDOS, ERRlock,
201 4 : NT_STATUS_LOCK_NOT_GRANTED)) return false;
202 :
203 4 : 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 4 : if (!check_error(__location__, cli, ERRDOS, ERRlock,
208 4 : NT_STATUS_LOCK_NOT_GRANTED)) return false;
209 :
210 4 : 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 4 : if (!check_error(__location__, cli, ERRDOS, ERRlock,
215 4 : NT_STATUS_FILE_LOCK_CONFLICT)) return false;
216 :
217 4 : 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 4 : cli->session->pid = 2;
223 :
224 4 : 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 4 : 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 4 : if (!check_error(__location__, cli,
233 : ERRDOS, ERRnotlocked,
234 4 : NT_STATUS_RANGE_NOT_LOCKED)) return false;
235 :
236 4 : 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 4 : if (!check_error(__location__, cli,
241 : ERRDOS, ERRnotlocked,
242 4 : NT_STATUS_RANGE_NOT_LOCKED)) return false;
243 :
244 4 : 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 4 : if (!check_error(__location__, cli, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false;
249 :
250 4 : cli->session->pid = 1;
251 :
252 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum1),
253 : talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli->tree)));
254 :
255 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum2),
256 : talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli->tree)));
257 :
258 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum3),
259 : talloc_asprintf(tctx, "close3 failed (%s)", smbcli_errstr(cli->tree)));
260 :
261 4 : 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 4 : static bool torture_locktest3(struct torture_context *tctx,
271 : struct smbcli_state *cli1,
272 : struct smbcli_state *cli2)
273 : {
274 4 : const char *fname = BASEDIR "\\lockt3.lck";
275 0 : int fnum1, fnum2, i;
276 0 : uint32_t offset;
277 0 : extern int torture_numops;
278 :
279 : #define NEXT_OFFSET offset += (~(uint32_t)0) / torture_numops
280 :
281 4 : torture_comment(tctx, "Testing 32 bit offset ranges");
282 :
283 4 : torture_assert(tctx, torture_setup_dir(cli1, BASEDIR),
284 : talloc_asprintf(tctx, "Unable to set up %s", BASEDIR));
285 :
286 4 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
287 4 : torture_assert(tctx, fnum1 != -1,
288 : talloc_asprintf(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree)));
289 4 : fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
290 4 : torture_assert(tctx, fnum2 != -1,
291 : talloc_asprintf(tctx, "open2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree)));
292 :
293 4 : torture_comment(tctx, "Establishing %d locks\n", torture_numops);
294 :
295 44 : for (offset=i=0;i<torture_numops;i++) {
296 40 : NEXT_OFFSET;
297 40 : 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 40 : 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 4 : torture_comment(tctx, "Testing %d locks\n", torture_numops);
308 :
309 44 : for (offset=i=0;i<torture_numops;i++) {
310 40 : NEXT_OFFSET;
311 :
312 40 : 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 40 : 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 40 : 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 40 : 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 4 : torture_comment(tctx, "Removing %d locks\n", torture_numops);
330 :
331 44 : for (offset=i=0;i<torture_numops;i++) {
332 40 : NEXT_OFFSET;
333 :
334 40 : 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 40 : 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 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
348 : talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree)));
349 :
350 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2),
351 : talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree)));
352 :
353 4 : torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname),
354 : talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree)));
355 :
356 4 : 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 4 : static bool torture_locktest4(struct torture_context *tctx,
367 : struct smbcli_state *cli1,
368 : struct smbcli_state *cli2)
369 : {
370 4 : const char *fname = BASEDIR "\\lockt4.lck";
371 0 : int fnum1, fnum2, f;
372 0 : bool ret;
373 0 : uint8_t buf[1000];
374 4 : bool correct = true;
375 :
376 4 : if (!torture_setup_dir(cli1, BASEDIR)) {
377 0 : return false;
378 : }
379 :
380 4 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
381 4 : fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
382 :
383 4 : memset(buf, 0, sizeof(buf));
384 :
385 4 : 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 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK)) &&
392 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 2, 4, 0, WRITE_LOCK));
393 4 : EXPECTED(ret, false);
394 4 : torture_comment(tctx, "the same process %s set overlapping write locks\n", ret?"can":"cannot");
395 :
396 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 10, 4, 0, READ_LOCK)) &&
397 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 12, 4, 0, READ_LOCK));
398 4 : EXPECTED(ret, true);
399 4 : torture_comment(tctx, "the same process %s set overlapping read locks\n", ret?"can":"cannot");
400 :
401 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 20, 4, 0, WRITE_LOCK)) &&
402 4 : NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 22, 4, 0, WRITE_LOCK));
403 4 : EXPECTED(ret, false);
404 4 : torture_comment(tctx, "a different connection %s set overlapping write locks\n", ret?"can":"cannot");
405 :
406 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 30, 4, 0, READ_LOCK)) &&
407 4 : NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 32, 4, 0, READ_LOCK));
408 4 : EXPECTED(ret, true);
409 4 : torture_comment(tctx, "a different connection %s set overlapping read locks\n", ret?"can":"cannot");
410 :
411 8 : ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 40, 4, 0, WRITE_LOCK))) &&
412 4 : NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 42, 4, 0, WRITE_LOCK)));
413 4 : EXPECTED(ret, false);
414 4 : torture_comment(tctx, "a different pid %s set overlapping write locks\n", ret?"can":"cannot");
415 :
416 8 : ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 50, 4, 0, READ_LOCK))) &&
417 4 : NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 52, 4, 0, READ_LOCK)));
418 4 : EXPECTED(ret, true);
419 4 : torture_comment(tctx, "a different pid %s set overlapping read locks\n", ret?"can":"cannot");
420 :
421 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 60, 4, 0, READ_LOCK)) &&
422 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 60, 4, 0, READ_LOCK));
423 4 : EXPECTED(ret, true);
424 4 : torture_comment(tctx, "the same process %s set the same read lock twice\n", ret?"can":"cannot");
425 :
426 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 70, 4, 0, WRITE_LOCK)) &&
427 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 70, 4, 0, WRITE_LOCK));
428 4 : EXPECTED(ret, false);
429 4 : torture_comment(tctx, "the same process %s set the same write lock twice\n", ret?"can":"cannot");
430 :
431 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 80, 4, 0, READ_LOCK)) &&
432 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 80, 4, 0, WRITE_LOCK));
433 4 : EXPECTED(ret, false);
434 4 : torture_comment(tctx, "the same process %s overlay a read lock with a write lock\n", ret?"can":"cannot");
435 :
436 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 90, 4, 0, WRITE_LOCK)) &&
437 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 90, 4, 0, READ_LOCK));
438 4 : EXPECTED(ret, true);
439 4 : torture_comment(tctx, "the same process %s overlay a write lock with a read lock\n", ret?"can":"cannot");
440 :
441 8 : ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 100, 4, 0, WRITE_LOCK))) &&
442 4 : NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 100, 4, 0, READ_LOCK)));
443 4 : EXPECTED(ret, false);
444 4 : torture_comment(tctx, "a different pid %s overlay a write lock with a read lock\n", ret?"can":"cannot");
445 :
446 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 110, 4, 0, READ_LOCK)) &&
447 8 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 112, 4, 0, READ_LOCK)) &&
448 4 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 110, 6));
449 4 : EXPECTED(ret, false);
450 4 : torture_comment(tctx, "the same process %s coalesce read locks\n", ret?"can":"cannot");
451 :
452 :
453 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 120, 4, 0, WRITE_LOCK)) &&
454 4 : (smbcli_read(cli2->tree, fnum2, buf, 120, 4) == 4);
455 4 : EXPECTED(ret, false);
456 4 : torture_comment(tctx, "this server %s strict write locking\n", ret?"doesn't do":"does");
457 :
458 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 130, 4, 0, READ_LOCK)) &&
459 4 : (smbcli_write(cli2->tree, fnum2, 0, buf, 130, 4) == 4);
460 4 : EXPECTED(ret, false);
461 4 : torture_comment(tctx, "this server %s strict read locking\n", ret?"doesn't do":"does");
462 :
463 :
464 4 : 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 12 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 140, 4)) &&
467 4 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 140, 4));
468 4 : EXPECTED(ret, true);
469 4 : torture_comment(tctx, "this server %s do recursive read locking\n", ret?"does":"doesn't");
470 :
471 :
472 4 : 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 8 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 150, 4)) &&
475 8 : (smbcli_read(cli2->tree, fnum2, buf, 150, 4) == 4) &&
476 12 : !(smbcli_write(cli2->tree, fnum2, 0, buf, 150, 4) == 4) &&
477 4 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 150, 4));
478 4 : EXPECTED(ret, true);
479 4 : torture_comment(tctx, "this server %s do recursive lock overlays\n", ret?"does":"doesn't");
480 :
481 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 160, 4, 0, READ_LOCK)) &&
482 8 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 160, 4)) &&
483 12 : (smbcli_write(cli2->tree, fnum2, 0, buf, 160, 4) == 4) &&
484 4 : (smbcli_read(cli2->tree, fnum2, buf, 160, 4) == 4);
485 4 : EXPECTED(ret, true);
486 4 : torture_comment(tctx, "the same process %s remove a read lock using write locking\n", ret?"can":"cannot");
487 :
488 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 170, 4, 0, WRITE_LOCK)) &&
489 8 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 170, 4)) &&
490 12 : (smbcli_write(cli2->tree, fnum2, 0, buf, 170, 4) == 4) &&
491 4 : (smbcli_read(cli2->tree, fnum2, buf, 170, 4) == 4);
492 4 : EXPECTED(ret, true);
493 4 : torture_comment(tctx, "the same process %s remove a write lock using read locking\n", ret?"can":"cannot");
494 :
495 4 : 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 8 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 190, 4)) &&
498 12 : !(smbcli_write(cli2->tree, fnum2, 0, buf, 190, 4) == 4) &&
499 4 : (smbcli_read(cli2->tree, fnum2, buf, 190, 4) == 4);
500 4 : EXPECTED(ret, true);
501 4 : torture_comment(tctx, "the same process %s remove the first lock first\n", ret?"does":"doesn't");
502 :
503 4 : smbcli_close(cli1->tree, fnum1);
504 4 : smbcli_close(cli2->tree, fnum2);
505 4 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
506 4 : f = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
507 4 : 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 8 : NT_STATUS_IS_OK(smbcli_close(cli1->tree, fnum1)) &&
510 12 : ((fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE)) != -1) &&
511 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 7, 1, 0, WRITE_LOCK));
512 4 : smbcli_close(cli1->tree, f);
513 4 : smbcli_close(cli1->tree, fnum1);
514 4 : EXPECTED(ret, true);
515 4 : torture_comment(tctx, "the server %s have the NT byte range lock bug\n", !ret?"does":"doesn't");
516 :
517 4 : fail:
518 4 : smbcli_close(cli1->tree, fnum1);
519 4 : smbcli_close(cli2->tree, fnum2);
520 4 : smbcli_unlink(cli1->tree, fname);
521 :
522 4 : return correct;
523 : }
524 :
525 : /*
526 : looks at lock upgrade/downgrade.
527 : */
528 4 : static bool torture_locktest5(struct torture_context *tctx, struct smbcli_state *cli1,
529 : struct smbcli_state *cli2)
530 : {
531 4 : const char *fname = BASEDIR "\\lockt5.lck";
532 0 : int fnum1, fnum2, fnum3;
533 0 : bool ret;
534 0 : uint8_t buf[1000];
535 4 : bool correct = true;
536 :
537 4 : if (!torture_setup_dir(cli1, BASEDIR)) {
538 0 : return false;
539 : }
540 :
541 4 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
542 4 : fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
543 4 : fnum3 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
544 :
545 4 : memset(buf, 0, sizeof(buf));
546 :
547 4 : 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 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 8, 0, READ_LOCK)) &&
552 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum3, 0, 1, 0, READ_LOCK));
553 4 : smbcli_close(cli1->tree, fnum1);
554 4 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
555 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 7, 1, 0, WRITE_LOCK));
556 4 : EXPECTED(ret, true);
557 4 : torture_comment(tctx, "this server %s the NT locking bug\n", ret ? "doesn't have" : "has");
558 4 : smbcli_close(cli1->tree, fnum1);
559 4 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE);
560 4 : smbcli_unlock(cli1->tree, fnum3, 0, 1);
561 :
562 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK)) &&
563 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 1, 1, 0, READ_LOCK));
564 4 : EXPECTED(ret, true);
565 4 : torture_comment(tctx, "the same process %s overlay a write with a read lock\n", ret?"can":"cannot");
566 :
567 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, READ_LOCK));
568 4 : EXPECTED(ret, false);
569 :
570 4 : 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 4 : smbcli_unlock(cli2->tree, fnum2, 0, 4);
574 :
575 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum3, 0, 4, 0, READ_LOCK));
576 4 : EXPECTED(ret, false);
577 :
578 4 : 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 4 : smbcli_unlock(cli1->tree, fnum3, 0, 4);
582 :
583 : /* Stack 2 more locks here. */
584 8 : ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, READ_LOCK)) &&
585 4 : NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, READ_LOCK));
586 :
587 4 : EXPECTED(ret, true);
588 4 : 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 8 : ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)) &&
594 4 : NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, READ_LOCK));
595 :
596 4 : EXPECTED(ret, true);
597 4 : torture_comment(tctx, "the first unlock removes the %s lock\n", ret?"WRITE":"READ");
598 :
599 : /* Unlock the process 2 lock. */
600 4 : 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 4 : ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 1, 1)) &&
605 8 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)) &&
606 4 : NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4));
607 :
608 4 : EXPECTED(ret, true);
609 4 : 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 4 : ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4));
613 4 : EXPECTED(ret, false);
614 4 : 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 4 : ret = NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, WRITE_LOCK));
618 4 : EXPECTED(ret, true);
619 :
620 4 : torture_comment(tctx, "a different process %s get a write lock on the unlocked stack\n", ret?"can":"cannot");
621 :
622 :
623 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1),
624 : talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree)));
625 :
626 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2),
627 : talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree)));
628 :
629 4 : torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum3),
630 : talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree)));
631 :
632 4 : torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname),
633 : talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree)));
634 :
635 4 : return correct;
636 : }
637 :
638 : /*
639 : tries the unusual lockingX locktype bits
640 : */
641 4 : static bool torture_locktest6(struct torture_context *tctx,
642 : struct smbcli_state *cli)
643 : {
644 4 : const char *fname[1] = { "\\lock6.txt" };
645 0 : int i;
646 0 : int fnum;
647 0 : NTSTATUS status;
648 :
649 4 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
650 :
651 8 : for (i=0;i<1;i++) {
652 4 : torture_comment(tctx, "Testing %s\n", fname[i]);
653 :
654 4 : smbcli_unlink(cli->tree, fname[i]);
655 :
656 4 : fnum = smbcli_open(cli->tree, fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
657 4 : status = smbcli_locktype(cli->tree, fnum, 0, 8, 0, LOCKING_ANDX_CHANGE_LOCKTYPE);
658 4 : smbcli_close(cli->tree, fnum);
659 4 : torture_comment(tctx, "CHANGE_LOCKTYPE gave %s\n", nt_errstr(status));
660 :
661 4 : fnum = smbcli_open(cli->tree, fname[i], O_RDWR, DENY_NONE);
662 4 : status = smbcli_locktype(cli->tree, fnum, 0, 8, 0, LOCKING_ANDX_CANCEL_LOCK);
663 4 : smbcli_close(cli->tree, fnum);
664 4 : torture_comment(tctx, "CANCEL_LOCK gave %s\n", nt_errstr(status));
665 :
666 4 : smbcli_unlink(cli->tree, fname[i]);
667 : }
668 :
669 4 : return true;
670 : }
671 :
672 4 : static bool torture_locktest7(struct torture_context *tctx,
673 : struct smbcli_state *cli1)
674 : {
675 4 : const char *fname = BASEDIR "\\lockt7.lck";
676 0 : int fnum1;
677 4 : int fnum2 = -1;
678 0 : size_t size;
679 0 : uint8_t buf[200];
680 4 : bool correct = false;
681 :
682 4 : torture_assert(tctx, torture_setup_dir(cli1, BASEDIR),
683 : talloc_asprintf(tctx, "Unable to set up %s", BASEDIR));
684 :
685 4 : fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
686 :
687 4 : memset(buf, 0, sizeof(buf));
688 :
689 4 : torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) == sizeof(buf),
690 : "Failed to create file");
691 :
692 4 : cli1->session->pid = 1;
693 :
694 4 : 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 4 : torture_comment(tctx, "pid1 successfully locked range 130:4 for READ\n");
699 :
700 4 : 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 4 : torture_comment(tctx, "pid1 successfully read the range 130:4\n");
705 :
706 4 : if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) {
707 4 : torture_comment(tctx, "pid1 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree));
708 4 : 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 4 : cli1->session->pid = 2;
715 :
716 4 : 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 4 : torture_comment(tctx, "pid2 successfully read the range 130:4\n");
720 : }
721 :
722 4 : if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) {
723 4 : torture_comment(tctx, "pid2 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree));
724 4 : 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 4 : cli1->session->pid = 1;
731 4 : smbcli_unlock(cli1->tree, fnum1, 130, 4);
732 :
733 4 : 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 4 : torture_comment(tctx, "pid1 successfully locked range 130:4 for WRITE\n");
737 :
738 4 : 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 4 : torture_comment(tctx, "pid1 successfully read the range 130:4\n");
742 :
743 4 : 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 4 : torture_comment(tctx, "pid1 successfully wrote to the range 130:4\n");
747 :
748 4 : cli1->session->pid = 2;
749 :
750 4 : if (smbcli_read(cli1->tree, fnum1, buf, 130, 4) != 4) {
751 4 : torture_comment(tctx, "pid2 unable to read the range 130:4, error was %s\n",
752 : smbcli_errstr(cli1->tree));
753 4 : 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 4 : if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) {
760 4 : torture_comment(tctx, "pid2 unable to write to the range 130:4, error was %s\n",
761 : smbcli_errstr(cli1->tree));
762 4 : 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 4 : torture_comment(tctx, "Testing truncate of locked file.\n");
774 :
775 4 : fnum2 = smbcli_open(cli1->tree, fname, O_RDWR|O_TRUNC, DENY_NONE);
776 :
777 4 : torture_assert(tctx, fnum2 != -1, "Unable to truncate locked file");
778 :
779 4 : torture_comment(tctx, "Truncated locked file.\n");
780 :
781 4 : 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 4 : torture_assert(tctx, size == 0, talloc_asprintf(tctx, "Unable to truncate locked file. Size was %u", (unsigned)size));
785 :
786 4 : cli1->session->pid = 1;
787 :
788 4 : smbcli_unlock(cli1->tree, fnum1, 130, 4);
789 4 : correct = true;
790 :
791 4 : fail:
792 4 : smbcli_close(cli1->tree, fnum1);
793 4 : smbcli_close(cli1->tree, fnum2);
794 4 : smbcli_unlink(cli1->tree, fname);
795 :
796 4 : return correct;
797 : }
798 :
799 2354 : struct torture_suite *torture_base_locktest(TALLOC_CTX *mem_ctx)
800 : {
801 2354 : struct torture_suite *suite = torture_suite_create(mem_ctx, "lock");
802 2354 : torture_suite_add_2smb_test(suite, "LOCK1", torture_locktest1);
803 2354 : torture_suite_add_1smb_test(suite, "LOCK2", torture_locktest2);
804 2354 : torture_suite_add_2smb_test(suite, "LOCK3", torture_locktest3);
805 2354 : torture_suite_add_2smb_test(suite, "LOCK4", torture_locktest4);
806 2354 : torture_suite_add_2smb_test(suite, "LOCK5", torture_locktest5);
807 2354 : torture_suite_add_1smb_test(suite, "LOCK6", torture_locktest6);
808 2354 : torture_suite_add_1smb_test(suite, "LOCK7", torture_locktest7);
809 :
810 2354 : return suite;
811 : }
|