Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : unlink test suite
4 : Copyright (C) Andrew Tridgell 2003
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 "torture/torture.h"
22 : #include "system/filesys.h"
23 : #include "libcli/raw/libcliraw.h"
24 : #include "libcli/raw/raw_proto.h"
25 : #include "libcli/libcli.h"
26 : #include "torture/util.h"
27 : #include "torture/raw/proto.h"
28 :
29 : #define CHECK_STATUS(status, correct) do { \
30 : if (!NT_STATUS_EQUAL(status, correct)) { \
31 : printf("(%s) Incorrect status %s - should be %s\n", \
32 : __location__, nt_errstr(status), nt_errstr(correct)); \
33 : ret = false; \
34 : goto done; \
35 : }} while (0)
36 :
37 : #define BASEDIR "\\testunlink"
38 :
39 : /*
40 : test unlink ops
41 : */
42 5 : static bool test_unlink(struct torture_context *tctx, struct smbcli_state *cli)
43 : {
44 0 : union smb_unlink io;
45 0 : NTSTATUS status;
46 5 : bool ret = true;
47 5 : const char *fname = BASEDIR "\\test.txt";
48 :
49 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
50 :
51 5 : printf("Trying non-existent file\n");
52 5 : io.unlink.in.pattern = fname;
53 5 : io.unlink.in.attrib = 0;
54 5 : status = smb_raw_unlink(cli->tree, &io);
55 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
56 :
57 5 : smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
58 :
59 5 : io.unlink.in.pattern = fname;
60 5 : io.unlink.in.attrib = 0;
61 5 : status = smb_raw_unlink(cli->tree, &io);
62 5 : CHECK_STATUS(status, NT_STATUS_OK);
63 :
64 5 : printf("Trying a hidden file\n");
65 5 : smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
66 5 : torture_set_file_attribute(cli->tree, fname, FILE_ATTRIBUTE_HIDDEN);
67 :
68 5 : io.unlink.in.pattern = fname;
69 5 : io.unlink.in.attrib = 0;
70 5 : status = smb_raw_unlink(cli->tree, &io);
71 5 : CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
72 :
73 5 : io.unlink.in.pattern = fname;
74 5 : io.unlink.in.attrib = FILE_ATTRIBUTE_HIDDEN;
75 5 : status = smb_raw_unlink(cli->tree, &io);
76 5 : CHECK_STATUS(status, NT_STATUS_OK);
77 :
78 5 : io.unlink.in.pattern = fname;
79 5 : io.unlink.in.attrib = FILE_ATTRIBUTE_HIDDEN;
80 5 : status = smb_raw_unlink(cli->tree, &io);
81 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
82 :
83 5 : printf("Trying a directory\n");
84 5 : io.unlink.in.pattern = BASEDIR;
85 5 : io.unlink.in.attrib = 0;
86 5 : status = smb_raw_unlink(cli->tree, &io);
87 5 : CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
88 :
89 5 : io.unlink.in.pattern = BASEDIR;
90 5 : io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
91 5 : status = smb_raw_unlink(cli->tree, &io);
92 5 : CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
93 :
94 5 : printf("Trying a bad path\n");
95 5 : io.unlink.in.pattern = "..";
96 5 : io.unlink.in.attrib = 0;
97 5 : status = smb_raw_unlink(cli->tree, &io);
98 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
99 :
100 5 : io.unlink.in.pattern = "\\..";
101 5 : io.unlink.in.attrib = 0;
102 5 : status = smb_raw_unlink(cli->tree, &io);
103 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
104 :
105 5 : io.unlink.in.pattern = BASEDIR "\\..\\..";
106 5 : io.unlink.in.attrib = 0;
107 5 : status = smb_raw_unlink(cli->tree, &io);
108 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
109 :
110 5 : io.unlink.in.pattern = BASEDIR "\\..";
111 5 : io.unlink.in.attrib = 0;
112 5 : status = smb_raw_unlink(cli->tree, &io);
113 5 : CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
114 :
115 5 : done:
116 5 : smb_raw_exit(cli->session);
117 5 : smbcli_deltree(cli->tree, BASEDIR);
118 5 : return ret;
119 : }
120 :
121 :
122 : /*
123 : test delete on close
124 : */
125 5 : static bool test_delete_on_close(struct torture_context *tctx,
126 : struct smbcli_state *cli)
127 : {
128 0 : union smb_open op;
129 0 : union smb_unlink io;
130 0 : struct smb_rmdir dio;
131 0 : NTSTATUS status;
132 5 : bool ret = true;
133 0 : int fnum, fnum2;
134 5 : const char *fname = BASEDIR "\\test.txt";
135 5 : const char *dname = BASEDIR "\\test.dir";
136 5 : const char *inside = BASEDIR "\\test.dir\\test.txt";
137 0 : union smb_setfileinfo sfinfo;
138 :
139 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
140 :
141 5 : dio.in.path = dname;
142 :
143 5 : io.unlink.in.pattern = fname;
144 5 : io.unlink.in.attrib = 0;
145 5 : status = smb_raw_unlink(cli->tree, &io);
146 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
147 :
148 5 : printf("Testing with delete_on_close 0\n");
149 5 : fnum = create_complex_file(cli, tctx, fname);
150 :
151 5 : sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
152 5 : sfinfo.disposition_info.in.file.fnum = fnum;
153 5 : sfinfo.disposition_info.in.delete_on_close = 0;
154 5 : status = smb_raw_setfileinfo(cli->tree, &sfinfo);
155 5 : CHECK_STATUS(status, NT_STATUS_OK);
156 :
157 5 : smbcli_close(cli->tree, fnum);
158 :
159 5 : status = smb_raw_unlink(cli->tree, &io);
160 5 : CHECK_STATUS(status, NT_STATUS_OK);
161 :
162 5 : printf("Testing with delete_on_close 1\n");
163 5 : fnum = create_complex_file(cli, tctx, fname);
164 5 : sfinfo.disposition_info.in.file.fnum = fnum;
165 5 : sfinfo.disposition_info.in.delete_on_close = 1;
166 5 : status = smb_raw_setfileinfo(cli->tree, &sfinfo);
167 5 : CHECK_STATUS(status, NT_STATUS_OK);
168 :
169 5 : smbcli_close(cli->tree, fnum);
170 :
171 5 : status = smb_raw_unlink(cli->tree, &io);
172 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
173 :
174 :
175 5 : printf("Testing with directory and delete_on_close 0\n");
176 5 : status = create_directory_handle(cli->tree, dname, &fnum);
177 5 : CHECK_STATUS(status, NT_STATUS_OK);
178 :
179 5 : sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
180 5 : sfinfo.disposition_info.in.file.fnum = fnum;
181 5 : sfinfo.disposition_info.in.delete_on_close = 0;
182 5 : status = smb_raw_setfileinfo(cli->tree, &sfinfo);
183 5 : CHECK_STATUS(status, NT_STATUS_OK);
184 :
185 5 : smbcli_close(cli->tree, fnum);
186 :
187 5 : status = smb_raw_rmdir(cli->tree, &dio);
188 5 : CHECK_STATUS(status, NT_STATUS_OK);
189 :
190 5 : printf("Testing with directory delete_on_close 1\n");
191 5 : status = create_directory_handle(cli->tree, dname, &fnum);
192 5 : CHECK_STATUS(status, NT_STATUS_OK);
193 :
194 5 : sfinfo.disposition_info.in.file.fnum = fnum;
195 5 : sfinfo.disposition_info.in.delete_on_close = 1;
196 5 : status = smb_raw_setfileinfo(cli->tree, &sfinfo);
197 5 : CHECK_STATUS(status, NT_STATUS_OK);
198 :
199 5 : smbcli_close(cli->tree, fnum);
200 :
201 5 : status = smb_raw_rmdir(cli->tree, &dio);
202 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
203 :
204 :
205 5 : if (!torture_setting_bool(tctx, "samba3", false)) {
206 :
207 : /*
208 : * Known deficiency, also skipped in base-delete.
209 : */
210 :
211 1 : printf("Testing with non-empty directory delete_on_close\n");
212 1 : status = create_directory_handle(cli->tree, dname, &fnum);
213 1 : CHECK_STATUS(status, NT_STATUS_OK);
214 :
215 1 : fnum2 = create_complex_file(cli, tctx, inside);
216 :
217 1 : sfinfo.disposition_info.in.file.fnum = fnum;
218 1 : sfinfo.disposition_info.in.delete_on_close = 1;
219 1 : status = smb_raw_setfileinfo(cli->tree, &sfinfo);
220 1 : CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
221 :
222 1 : sfinfo.disposition_info.in.file.fnum = fnum2;
223 1 : status = smb_raw_setfileinfo(cli->tree, &sfinfo);
224 1 : CHECK_STATUS(status, NT_STATUS_OK);
225 :
226 1 : sfinfo.disposition_info.in.file.fnum = fnum;
227 1 : status = smb_raw_setfileinfo(cli->tree, &sfinfo);
228 1 : CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
229 :
230 1 : smbcli_close(cli->tree, fnum2);
231 :
232 1 : status = smb_raw_setfileinfo(cli->tree, &sfinfo);
233 1 : CHECK_STATUS(status, NT_STATUS_OK);
234 :
235 1 : smbcli_close(cli->tree, fnum);
236 :
237 1 : status = smb_raw_rmdir(cli->tree, &dio);
238 1 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
239 : }
240 :
241 5 : printf("Testing open dir with delete_on_close\n");
242 5 : status = create_directory_handle(cli->tree, dname, &fnum);
243 5 : CHECK_STATUS(status, NT_STATUS_OK);
244 :
245 5 : smbcli_close(cli->tree, fnum);
246 5 : fnum2 = create_complex_file(cli, tctx, inside);
247 5 : smbcli_close(cli->tree, fnum2);
248 :
249 5 : op.generic.level = RAW_OPEN_NTCREATEX;
250 5 : op.ntcreatex.in.root_fid.fnum = 0;
251 5 : op.ntcreatex.in.flags = 0;
252 5 : op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
253 5 : op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
254 5 : op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
255 5 : op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
256 5 : op.ntcreatex.in.alloc_size = 0;
257 5 : op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
258 5 : op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
259 5 : op.ntcreatex.in.security_flags = 0;
260 5 : op.ntcreatex.in.fname = dname;
261 :
262 5 : status = smb_raw_open(cli->tree, tctx, &op);
263 5 : CHECK_STATUS(status, NT_STATUS_OK);
264 5 : fnum = op.ntcreatex.out.file.fnum;
265 :
266 5 : smbcli_close(cli->tree, fnum);
267 :
268 5 : status = smb_raw_rmdir(cli->tree, &dio);
269 5 : CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
270 :
271 5 : smbcli_deltree(cli->tree, dname);
272 :
273 5 : printf("Testing double open dir with second delete_on_close\n");
274 5 : status = create_directory_handle(cli->tree, dname, &fnum);
275 5 : CHECK_STATUS(status, NT_STATUS_OK);
276 5 : smbcli_close(cli->tree, fnum);
277 :
278 5 : fnum2 = create_complex_file(cli, tctx, inside);
279 5 : smbcli_close(cli->tree, fnum2);
280 :
281 5 : op.generic.level = RAW_OPEN_NTCREATEX;
282 5 : op.ntcreatex.in.root_fid.fnum = 0;
283 5 : op.ntcreatex.in.flags = 0;
284 5 : op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
285 5 : op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
286 5 : op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
287 5 : op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
288 5 : op.ntcreatex.in.alloc_size = 0;
289 5 : op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
290 5 : op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
291 5 : op.ntcreatex.in.security_flags = 0;
292 5 : op.ntcreatex.in.fname = dname;
293 :
294 5 : status = smb_raw_open(cli->tree, tctx, &op);
295 5 : CHECK_STATUS(status, NT_STATUS_OK);
296 5 : fnum2 = op.ntcreatex.out.file.fnum;
297 :
298 5 : smbcli_close(cli->tree, fnum2);
299 :
300 5 : status = smb_raw_rmdir(cli->tree, &dio);
301 5 : CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
302 :
303 5 : smbcli_deltree(cli->tree, dname);
304 :
305 5 : printf("Testing pre-existing open dir with second delete_on_close\n");
306 5 : status = create_directory_handle(cli->tree, dname, &fnum);
307 5 : CHECK_STATUS(status, NT_STATUS_OK);
308 :
309 5 : smbcli_close(cli->tree, fnum);
310 :
311 5 : fnum = create_complex_file(cli, tctx, inside);
312 5 : smbcli_close(cli->tree, fnum);
313 :
314 : /* we have a dir with a file in it, no handles open */
315 :
316 5 : op.generic.level = RAW_OPEN_NTCREATEX;
317 5 : op.ntcreatex.in.root_fid.fnum = 0;
318 5 : op.ntcreatex.in.flags = 0;
319 5 : op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
320 5 : op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
321 5 : op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
322 5 : op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
323 5 : op.ntcreatex.in.alloc_size = 0;
324 5 : op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
325 5 : op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
326 5 : op.ntcreatex.in.security_flags = 0;
327 5 : op.ntcreatex.in.fname = dname;
328 :
329 5 : status = smb_raw_open(cli->tree, tctx, &op);
330 5 : CHECK_STATUS(status, NT_STATUS_OK);
331 5 : fnum = op.ntcreatex.out.file.fnum;
332 :
333 : /* open without delete on close */
334 5 : op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
335 5 : status = smb_raw_open(cli->tree, tctx, &op);
336 5 : CHECK_STATUS(status, NT_STATUS_OK);
337 5 : fnum2 = op.ntcreatex.out.file.fnum;
338 :
339 : /* close 2nd file handle */
340 5 : smbcli_close(cli->tree, fnum2);
341 :
342 5 : status = smb_raw_rmdir(cli->tree, &dio);
343 5 : CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
344 :
345 :
346 5 : smbcli_close(cli->tree, fnum);
347 :
348 5 : status = smb_raw_rmdir(cli->tree, &dio);
349 5 : CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
350 :
351 5 : done:
352 5 : smb_raw_exit(cli->session);
353 5 : smbcli_deltree(cli->tree, BASEDIR);
354 5 : return ret;
355 : }
356 :
357 :
358 : struct unlink_defer_cli_state {
359 : struct torture_context *tctx;
360 : struct smbcli_state *cli1;
361 : };
362 :
363 : /*
364 : * A handler function for oplock break requests. Ack it as a break to none
365 : */
366 5 : static bool oplock_handler_ack_to_none(struct smbcli_transport *transport,
367 : uint16_t tid, uint16_t fnum,
368 : uint8_t level, void *private_data)
369 : {
370 5 : struct unlink_defer_cli_state *ud_cli_state =
371 : (struct unlink_defer_cli_state *)private_data;
372 0 : union smb_setfileinfo sfinfo;
373 0 : bool ret;
374 5 : struct smbcli_request *req = NULL;
375 :
376 5 : torture_comment(ud_cli_state->tctx, "delete the file before sending "
377 : "the ack.");
378 :
379 : /* cli1: set delete on close */
380 5 : sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
381 5 : sfinfo.disposition_info.in.file.fnum = fnum;
382 5 : sfinfo.disposition_info.in.delete_on_close = 1;
383 5 : req = smb_raw_setfileinfo_send(ud_cli_state->cli1->tree, &sfinfo);
384 5 : if (!req) {
385 0 : torture_comment(ud_cli_state->tctx, "smb_raw_setfileinfo_send "
386 : "failed.");
387 : }
388 :
389 5 : smbcli_close(ud_cli_state->cli1->tree, fnum);
390 :
391 5 : torture_comment(ud_cli_state->tctx, "Acking the oplock to NONE\n");
392 :
393 5 : ret = smbcli_oplock_ack(ud_cli_state->cli1->tree, fnum,
394 : OPLOCK_BREAK_TO_NONE);
395 :
396 5 : return ret;
397 : }
398 :
399 5 : static bool test_unlink_defer(struct torture_context *tctx,
400 : struct smbcli_state *cli1,
401 : struct smbcli_state *cli2)
402 : {
403 5 : const char *fname = BASEDIR "\\test_unlink_defer.dat";
404 0 : NTSTATUS status;
405 5 : bool ret = true;
406 0 : union smb_open io;
407 0 : union smb_unlink unl;
408 5 : struct unlink_defer_cli_state ud_cli_state = {};
409 :
410 5 : if (!torture_setup_dir(cli1, BASEDIR)) {
411 0 : return false;
412 : }
413 :
414 : /* cleanup */
415 5 : smbcli_unlink(cli1->tree, fname);
416 :
417 5 : ud_cli_state.tctx = tctx;
418 5 : ud_cli_state.cli1 = cli1;
419 :
420 5 : smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_none,
421 : &ud_cli_state);
422 :
423 5 : io.generic.level = RAW_OPEN_NTCREATEX;
424 5 : io.ntcreatex.in.root_fid.fnum = 0;
425 5 : io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
426 5 : io.ntcreatex.in.alloc_size = 0;
427 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
428 5 : io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
429 : NTCREATEX_SHARE_ACCESS_WRITE |
430 : NTCREATEX_SHARE_ACCESS_DELETE;
431 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
432 5 : io.ntcreatex.in.create_options = 0;
433 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
434 5 : io.ntcreatex.in.security_flags = 0;
435 5 : io.ntcreatex.in.fname = fname;
436 :
437 : /* cli1: open file with a batch oplock. */
438 5 : io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
439 : NTCREATEX_FLAGS_REQUEST_OPLOCK |
440 : NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
441 :
442 5 : status = smb_raw_open(cli1->tree, tctx, &io);
443 5 : CHECK_STATUS(status, NT_STATUS_OK);
444 :
445 : /* cli2: Try to unlink it, but block on the oplock */
446 5 : torture_comment(tctx, "Try an unlink (should defer the open\n");
447 5 : unl.unlink.in.pattern = fname;
448 5 : unl.unlink.in.attrib = 0;
449 5 : status = smb_raw_unlink(cli2->tree, &unl);
450 :
451 5 : done:
452 5 : smb_raw_exit(cli1->session);
453 5 : smb_raw_exit(cli2->session);
454 5 : smbcli_deltree(cli1->tree, BASEDIR);
455 5 : return ret;
456 : }
457 :
458 : /*
459 : basic testing of unlink calls
460 : */
461 2354 : struct torture_suite *torture_raw_unlink(TALLOC_CTX *mem_ctx)
462 : {
463 2354 : struct torture_suite *suite = torture_suite_create(mem_ctx, "unlink");
464 :
465 2354 : torture_suite_add_1smb_test(suite, "unlink", test_unlink);
466 2354 : torture_suite_add_1smb_test(suite, "delete_on_close", test_delete_on_close);
467 2354 : torture_suite_add_2smb_test(suite, "unlink-defer", test_unlink_defer);
468 :
469 2354 : return suite;
470 : }
|