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) 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)); \
37 #define BASEDIR "\\testunlink"
42 static bool test_unlink(struct torture_context
*tctx
, struct smbcli_state
*cli
)
47 const char *fname
= BASEDIR
"\\test.txt";
49 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
51 printf("Trying non-existent file\n");
52 io
.unlink
.in
.pattern
= fname
;
53 io
.unlink
.in
.attrib
= 0;
54 status
= smb_raw_unlink(cli
->tree
, &io
);
55 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
57 smbcli_close(cli
->tree
, smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
));
59 io
.unlink
.in
.pattern
= fname
;
60 io
.unlink
.in
.attrib
= 0;
61 status
= smb_raw_unlink(cli
->tree
, &io
);
62 CHECK_STATUS(status
, NT_STATUS_OK
);
64 printf("Trying a hidden file\n");
65 smbcli_close(cli
->tree
, smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
));
66 torture_set_file_attribute(cli
->tree
, fname
, FILE_ATTRIBUTE_HIDDEN
);
68 io
.unlink
.in
.pattern
= fname
;
69 io
.unlink
.in
.attrib
= 0;
70 status
= smb_raw_unlink(cli
->tree
, &io
);
71 CHECK_STATUS(status
, NT_STATUS_NO_SUCH_FILE
);
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_OK
);
78 io
.unlink
.in
.pattern
= fname
;
79 io
.unlink
.in
.attrib
= FILE_ATTRIBUTE_HIDDEN
;
80 status
= smb_raw_unlink(cli
->tree
, &io
);
81 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
83 printf("Trying a directory\n");
84 io
.unlink
.in
.pattern
= BASEDIR
;
85 io
.unlink
.in
.attrib
= 0;
86 status
= smb_raw_unlink(cli
->tree
, &io
);
87 CHECK_STATUS(status
, NT_STATUS_FILE_IS_A_DIRECTORY
);
89 io
.unlink
.in
.pattern
= BASEDIR
;
90 io
.unlink
.in
.attrib
= FILE_ATTRIBUTE_DIRECTORY
;
91 status
= smb_raw_unlink(cli
->tree
, &io
);
92 CHECK_STATUS(status
, NT_STATUS_FILE_IS_A_DIRECTORY
);
94 printf("Trying a bad path\n");
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
= "\\..";
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_OBJECT_PATH_SYNTAX_BAD
);
110 io
.unlink
.in
.pattern
= BASEDIR
"\\..";
111 io
.unlink
.in
.attrib
= 0;
112 status
= smb_raw_unlink(cli
->tree
, &io
);
113 CHECK_STATUS(status
, NT_STATUS_FILE_IS_A_DIRECTORY
);
116 smb_raw_exit(cli
->session
);
117 smbcli_deltree(cli
->tree
, BASEDIR
);
125 static bool test_delete_on_close(struct torture_context
*tctx
,
126 struct smbcli_state
*cli
)
130 struct smb_rmdir dio
;
134 const char *fname
= BASEDIR
"\\test.txt";
135 const char *dname
= BASEDIR
"\\test.dir";
136 const char *inside
= BASEDIR
"\\test.dir\\test.txt";
137 union smb_setfileinfo sfinfo
;
139 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
143 io
.unlink
.in
.pattern
= fname
;
144 io
.unlink
.in
.attrib
= 0;
145 status
= smb_raw_unlink(cli
->tree
, &io
);
146 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
148 printf("Testing with delete_on_close 0\n");
149 fnum
= create_complex_file(cli
, tctx
, fname
);
151 sfinfo
.disposition_info
.level
= RAW_SFILEINFO_DISPOSITION_INFO
;
152 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
153 sfinfo
.disposition_info
.in
.delete_on_close
= 0;
154 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
155 CHECK_STATUS(status
, NT_STATUS_OK
);
157 smbcli_close(cli
->tree
, fnum
);
159 status
= smb_raw_unlink(cli
->tree
, &io
);
160 CHECK_STATUS(status
, NT_STATUS_OK
);
162 printf("Testing with delete_on_close 1\n");
163 fnum
= create_complex_file(cli
, tctx
, fname
);
164 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
165 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
166 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
167 CHECK_STATUS(status
, NT_STATUS_OK
);
169 smbcli_close(cli
->tree
, fnum
);
171 status
= smb_raw_unlink(cli
->tree
, &io
);
172 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
175 printf("Testing with directory and delete_on_close 0\n");
176 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
177 CHECK_STATUS(status
, NT_STATUS_OK
);
179 sfinfo
.disposition_info
.level
= RAW_SFILEINFO_DISPOSITION_INFO
;
180 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
181 sfinfo
.disposition_info
.in
.delete_on_close
= 0;
182 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
183 CHECK_STATUS(status
, NT_STATUS_OK
);
185 smbcli_close(cli
->tree
, fnum
);
187 status
= smb_raw_rmdir(cli
->tree
, &dio
);
188 CHECK_STATUS(status
, NT_STATUS_OK
);
190 printf("Testing with directory delete_on_close 1\n");
191 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
192 CHECK_STATUS(status
, NT_STATUS_OK
);
194 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
195 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
196 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
197 CHECK_STATUS(status
, NT_STATUS_OK
);
199 smbcli_close(cli
->tree
, fnum
);
201 status
= smb_raw_rmdir(cli
->tree
, &dio
);
202 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
205 if (!torture_setting_bool(tctx
, "samba3", false)) {
208 * Known deficiency, also skipped in base-delete.
211 printf("Testing with non-empty directory delete_on_close\n");
212 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
213 CHECK_STATUS(status
, NT_STATUS_OK
);
215 fnum2
= create_complex_file(cli
, tctx
, inside
);
217 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
218 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
219 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
220 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
222 sfinfo
.disposition_info
.in
.file
.fnum
= fnum2
;
223 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
224 CHECK_STATUS(status
, NT_STATUS_OK
);
226 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
227 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
228 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
230 smbcli_close(cli
->tree
, fnum2
);
232 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
233 CHECK_STATUS(status
, NT_STATUS_OK
);
235 smbcli_close(cli
->tree
, fnum
);
237 status
= smb_raw_rmdir(cli
->tree
, &dio
);
238 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
241 printf("Testing open dir with delete_on_close\n");
242 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
243 CHECK_STATUS(status
, NT_STATUS_OK
);
245 smbcli_close(cli
->tree
, fnum
);
246 fnum2
= create_complex_file(cli
, tctx
, inside
);
247 smbcli_close(cli
->tree
, fnum2
);
249 op
.generic
.level
= RAW_OPEN_NTCREATEX
;
250 op
.ntcreatex
.in
.root_fid
.fnum
= 0;
251 op
.ntcreatex
.in
.flags
= 0;
252 op
.ntcreatex
.in
.access_mask
= SEC_RIGHTS_FILE_ALL
;
253 op
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
|NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
254 op
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
255 op
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
| NTCREATEX_SHARE_ACCESS_WRITE
;
256 op
.ntcreatex
.in
.alloc_size
= 0;
257 op
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
258 op
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
259 op
.ntcreatex
.in
.security_flags
= 0;
260 op
.ntcreatex
.in
.fname
= dname
;
262 status
= smb_raw_open(cli
->tree
, tctx
, &op
);
263 CHECK_STATUS(status
, NT_STATUS_OK
);
264 fnum
= op
.ntcreatex
.out
.file
.fnum
;
266 smbcli_close(cli
->tree
, fnum
);
268 status
= smb_raw_rmdir(cli
->tree
, &dio
);
269 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
271 smbcli_deltree(cli
->tree
, dname
);
273 printf("Testing double open dir with second delete_on_close\n");
274 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
275 CHECK_STATUS(status
, NT_STATUS_OK
);
276 smbcli_close(cli
->tree
, fnum
);
278 fnum2
= create_complex_file(cli
, tctx
, inside
);
279 smbcli_close(cli
->tree
, fnum2
);
281 op
.generic
.level
= RAW_OPEN_NTCREATEX
;
282 op
.ntcreatex
.in
.root_fid
.fnum
= 0;
283 op
.ntcreatex
.in
.flags
= 0;
284 op
.ntcreatex
.in
.access_mask
= SEC_RIGHTS_FILE_ALL
;
285 op
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
|NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
286 op
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
287 op
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
| NTCREATEX_SHARE_ACCESS_WRITE
;
288 op
.ntcreatex
.in
.alloc_size
= 0;
289 op
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
290 op
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
291 op
.ntcreatex
.in
.security_flags
= 0;
292 op
.ntcreatex
.in
.fname
= dname
;
294 status
= smb_raw_open(cli
->tree
, tctx
, &op
);
295 CHECK_STATUS(status
, NT_STATUS_OK
);
296 fnum2
= op
.ntcreatex
.out
.file
.fnum
;
298 smbcli_close(cli
->tree
, fnum2
);
300 status
= smb_raw_rmdir(cli
->tree
, &dio
);
301 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
303 smbcli_deltree(cli
->tree
, dname
);
305 printf("Testing pre-existing open dir with second delete_on_close\n");
306 status
= create_directory_handle(cli
->tree
, dname
, &fnum
);
307 CHECK_STATUS(status
, NT_STATUS_OK
);
309 smbcli_close(cli
->tree
, fnum
);
311 fnum
= create_complex_file(cli
, tctx
, inside
);
312 smbcli_close(cli
->tree
, fnum
);
314 /* we have a dir with a file in it, no handles open */
316 op
.generic
.level
= RAW_OPEN_NTCREATEX
;
317 op
.ntcreatex
.in
.root_fid
.fnum
= 0;
318 op
.ntcreatex
.in
.flags
= 0;
319 op
.ntcreatex
.in
.access_mask
= SEC_RIGHTS_FILE_ALL
;
320 op
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
|NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
321 op
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
322 op
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
| NTCREATEX_SHARE_ACCESS_WRITE
| NTCREATEX_SHARE_ACCESS_DELETE
;
323 op
.ntcreatex
.in
.alloc_size
= 0;
324 op
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
325 op
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
326 op
.ntcreatex
.in
.security_flags
= 0;
327 op
.ntcreatex
.in
.fname
= dname
;
329 status
= smb_raw_open(cli
->tree
, tctx
, &op
);
330 CHECK_STATUS(status
, NT_STATUS_OK
);
331 fnum
= op
.ntcreatex
.out
.file
.fnum
;
333 /* open without delete on close */
334 op
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
335 status
= smb_raw_open(cli
->tree
, tctx
, &op
);
336 CHECK_STATUS(status
, NT_STATUS_OK
);
337 fnum2
= op
.ntcreatex
.out
.file
.fnum
;
339 /* close 2nd file handle */
340 smbcli_close(cli
->tree
, fnum2
);
342 status
= smb_raw_rmdir(cli
->tree
, &dio
);
343 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
346 smbcli_close(cli
->tree
, fnum
);
348 status
= smb_raw_rmdir(cli
->tree
, &dio
);
349 CHECK_STATUS(status
, NT_STATUS_DIRECTORY_NOT_EMPTY
);
352 smb_raw_exit(cli
->session
);
353 smbcli_deltree(cli
->tree
, BASEDIR
);
358 struct unlink_defer_cli_state
{
359 struct torture_context
*tctx
;
360 struct smbcli_state
*cli1
;
364 * A handler function for oplock break requests. Ack it as a break to none
366 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
)
370 struct unlink_defer_cli_state
*ud_cli_state
=
371 (struct unlink_defer_cli_state
*)private_data
;
372 union smb_setfileinfo sfinfo
;
374 struct smbcli_request
*req
= NULL
;
376 torture_comment(ud_cli_state
->tctx
, "delete the file before sending "
379 /* cli1: set delete on close */
380 sfinfo
.disposition_info
.level
= RAW_SFILEINFO_DISPOSITION_INFO
;
381 sfinfo
.disposition_info
.in
.file
.fnum
= fnum
;
382 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
383 req
= smb_raw_setfileinfo_send(ud_cli_state
->cli1
->tree
, &sfinfo
);
385 torture_comment(ud_cli_state
->tctx
, "smb_raw_setfileinfo_send "
389 smbcli_close(ud_cli_state
->cli1
->tree
, fnum
);
391 torture_comment(ud_cli_state
->tctx
, "Acking the oplock to NONE\n");
393 ret
= smbcli_oplock_ack(ud_cli_state
->cli1
->tree
, fnum
,
394 OPLOCK_BREAK_TO_NONE
);
399 static bool test_unlink_defer(struct torture_context
*tctx
,
400 struct smbcli_state
*cli1
,
401 struct smbcli_state
*cli2
)
403 const char *fname
= BASEDIR
"\\test_unlink_defer.dat";
407 union smb_unlink unl
;
408 struct unlink_defer_cli_state ud_cli_state
= {};
410 if (!torture_setup_dir(cli1
, BASEDIR
)) {
415 smbcli_unlink(cli1
->tree
, fname
);
417 ud_cli_state
.tctx
= tctx
;
418 ud_cli_state
.cli1
= cli1
;
420 smbcli_oplock_handler(cli1
->transport
, oplock_handler_ack_to_none
,
423 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
424 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
425 io
.ntcreatex
.in
.access_mask
= SEC_RIGHTS_FILE_ALL
;
426 io
.ntcreatex
.in
.alloc_size
= 0;
427 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
428 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
429 NTCREATEX_SHARE_ACCESS_WRITE
|
430 NTCREATEX_SHARE_ACCESS_DELETE
;
431 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
432 io
.ntcreatex
.in
.create_options
= 0;
433 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
434 io
.ntcreatex
.in
.security_flags
= 0;
435 io
.ntcreatex
.in
.fname
= fname
;
437 /* cli1: open file with a batch oplock. */
438 io
.ntcreatex
.in
.flags
= NTCREATEX_FLAGS_EXTENDED
|
439 NTCREATEX_FLAGS_REQUEST_OPLOCK
|
440 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK
;
442 status
= smb_raw_open(cli1
->tree
, tctx
, &io
);
443 CHECK_STATUS(status
, NT_STATUS_OK
);
445 /* cli2: Try to unlink it, but block on the oplock */
446 torture_comment(tctx
, "Try an unlink (should defer the open\n");
447 unl
.unlink
.in
.pattern
= fname
;
448 unl
.unlink
.in
.attrib
= 0;
449 status
= smb_raw_unlink(cli2
->tree
, &unl
);
452 smb_raw_exit(cli1
->session
);
453 smb_raw_exit(cli2
->session
);
454 smbcli_deltree(cli1
->tree
, BASEDIR
);
459 basic testing of unlink calls
461 struct torture_suite
*torture_raw_unlink(TALLOC_CTX
*mem_ctx
)
463 struct torture_suite
*suite
= torture_suite_create(mem_ctx
, "unlink");
465 torture_suite_add_1smb_test(suite
, "unlink", test_unlink
);
466 torture_suite_add_1smb_test(suite
, "delete_on_close", test_delete_on_close
);
467 torture_suite_add_2smb_test(suite
, "unlink-defer", test_unlink_defer
);