2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2003
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.
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.
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/>.
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"
29 #define CHECK_STATUS(status, correct) \
30 torture_assert_ntstatus_equal_goto(tctx, status, correct, ret, done, __location__)
32 #define BASEDIR "\\testunlink"
37 static bool test_unlink(struct torture_context
*tctx
, struct smbcli_state
*cli
)
42 const char *fname
= BASEDIR
"\\test.txt";
44 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
46 printf("Trying non-existent file\n");
47 io
.unlink
.in
.pattern
= fname
;
48 io
.unlink
.in
.attrib
= 0;
49 status
= smb_raw_unlink(cli
->tree
, &io
);
50 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
52 smbcli_close(cli
->tree
, smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
));
54 io
.unlink
.in
.pattern
= fname
;
55 io
.unlink
.in
.attrib
= 0;
56 status
= smb_raw_unlink(cli
->tree
, &io
);
57 CHECK_STATUS(status
, NT_STATUS_OK
);
59 printf("Trying a hidden file\n");
60 smbcli_close(cli
->tree
, smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
));
61 torture_set_file_attribute(cli
->tree
, fname
, FILE_ATTRIBUTE_HIDDEN
);
63 io
.unlink
.in
.pattern
= fname
;
64 io
.unlink
.in
.attrib
= 0;
65 status
= smb_raw_unlink(cli
->tree
, &io
);
66 CHECK_STATUS(status
, NT_STATUS_NO_SUCH_FILE
);
68 io
.unlink
.in
.pattern
= fname
;
69 io
.unlink
.in
.attrib
= FILE_ATTRIBUTE_HIDDEN
;
70 status
= smb_raw_unlink(cli
->tree
, &io
);
71 CHECK_STATUS(status
, NT_STATUS_OK
);
73 io
.unlink
.in
.pattern
= fname
;
74 io
.unlink
.in
.attrib
= FILE_ATTRIBUTE_HIDDEN
;
75 status
= smb_raw_unlink(cli
->tree
, &io
);
76 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
78 printf("Trying a directory\n");
79 io
.unlink
.in
.pattern
= BASEDIR
;
80 io
.unlink
.in
.attrib
= 0;
81 status
= smb_raw_unlink(cli
->tree
, &io
);
82 CHECK_STATUS(status
, NT_STATUS_FILE_IS_A_DIRECTORY
);
84 io
.unlink
.in
.pattern
= BASEDIR
;
85 io
.unlink
.in
.attrib
= FILE_ATTRIBUTE_DIRECTORY
;
86 status
= smb_raw_unlink(cli
->tree
, &io
);
87 CHECK_STATUS(status
, NT_STATUS_FILE_IS_A_DIRECTORY
);
89 printf("Trying a bad path\n");
90 io
.unlink
.in
.pattern
= "..";
91 io
.unlink
.in
.attrib
= 0;
92 status
= smb_raw_unlink(cli
->tree
, &io
);
93 CHECK_STATUS(status
, NT_STATUS_OBJECT_PATH_SYNTAX_BAD
);
95 io
.unlink
.in
.pattern
= "\\..";
96 io
.unlink
.in
.attrib
= 0;
97 status
= smb_raw_unlink(cli
->tree
, &io
);
98 CHECK_STATUS(status
, NT_STATUS_OBJECT_PATH_SYNTAX_BAD
);
100 io
.unlink
.in
.pattern
= BASEDIR
"\\..\\..";
101 io
.unlink
.in
.attrib
= 0;
102 status
= smb_raw_unlink(cli
->tree
, &io
);
103 CHECK_STATUS(status
, NT_STATUS_OBJECT_PATH_SYNTAX_BAD
);
105 io
.unlink
.in
.pattern
= BASEDIR
"\\..";
106 io
.unlink
.in
.attrib
= 0;
107 status
= smb_raw_unlink(cli
->tree
, &io
);
108 CHECK_STATUS(status
, NT_STATUS_FILE_IS_A_DIRECTORY
);
111 smb_raw_exit(cli
->session
);
112 smbcli_deltree(cli
->tree
, BASEDIR
);
120 static bool test_delete_on_close(struct torture_context
*tctx
,
121 struct smbcli_state
*cli
)
125 struct smb_rmdir dio
;
129 const char *fname
= BASEDIR
"\\test.txt";
130 const char *dname
= BASEDIR
"\\test.dir";
131 const char *inside
= BASEDIR
"\\test.dir\\test.txt";
132 union smb_setfileinfo sfinfo
;
134 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
138 io
.unlink
.in
.pattern
= fname
;
139 io
.unlink
.in
.attrib
= 0;
140 status
= smb_raw_unlink(cli
->tree
, &io
);
141 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
143 printf("Testing with delete_on_close 0\n");
144 fnum
= create_complex_file(cli
, tctx
, fname
);
146 sfinfo
.disposition_info
.level
= RAW_SFILEINFO_DISPOSITION_INFO
;
147 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
148 sfinfo
.disposition_info
.in
.delete_on_close
= 0;
149 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
150 CHECK_STATUS(status
, NT_STATUS_OK
);
152 smbcli_close(cli
->tree
, fnum
);
154 status
= smb_raw_unlink(cli
->tree
, &io
);
155 CHECK_STATUS(status
, NT_STATUS_OK
);
157 printf("Testing with delete_on_close 1\n");
158 fnum
= create_complex_file(cli
, tctx
, fname
);
159 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
160 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
161 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
162 CHECK_STATUS(status
, NT_STATUS_OK
);
164 smbcli_close(cli
->tree
, fnum
);
166 status
= smb_raw_unlink(cli
->tree
, &io
);
167 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
170 printf("Testing with directory and delete_on_close 0\n");
171 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
172 CHECK_STATUS(status
, NT_STATUS_OK
);
174 sfinfo
.disposition_info
.level
= RAW_SFILEINFO_DISPOSITION_INFO
;
175 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
176 sfinfo
.disposition_info
.in
.delete_on_close
= 0;
177 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
178 CHECK_STATUS(status
, NT_STATUS_OK
);
180 smbcli_close(cli
->tree
, fnum
);
182 status
= smb_raw_rmdir(cli
->tree
, &dio
);
183 CHECK_STATUS(status
, NT_STATUS_OK
);
185 printf("Testing with directory delete_on_close 1\n");
186 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
187 CHECK_STATUS(status
, NT_STATUS_OK
);
189 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
190 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
191 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
192 CHECK_STATUS(status
, NT_STATUS_OK
);
194 smbcli_close(cli
->tree
, fnum
);
196 status
= smb_raw_rmdir(cli
->tree
, &dio
);
197 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
200 if (!torture_setting_bool(tctx
, "samba3", false)) {
203 * Known deficiency, also skipped in base-delete.
206 printf("Testing with non-empty directory delete_on_close\n");
207 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
208 CHECK_STATUS(status
, NT_STATUS_OK
);
210 fnum2
= create_complex_file(cli
, tctx
, inside
);
212 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
213 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
214 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
215 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
217 sfinfo
.disposition_info
.in
.file
.fnum
= fnum2
;
218 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
219 CHECK_STATUS(status
, NT_STATUS_OK
);
221 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
222 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
223 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
225 smbcli_close(cli
->tree
, fnum2
);
227 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
228 CHECK_STATUS(status
, NT_STATUS_OK
);
230 smbcli_close(cli
->tree
, fnum
);
232 status
= smb_raw_rmdir(cli
->tree
, &dio
);
233 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
236 printf("Testing open dir with delete_on_close\n");
237 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
238 CHECK_STATUS(status
, NT_STATUS_OK
);
240 smbcli_close(cli
->tree
, fnum
);
241 fnum2
= create_complex_file(cli
, tctx
, inside
);
242 smbcli_close(cli
->tree
, fnum2
);
244 op
.generic
.level
= RAW_OPEN_NTCREATEX
;
245 op
.ntcreatex
.in
.root_fid
.fnum
= 0;
246 op
.ntcreatex
.in
.flags
= 0;
247 op
.ntcreatex
.in
.access_mask
= SEC_RIGHTS_FILE_ALL
;
248 op
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
|NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
249 op
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
250 op
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
| NTCREATEX_SHARE_ACCESS_WRITE
;
251 op
.ntcreatex
.in
.alloc_size
= 0;
252 op
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
253 op
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
254 op
.ntcreatex
.in
.security_flags
= 0;
255 op
.ntcreatex
.in
.fname
= dname
;
257 status
= smb_raw_open(cli
->tree
, tctx
, &op
);
258 CHECK_STATUS(status
, NT_STATUS_OK
);
259 fnum
= op
.ntcreatex
.out
.file
.fnum
;
261 smbcli_close(cli
->tree
, fnum
);
263 status
= smb_raw_rmdir(cli
->tree
, &dio
);
264 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
266 smbcli_deltree(cli
->tree
, dname
);
268 printf("Testing double open dir with second delete_on_close\n");
269 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
270 CHECK_STATUS(status
, NT_STATUS_OK
);
271 smbcli_close(cli
->tree
, fnum
);
273 fnum2
= create_complex_file(cli
, tctx
, inside
);
274 smbcli_close(cli
->tree
, fnum2
);
276 op
.generic
.level
= RAW_OPEN_NTCREATEX
;
277 op
.ntcreatex
.in
.root_fid
.fnum
= 0;
278 op
.ntcreatex
.in
.flags
= 0;
279 op
.ntcreatex
.in
.access_mask
= SEC_RIGHTS_FILE_ALL
;
280 op
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
|NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
281 op
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
282 op
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
| NTCREATEX_SHARE_ACCESS_WRITE
;
283 op
.ntcreatex
.in
.alloc_size
= 0;
284 op
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
285 op
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
286 op
.ntcreatex
.in
.security_flags
= 0;
287 op
.ntcreatex
.in
.fname
= dname
;
289 status
= smb_raw_open(cli
->tree
, tctx
, &op
);
290 CHECK_STATUS(status
, NT_STATUS_OK
);
291 fnum2
= op
.ntcreatex
.out
.file
.fnum
;
293 smbcli_close(cli
->tree
, fnum2
);
295 status
= smb_raw_rmdir(cli
->tree
, &dio
);
296 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
298 smbcli_deltree(cli
->tree
, dname
);
300 printf("Testing pre-existing open dir with second delete_on_close\n");
301 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
302 CHECK_STATUS(status
, NT_STATUS_OK
);
304 smbcli_close(cli
->tree
, fnum
);
306 fnum
= create_complex_file(cli
, tctx
, inside
);
307 smbcli_close(cli
->tree
, fnum
);
309 /* we have a dir with a file in it, no handles open */
311 op
.generic
.level
= RAW_OPEN_NTCREATEX
;
312 op
.ntcreatex
.in
.root_fid
.fnum
= 0;
313 op
.ntcreatex
.in
.flags
= 0;
314 op
.ntcreatex
.in
.access_mask
= SEC_RIGHTS_FILE_ALL
;
315 op
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
|NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
316 op
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
317 op
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
| NTCREATEX_SHARE_ACCESS_WRITE
| NTCREATEX_SHARE_ACCESS_DELETE
;
318 op
.ntcreatex
.in
.alloc_size
= 0;
319 op
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
320 op
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
321 op
.ntcreatex
.in
.security_flags
= 0;
322 op
.ntcreatex
.in
.fname
= dname
;
324 status
= smb_raw_open(cli
->tree
, tctx
, &op
);
325 CHECK_STATUS(status
, NT_STATUS_OK
);
326 fnum
= op
.ntcreatex
.out
.file
.fnum
;
328 /* open without delete on close */
329 op
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
330 status
= smb_raw_open(cli
->tree
, tctx
, &op
);
331 CHECK_STATUS(status
, NT_STATUS_OK
);
332 fnum2
= op
.ntcreatex
.out
.file
.fnum
;
334 /* close 2nd file handle */
335 smbcli_close(cli
->tree
, fnum2
);
337 status
= smb_raw_rmdir(cli
->tree
, &dio
);
338 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
341 smbcli_close(cli
->tree
, fnum
);
343 status
= smb_raw_rmdir(cli
->tree
, &dio
);
344 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
347 smb_raw_exit(cli
->session
);
348 smbcli_deltree(cli
->tree
, BASEDIR
);
353 struct unlink_defer_cli_state
{
354 struct torture_context
*tctx
;
355 struct smbcli_state
*cli1
;
359 * A handler function for oplock break requests. Ack it as a break to none
361 static bool oplock_handler_ack_to_none(struct smbcli_transport
*transport
,
362 uint16_t tid
, uint16_t fnum
,
363 uint8_t level
, void *private_data
)
365 struct unlink_defer_cli_state
*ud_cli_state
=
366 (struct unlink_defer_cli_state
*)private_data
;
367 union smb_setfileinfo sfinfo
;
369 struct smbcli_request
*req
= NULL
;
371 torture_comment(ud_cli_state
->tctx
, "delete the file before sending "
374 /* cli1: set delete on close */
375 sfinfo
.disposition_info
.level
= RAW_SFILEINFO_DISPOSITION_INFO
;
376 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
377 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
378 req
= smb_raw_setfileinfo_send(ud_cli_state
->cli1
->tree
, &sfinfo
);
380 torture_comment(ud_cli_state
->tctx
, "smb_raw_setfileinfo_send "
384 smbcli_close(ud_cli_state
->cli1
->tree
, fnum
);
386 torture_comment(ud_cli_state
->tctx
, "Acking the oplock to NONE\n");
388 ret
= smbcli_oplock_ack(ud_cli_state
->cli1
->tree
, fnum
,
389 OPLOCK_BREAK_TO_NONE
);
394 static bool test_unlink_defer(struct torture_context
*tctx
,
395 struct smbcli_state
*cli1
,
396 struct smbcli_state
*cli2
)
398 const char *fname
= BASEDIR
"\\test_unlink_defer.dat";
402 union smb_unlink unl
;
403 struct unlink_defer_cli_state ud_cli_state
= {};
405 if (!torture_setup_dir(cli1
, BASEDIR
)) {
410 smbcli_unlink(cli1
->tree
, fname
);
412 ud_cli_state
.tctx
= tctx
;
413 ud_cli_state
.cli1
= cli1
;
415 smbcli_oplock_handler(cli1
->transport
, oplock_handler_ack_to_none
,
418 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
419 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
420 io
.ntcreatex
.in
.access_mask
= SEC_RIGHTS_FILE_ALL
;
421 io
.ntcreatex
.in
.alloc_size
= 0;
422 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
423 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
424 NTCREATEX_SHARE_ACCESS_WRITE
|
425 NTCREATEX_SHARE_ACCESS_DELETE
;
426 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
427 io
.ntcreatex
.in
.create_options
= 0;
428 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
429 io
.ntcreatex
.in
.security_flags
= 0;
430 io
.ntcreatex
.in
.fname
= fname
;
432 /* cli1: open file with a batch oplock. */
433 io
.ntcreatex
.in
.flags
= NTCREATEX_FLAGS_EXTENDED
|
434 NTCREATEX_FLAGS_REQUEST_OPLOCK
|
435 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK
;
437 status
= smb_raw_open(cli1
->tree
, tctx
, &io
);
438 CHECK_STATUS(status
, NT_STATUS_OK
);
440 /* cli2: Try to unlink it, but block on the oplock */
441 torture_comment(tctx
, "Try an unlink (should defer the open\n");
442 unl
.unlink
.in
.pattern
= fname
;
443 unl
.unlink
.in
.attrib
= 0;
444 status
= smb_raw_unlink(cli2
->tree
, &unl
);
447 smb_raw_exit(cli1
->session
);
448 smb_raw_exit(cli2
->session
);
449 smbcli_deltree(cli1
->tree
, BASEDIR
);
454 basic testing of unlink calls
456 struct torture_suite
*torture_raw_unlink(TALLOC_CTX
*mem_ctx
)
458 struct torture_suite
*suite
= torture_suite_create(mem_ctx
, "unlink");
460 torture_suite_add_1smb_test(suite
, "unlink", test_unlink
);
461 torture_suite_add_1smb_test(suite
, "delete_on_close", test_delete_on_close
);
462 torture_suite_add_2smb_test(suite
, "unlink-defer", test_unlink_defer
);