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