diff options
-rw-r--r-- | source4/torture/raw/streams.c | 247 |
1 files changed, 245 insertions, 2 deletions
diff --git a/source4/torture/raw/streams.c b/source4/torture/raw/streams.c index ac9d0d36aa..bbc0bcae82 100644 --- a/source4/torture/raw/streams.c +++ b/source4/torture/raw/streams.c @@ -172,7 +172,8 @@ static bool check_stream_list(struct smbcli_state *cli, const char *fname, /* test basic io on streams */ -static bool test_stream_io(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) +static bool test_stream_io(struct torture_context *tctx, + struct smbcli_state *cli, TALLOC_CTX *mem_ctx) { NTSTATUS status; union smb_open io; @@ -261,6 +262,10 @@ static bool test_stream_io(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$DATA", "test MORE DATA "); ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:", NULL); ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream", "SECOND STREAM"); + if (!torture_setting_bool(tctx, "samba4", false)) { + ret &= check_stream(cli, __location__, mem_ctx, fname, + "SECOND STREAM:$DATA", "SECOND STREAM"); + } ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$DATA", "SECOND STREAM"); ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:", NULL); ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$FOO", NULL); @@ -290,6 +295,18 @@ static bool test_stream_io(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) check_stream_list(cli, fname, 1, one); + if (!torture_setting_bool(tctx, "samba4", false)) { + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.fname = sname1; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + } + printf("(%s) deleting file\n", __location__); status = smbcli_unlink(cli->tree, fname); CHECK_STATUS(status, NT_STATUS_OK); @@ -299,6 +316,228 @@ done: return ret; } +/* + test stream sharemodes +*/ +static bool test_stream_sharemodes(struct torture_context *tctx, + struct smbcli_state *cli, + TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\stream.txt"; + const char *sname1, *sname2; + bool ret = true; + int fnum1 = -1; + int fnum2 = -1; + + sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One"); + sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream"); + + printf("(%s) testing stream share mode conflicts\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = sname1; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + + /* + * A different stream does not give a sharing violation + */ + + CHECK_STATUS(status, NT_STATUS_OK); + fnum1 = io.ntcreatex.out.file.fnum; + + /* + * ... whereas the same stream does with unchanged access/share_access + * flags + */ + + io.ntcreatex.in.open_disposition = 0; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + +done: + if (fnum1 != -1) smbcli_close(cli->tree, fnum1); + if (fnum2 != -1) smbcli_close(cli->tree, fnum2); + status = smbcli_unlink(cli->tree, fname); + return ret; +} + +/* + * Test FILE_SHARE_DELETE on streams + * + * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened + * with SEC_STD_DELETE. + * + * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to + * be opened with SEC_STD_DELETE. + * + * A stream held open with FILE_SHARE_DELETE allows the file to be + * deleted. After the main file is deleted, access to the open file descriptor + * still works, but all name-based access to both the main file as well as the + * stream is denied with DELETE ending. + * + * This means, an open of the main file with SEC_STD_DELETE should walk all + * streams and also open them with SEC_STD_DELETE. If any of these opens gives + * SHARING_VIOLATION, the main open fails. + * + * Closing the main file after delete_on_close has been set does not really + * unlink it but leaves the corresponding share mode entry with + * delete_on_close being set around until all streams are closed. + * + * Opening a stream must also look at the main file's share mode entry, look + * at the delete_on_close bit and potentially return DELETE_PENDING. + */ + +static bool test_stream_delete(struct torture_context *tctx, + struct smbcli_state *cli, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + union smb_open io; + const char *fname = BASEDIR "\\stream.txt"; + const char *sname1; + bool ret = true; + int fnum = -1; + uint8_t buf[9]; + ssize_t retsize; + union smb_fileinfo finfo; + + sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One"); + + printf("(%s) opening non-existant directory stream\n", __location__); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = sname1; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9); + CHECK_VALUE(retsize, 9); + + /* + * One stream opened without FILE_SHARE_DELETE prevents the main file + * to be deleted or even opened with DELETE access + */ + + status = smbcli_unlink(cli->tree, fname); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.fname = fname; + io.ntcreatex.in.access_mask = SEC_STD_DELETE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + smbcli_close(cli->tree, fnum); + + /* + * ... but unlink works if a stream is opened with FILE_SHARE_DELETE + */ + + io.ntcreatex.in.fname = sname1; + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + status = smbcli_unlink(cli->tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * file access still works on the stream while the main file is closed + */ + + retsize = smbcli_read(cli->tree, fnum, buf, 0, 9); + CHECK_VALUE(retsize, 9); + + finfo.generic.level = RAW_FILEINFO_STANDARD; + finfo.generic.in.file.path = fname; + + /* + * name-based access to both the main file and the stream does not + * work anymore but gives DELETE_PENDING + */ + + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_DELETE_PENDING); + + finfo.generic.in.file.path = sname1; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_DELETE_PENDING); + + /* + * fd-based qfileinfo on the stream still works, the stream does not + * have the delete-on-close bit set. This could mean that open on the + * stream first opens the main file + */ + + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; + finfo.all_info.in.file.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.all_info.out.delete_pending, 0); + + smbcli_close(cli->tree, fnum); + + /* + * After closing the stream the file is really gone. + */ + + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA + |SEC_STD_DELETE; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + + finfo.generic.in.file.path = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smbcli_close(cli->tree, fnum); + + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); +done: + smbcli_close(cli->tree, fnum); + return ret; +} + /* basic testing of streams calls */ @@ -311,7 +550,11 @@ bool torture_raw_streams(struct torture_context *torture, return false; } - ret &= test_stream_io(cli, torture); + ret &= test_stream_io(torture, cli, torture); + ret &= test_stream_sharemodes(torture, cli, torture); + if (!torture_setting_bool(torture, "samba4", false)) { + ret &= test_stream_delete(torture, cli, torture); + } smb_raw_exit(cli->session); smbcli_deltree(cli->tree, BASEDIR); |