diff options
Diffstat (limited to 'source4/torture/raw/qfileinfo.c')
-rw-r--r-- | source4/torture/raw/qfileinfo.c | 1084 |
1 files changed, 1084 insertions, 0 deletions
diff --git a/source4/torture/raw/qfileinfo.c b/source4/torture/raw/qfileinfo.c new file mode 100644 index 0000000..1d29281 --- /dev/null +++ b/source4/torture/raw/qfileinfo.c @@ -0,0 +1,1084 @@ +/* + Unix SMB/CIFS implementation. + RAW_FILEINFO_* individual test suite + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" +#include "torture/util.h" +#include "torture/rpc/torture_rpc.h" +#include "param/param.h" +#include "torture/raw/proto.h" + +static struct { + const char *name; + enum smb_fileinfo_level level; + unsigned int only_paths:1; + unsigned int only_handles:1; + uint32_t capability_mask; + unsigned int expected_ipc_access_denied:1; + NTSTATUS expected_ipc_fnum_status; + NTSTATUS fnum_status, fname_status; + union smb_fileinfo fnum_finfo, fname_finfo; +} levels[] = { + { + .name = "GETATTR", + .level = RAW_FILEINFO_GETATTR, + .only_paths = 1, + .only_handles = 0, + .expected_ipc_access_denied = 1, + }, + { + .name ="GETATTRE", + .level = RAW_FILEINFO_GETATTRE, + .only_paths = 0, + .only_handles = 1, + }, + { + .name ="STANDARD", + .level = RAW_FILEINFO_STANDARD, + }, + { + .name ="EA_SIZE", + .level = RAW_FILEINFO_EA_SIZE, + }, + { + .name ="ALL_EAS", + .level = RAW_FILEINFO_ALL_EAS, + .expected_ipc_fnum_status = NT_STATUS_ACCESS_DENIED, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="IS_NAME_VALID", + .level = RAW_FILEINFO_IS_NAME_VALID, + .only_paths = 1, + .only_handles = 0, + }, + { + .name ="BASIC_INFO", + .level = RAW_FILEINFO_BASIC_INFO, + }, + { + .name ="STANDARD_INFO", + .level = RAW_FILEINFO_STANDARD_INFO, + }, + { + .name ="EA_INFO", + .level = RAW_FILEINFO_EA_INFO, + }, + { + .name ="NAME_INFO", + .level = RAW_FILEINFO_NAME_INFO, + }, + { + .name ="ALL_INFO", + .level = RAW_FILEINFO_ALL_INFO, + }, + { + .name ="ALT_NAME_INFO", + .level = RAW_FILEINFO_ALT_NAME_INFO, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="STREAM_INFO", + .level = RAW_FILEINFO_STREAM_INFO, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="COMPRESSION_INFO", + .level = RAW_FILEINFO_COMPRESSION_INFO, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="UNIX_BASIC_INFO", + .level = RAW_FILEINFO_UNIX_BASIC, + .only_paths = 0, + .only_handles = 0, + .capability_mask = CAP_UNIX, + }, + { + .name ="UNIX_LINK_INFO", + .level = RAW_FILEINFO_UNIX_LINK, + .only_paths = 0, + .only_handles = 0, + .capability_mask = CAP_UNIX, + }, + { + .name ="BASIC_INFORMATION", + .level = RAW_FILEINFO_BASIC_INFORMATION, + }, + { + .name ="STANDARD_INFORMATION", + .level = RAW_FILEINFO_STANDARD_INFORMATION, + }, + { + .name ="INTERNAL_INFORMATION", + .level = RAW_FILEINFO_INTERNAL_INFORMATION, + }, + { + .name ="EA_INFORMATION", + .level = RAW_FILEINFO_EA_INFORMATION, + }, + { + .name = "ACCESS_INFORMATION", + .level = RAW_FILEINFO_ACCESS_INFORMATION, + }, + { + .name = "NAME_INFORMATION", + .level = RAW_FILEINFO_NAME_INFORMATION, + }, + { + .name ="POSITION_INFORMATION", + .level = RAW_FILEINFO_POSITION_INFORMATION, + }, + { + .name ="MODE_INFORMATION", + .level = RAW_FILEINFO_MODE_INFORMATION, + }, + { + .name ="ALIGNMENT_INFORMATION", + .level = RAW_FILEINFO_ALIGNMENT_INFORMATION, + }, + { + .name ="ALL_INFORMATION", + .level = RAW_FILEINFO_ALL_INFORMATION, + }, + { + .name ="ALT_NAME_INFORMATION", + .level = RAW_FILEINFO_ALT_NAME_INFORMATION, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="STREAM_INFORMATION", + .level = RAW_FILEINFO_STREAM_INFORMATION, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name = "COMPRESSION_INFORMATION", + .level = RAW_FILEINFO_COMPRESSION_INFORMATION, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name ="NETWORK_OPEN_INFORMATION", + .level = RAW_FILEINFO_NETWORK_OPEN_INFORMATION, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { + .name = "ATTRIBUTE_TAG_INFORMATION", + .level = RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, + .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER, + .fnum_status = NT_STATUS_SUCCESS, + .fname_status = NT_STATUS_SUCCESS, + .fnum_finfo = { + .generic = { + .level = 0, + }, + }, + .fname_finfo = { + .generic = { + .level = 0, + }, + }, + }, + { .name = NULL, }, +}; + +/* + compare a dos time (2 second resolution) to a nt time +*/ +static int dos_nt_time_cmp(time_t t, NTTIME nt) +{ + time_t t2 = nt_time_to_unix(nt); + if (labs(t2 - t) <= 2) return 0; + return t2 > t ? 1 : -1; +} + + +/* + find a level in the levels[] table +*/ +static union smb_fileinfo *fnum_find(const char *name) +{ + int i; + for (i=0; levels[i].name; i++) { + if (NT_STATUS_IS_OK(levels[i].fnum_status) && + strcmp(name, levels[i].name) == 0 && + !levels[i].only_paths) { + return &levels[i].fnum_finfo; + } + } + return NULL; +} + +/* + find a level in the levels[] table +*/ +static union smb_fileinfo *fname_find(bool is_ipc, const char *name) +{ + int i; + if (is_ipc) { + return NULL; + } + for (i=0; levels[i].name; i++) { + if (NT_STATUS_IS_OK(levels[i].fname_status) && + strcmp(name, levels[i].name) == 0 && + !levels[i].only_handles) { + return &levels[i].fname_finfo; + } + } + return NULL; +} + +/* local macros to make the code below more readable */ +#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \ + printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \ + #n1, #v1, (unsigned int)s1->n1.out.v1, \ + #n2, #v2, (unsigned int)s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +#define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp_safe(s1->n1.out.v1.s, s2->n2.out.v2.s) || \ + s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \ + printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \ + #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \ + #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \ + printf("%s/%s != %s/%s at %s(%d)\n", \ + #n1, #v1, \ + #n2, #v2, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) + +/* used to find hints on unknown values - and to make sure + we zero-fill */ +#if 0 /* unused */ +#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \ + printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \ + #n1, #v1, \ + (unsigned int)s1->n1.out.v1, \ + (unsigned int)s1->n1.out.v1, \ + __FILE__, __LINE__); \ + ret = false; \ +}} while(0) +#endif + +/* basic testing of all RAW_FILEINFO_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +static bool torture_raw_qfileinfo_internals(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct smbcli_tree *tree, + int fnum, const char *fname, + bool is_ipc) +{ + size_t i; + bool ret = true; + size_t count; + union smb_fileinfo *s1, *s2; + NTTIME correct_time; + uint64_t correct_size; + uint32_t correct_attrib; + const char *correct_name; + bool skip_streams = false; + + /* scan all the fileinfo and pathinfo levels */ + for (i=0; levels[i].name; i++) { + if (!levels[i].only_paths) { + levels[i].fnum_finfo.generic.level = levels[i].level; + levels[i].fnum_finfo.generic.in.file.fnum = fnum; + levels[i].fnum_status = smb_raw_fileinfo(tree, mem_ctx, + &levels[i].fnum_finfo); + } + + if (!levels[i].only_handles) { + levels[i].fname_finfo.generic.level = levels[i].level; + levels[i].fname_finfo.generic.in.file.path = talloc_strdup(mem_ctx, fname); + levels[i].fname_status = smb_raw_pathinfo(tree, mem_ctx, + &levels[i].fname_finfo); + } + } + + /* check for completely broken levels */ + for (count=i=0; levels[i].name; i++) { + uint32_t cap = tree->session->transport->negotiate.capabilities; + /* see if this server claims to support this level */ + if ((cap & levels[i].capability_mask) != levels[i].capability_mask) { + continue; + } + + if (is_ipc) { + if (levels[i].expected_ipc_access_denied && NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, levels[i].fname_status)) { + } else if (!levels[i].only_handles && + NT_STATUS_EQUAL(levels[i].fname_status, + NT_STATUS_NOT_SUPPORTED)) { + torture_warning(torture, "fname level %s %s", + levels[i].name, + nt_errstr(levels[i].fname_status)); + continue; + } else if (!levels[i].only_handles && !NT_STATUS_EQUAL(NT_STATUS_INVALID_DEVICE_REQUEST, levels[i].fname_status)) { + printf("ERROR: fname level %s failed, expected NT_STATUS_INVALID_DEVICE_REQUEST - %s\n", + levels[i].name, nt_errstr(levels[i].fname_status)); + count++; + } + if (!levels[i].only_paths && + (NT_STATUS_EQUAL(levels[i].fnum_status, + NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(levels[i].fnum_status, + NT_STATUS_NOT_IMPLEMENTED))) { + torture_warning(torture, "fnum level %s %s", + levels[i].name, + nt_errstr(levels[i].fnum_status)); + continue; + } + if (!levels[i].only_paths && !NT_STATUS_EQUAL(levels[i].expected_ipc_fnum_status, levels[i].fnum_status)) { + printf("ERROR: fnum level %s failed, expected %s - %s\n", + levels[i].name, nt_errstr(levels[i].expected_ipc_fnum_status), + nt_errstr(levels[i].fnum_status)); + count++; + } + } else { + if (!levels[i].only_paths && + (NT_STATUS_EQUAL(levels[i].fnum_status, + NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(levels[i].fnum_status, + NT_STATUS_NOT_IMPLEMENTED))) { + torture_warning(torture, "fnum level %s %s", + levels[i].name, + nt_errstr(levels[i].fnum_status)); + continue; + } + + if (!levels[i].only_handles && + (NT_STATUS_EQUAL(levels[i].fname_status, + NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(levels[i].fname_status, + NT_STATUS_NOT_IMPLEMENTED))) { + torture_warning(torture, "fname level %s %s", + levels[i].name, + nt_errstr(levels[i].fname_status)); + continue; + } + + if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) { + printf("ERROR: fnum level %s failed - %s\n", + levels[i].name, nt_errstr(levels[i].fnum_status)); + count++; + } + if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) { + printf("ERROR: fname level %s failed - %s\n", + levels[i].name, nt_errstr(levels[i].fname_status)); + count++; + } + } + + } + + if (count != 0) { + ret = false; + printf("%zu levels failed\n", count); + if (count > 35) { + torture_fail(torture, "too many level failures - giving up"); + } + } + + /* see if we can do streams */ + s1 = fnum_find("STREAM_INFO"); + if (!s1 || s1->stream_info.out.num_streams == 0) { + if (!is_ipc) { + printf("STREAM_INFO broken (%d) - skipping streams checks\n", + s1 ? s1->stream_info.out.num_streams : -1); + } + skip_streams = true; + } + + + /* this code is incredibly repititive but doesn't lend itself to loops, so + we use lots of macros to make it less painful */ + + /* first off we check the levels that are supposed to be aliases. It will be quite rare for + this code to fail, but we need to check it for completeness */ + + + +#define ALIAS_CHECK(sname1, sname2) \ + do { \ + s1 = fnum_find(sname1); s2 = fnum_find(sname2); \ + if (s1 && s2) { INFO_CHECK } \ + s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \ + if (s1 && s2) { INFO_CHECK } \ + s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \ + if (s1 && s2) { INFO_CHECK } \ + } while (0) + +#define INFO_CHECK \ + STRUCT_EQUAL(basic_info, create_time, basic_info, create_time); \ + STRUCT_EQUAL(basic_info, access_time, basic_info, access_time); \ + STRUCT_EQUAL(basic_info, write_time, basic_info, write_time); \ + STRUCT_EQUAL(basic_info, change_time, basic_info, change_time); \ + VAL_EQUAL (basic_info, attrib, basic_info, attrib); + + ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(standard_info, alloc_size, standard_info, alloc_size); \ + VAL_EQUAL(standard_info, size, standard_info, size); \ + VAL_EQUAL(standard_info, nlink, standard_info, nlink); \ + VAL_EQUAL(standard_info, delete_pending, standard_info, delete_pending); \ + VAL_EQUAL(standard_info, directory, standard_info, directory); + + ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(ea_info, ea_size, ea_info, ea_size); + + ALIAS_CHECK("EA_INFO", "EA_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + STR_EQUAL(name_info, fname, name_info, fname); + + ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + STRUCT_EQUAL(all_info, create_time, all_info, create_time); \ + STRUCT_EQUAL(all_info, access_time, all_info, access_time); \ + STRUCT_EQUAL(all_info, write_time, all_info, write_time); \ + STRUCT_EQUAL(all_info, change_time, all_info, change_time); \ + VAL_EQUAL(all_info, attrib, all_info, attrib); \ + VAL_EQUAL(all_info, alloc_size, all_info, alloc_size); \ + VAL_EQUAL(all_info, size, all_info, size); \ + VAL_EQUAL(all_info, nlink, all_info, nlink); \ + VAL_EQUAL(all_info, delete_pending, all_info, delete_pending); \ + VAL_EQUAL(all_info, directory, all_info, directory); \ + VAL_EQUAL(all_info, ea_size, all_info, ea_size); \ + STR_EQUAL(all_info, fname, all_info, fname); + + ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \ + VAL_EQUAL(compression_info, format, compression_info, format); \ + VAL_EQUAL(compression_info, unit_shift, compression_info, unit_shift); \ + VAL_EQUAL(compression_info, chunk_shift, compression_info, chunk_shift); \ + VAL_EQUAL(compression_info, cluster_shift, compression_info, cluster_shift); + + ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION"); + + +#undef INFO_CHECK +#define INFO_CHECK \ + STR_EQUAL(alt_name_info, fname, alt_name_info, fname); + + ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION"); + + +#define TIME_CHECK_NT(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + nt_time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + nt_time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + }} while (0) + +#define TIME_CHECK_DOS(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + timestring(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + timestring(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + }} while (0) + +#if 0 /* unused */ +#define TIME_CHECK_UNX(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + timestring(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + timestring(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, correct_time)); \ + ret = false; \ + }} while (0) +#endif + + /* now check that all the times that are supposed to be equal are correct */ + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.create_time; + torture_comment(torture, "create_time: %s\n", nt_time_string(mem_ctx, correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, create_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, create_time); + TIME_CHECK_DOS("GETATTRE", getattre, create_time); + TIME_CHECK_DOS("STANDARD", standard, create_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, create_time); + TIME_CHECK_NT ("ALL_INFO", all_info, create_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.access_time; + torture_comment(torture, "access_time: %s\n", nt_time_string(mem_ctx, correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, access_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, access_time); + TIME_CHECK_DOS("GETATTRE", getattre, access_time); + TIME_CHECK_DOS("STANDARD", standard, access_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, access_time); + TIME_CHECK_NT ("ALL_INFO", all_info, access_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.write_time; + torture_comment(torture, "write_time : %s\n", nt_time_string(mem_ctx, correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, write_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, write_time); + TIME_CHECK_DOS("GETATTR", getattr, write_time); + TIME_CHECK_DOS("GETATTRE", getattre, write_time); + TIME_CHECK_DOS("STANDARD", standard, write_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, write_time); + TIME_CHECK_NT ("ALL_INFO", all_info, write_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.change_time; + torture_comment(torture, "change_time: %s\n", nt_time_string(mem_ctx, correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, change_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, change_time); + TIME_CHECK_NT ("ALL_INFO", all_info, change_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time); + + +#define SIZE_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_size) { \ + printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield, \ + (unsigned int)correct_size); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && s1->stype.out.tfield != correct_size) { \ + printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield, \ + (unsigned int)correct_size); \ + ret = false; \ + }} while (0) + + s1 = fnum_find("STANDARD_INFO"); + correct_size = s1->standard_info.out.size; + torture_comment(torture, "size: %u\n", (unsigned int)correct_size); + + SIZE_CHECK("GETATTR", getattr, size); + SIZE_CHECK("GETATTRE", getattre, size); + SIZE_CHECK("STANDARD", standard, size); + SIZE_CHECK("EA_SIZE", ea_size, size); + SIZE_CHECK("STANDARD_INFO", standard_info, size); + SIZE_CHECK("STANDARD_INFORMATION", standard_info, size); + SIZE_CHECK("ALL_INFO", all_info, size); + SIZE_CHECK("ALL_INFORMATION", all_info, size); + SIZE_CHECK("COMPRESSION_INFO", compression_info, compressed_size); + SIZE_CHECK("COMPRESSION_INFORMATION", compression_info, compressed_size); + SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size); + if (!skip_streams) { + SIZE_CHECK("STREAM_INFO", stream_info, streams[0].size); + SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].size); + } + + + s1 = fnum_find("STANDARD_INFO"); + correct_size = s1->standard_info.out.alloc_size; + torture_comment(torture, "alloc_size: %u\n", (unsigned int)correct_size); + + SIZE_CHECK("GETATTRE", getattre, alloc_size); + SIZE_CHECK("STANDARD", standard, alloc_size); + SIZE_CHECK("EA_SIZE", ea_size, alloc_size); + SIZE_CHECK("STANDARD_INFO", standard_info, alloc_size); + SIZE_CHECK("STANDARD_INFORMATION", standard_info, alloc_size); + SIZE_CHECK("ALL_INFO", all_info, alloc_size); + SIZE_CHECK("ALL_INFORMATION", all_info, alloc_size); + SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size); + if (!skip_streams) { + SIZE_CHECK("STREAM_INFO", stream_info, streams[0].alloc_size); + SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].alloc_size); + } + +#define ATTRIB_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_attrib) { \ + printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield, \ + (unsigned int)correct_attrib); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && s1->stype.out.tfield != correct_attrib) { \ + printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield, \ + (unsigned int)correct_attrib); \ + ret = false; \ + }} while (0) + + s1 = fnum_find("BASIC_INFO"); + correct_attrib = s1->basic_info.out.attrib; + torture_comment(torture, "attrib: 0x%x\n", (unsigned int)correct_attrib); + + ATTRIB_CHECK("GETATTR", getattr, attrib); + if (!is_ipc) { + ATTRIB_CHECK("GETATTRE", getattre, attrib); + ATTRIB_CHECK("STANDARD", standard, attrib); + ATTRIB_CHECK("EA_SIZE", ea_size, attrib); + } + ATTRIB_CHECK("BASIC_INFO", basic_info, attrib); + ATTRIB_CHECK("BASIC_INFORMATION", basic_info, attrib); + ATTRIB_CHECK("ALL_INFO", all_info, attrib); + ATTRIB_CHECK("ALL_INFORMATION", all_info, attrib); + ATTRIB_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, attrib); + ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib); + + correct_name = fname; + torture_comment(torture, "name: %s\n", correct_name); + +#define NAME_CHECK(sname, stype, tfield, flags) do { \ + s1 = fnum_find(sname); \ + if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \ + wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \ + printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \ + s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \ + wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \ + printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \ + s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \ + ret = false; \ + }} while (0) + + NAME_CHECK("NAME_INFO", name_info, fname, STR_UNICODE); + NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE); + + /* the ALL_INFO file name is the full path on the filesystem */ + s1 = fnum_find("ALL_INFO"); + if (s1 && !s1->all_info.out.fname.s) { + torture_fail(torture, "ALL_INFO didn't give a filename"); + } + if (s1 && s1->all_info.out.fname.s) { + char *p = strrchr(s1->all_info.out.fname.s, '\\'); + if (!p) { + printf("Not a full path in all_info/fname? - '%s'\n", + s1->all_info.out.fname.s); + ret = false; + } else { + if (strcmp_safe(correct_name, p) != 0) { + printf("incorrect basename in all_info/fname - '%s'\n", + s1->all_info.out.fname.s); + ret = false; + } + } + if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE, tree->session->transport)) { + printf("Should not null terminate all_info/fname\n"); + ret = false; + } + } + + s1 = fnum_find("ALT_NAME_INFO"); + if (s1) { + correct_name = s1->alt_name_info.out.fname.s; + } + + if (!correct_name) { + torture_comment(torture, "no alternate name information\n"); + } else { + torture_comment(torture, "alt_name: %s\n", correct_name); + + NAME_CHECK("ALT_NAME_INFO", alt_name_info, fname, STR_UNICODE); + NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE); + + /* and make sure we can open by alternate name */ + smbcli_close(tree, fnum); + fnum = smbcli_nt_create_full(tree, correct_name, 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + if (fnum == -1) { + printf("Unable to open by alt_name - %s\n", smbcli_errstr(tree)); + ret = false; + } + + if (!skip_streams) { + correct_name = "::$DATA"; + torture_comment(torture, "stream_name: %s\n", correct_name); + + NAME_CHECK("STREAM_INFO", stream_info, streams[0].stream_name, STR_UNICODE); + NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE); + } + } + + /* make sure the EAs look right */ + s1 = fnum_find("ALL_EAS"); + s2 = fnum_find("ALL_INFO"); + if (s1) { + for (i=0;i<s1->all_eas.out.num_eas;i++) { + printf(" flags=%d %s=%*.*s\n", + s1->all_eas.out.eas[i].flags, + s1->all_eas.out.eas[i].name.s, + (int)s1->all_eas.out.eas[i].value.length, + (int)s1->all_eas.out.eas[i].value.length, + s1->all_eas.out.eas[i].value.data); + } + } + if (s1 && s2) { + if (s1->all_eas.out.num_eas == 0) { + if (s2->all_info.out.ea_size != 0) { + printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n", + s2->all_info.out.ea_size); + } + } else { + if (s2->all_info.out.ea_size != + ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas)) { + printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n", + (int)ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas), + (int)s2->all_info.out.ea_size); + } + } + } + s2 = fname_find(is_ipc, "ALL_EAS"); + if (s2) { + VAL_EQUAL(all_eas, num_eas, all_eas, num_eas); + for (i=0;i<s1->all_eas.out.num_eas;i++) { + VAL_EQUAL(all_eas, eas[i].flags, all_eas, eas[i].flags); + STR_EQUAL(all_eas, eas[i].name, all_eas, eas[i].name); + VAL_EQUAL(all_eas, eas[i].value.length, all_eas, eas[i].value.length); + } + } + +#define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \ + s1 = fnum_find(sname1); s2 = fnum_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = false; \ + } \ + s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = false; \ + } \ + s1 = fname_find(is_ipc, sname1); s2 = fnum_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = false; \ + }} while (0) + + VAL_CHECK("STANDARD_INFO", standard_info, delete_pending, + "ALL_INFO", all_info, delete_pending); + VAL_CHECK("STANDARD_INFO", standard_info, directory, + "ALL_INFO", all_info, directory); + VAL_CHECK("STANDARD_INFO", standard_info, nlink, + "ALL_INFO", all_info, nlink); + s1 = fnum_find("BASIC_INFO"); + if (s1 && is_ipc) { + if (s1->basic_info.out.attrib != FILE_ATTRIBUTE_NORMAL) { + printf("(%d) attrib basic_info/nlink incorrect - %d should be %d\n", __LINE__, s1->basic_info.out.attrib, (int)FILE_ATTRIBUTE_NORMAL); + ret = false; + } + } + s1 = fnum_find("STANDARD_INFO"); + if (s1 && is_ipc) { + if (s1->standard_info.out.nlink != 1) { + printf("(%d) nlinks standard_info/nlink incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.nlink); + ret = false; + } + if (s1->standard_info.out.delete_pending != 1) { + printf("(%d) nlinks standard_info/delete_pending incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.delete_pending); + ret = false; + } + } + VAL_CHECK("EA_INFO", ea_info, ea_size, + "ALL_INFO", all_info, ea_size); + if (!is_ipc) { + VAL_CHECK("EA_SIZE", ea_size, ea_size, + "ALL_INFO", all_info, ea_size); + } + +#define NAME_PATH_CHECK(sname, stype, field) do { \ + s1 = fname_find(is_ipc, sname); s2 = fnum_find(sname); \ + if (s1 && s2) { \ + VAL_EQUAL(stype, field, stype, field); \ + } \ +} while (0) + + + s1 = fnum_find("INTERNAL_INFORMATION"); + if (s1) { + torture_comment(torture, "file_id=%.0f\n", (double)s1->internal_information.out.file_id); + } + + NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, file_id); + NAME_PATH_CHECK("POSITION_INFORMATION", position_information, position); + if (s1 && s2) { + printf("fnum pos = %.0f, fname pos = %.0f\n", + (double)s2->position_information.out.position, + (double)s1->position_information.out.position ); + } + NAME_PATH_CHECK("MODE_INFORMATION", mode_information, mode); + NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information, alignment_requirement); + NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib); + NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, reparse_tag); + +#if 0 + /* these are expected to differ */ + NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags); +#endif + +#if 0 /* unused */ +#define UNKNOWN_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != 0) { \ + printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \ + #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield); \ + } \ + s1 = fname_find(is_ipc, sname); \ + if (s1 && s1->stype.out.tfield != 0) { \ + printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \ + #stype, #tfield, \ + (unsigned int)s1->stype.out.tfield); \ + }} while (0) +#endif + /* now get a bit fancier .... */ + + /* when we set the delete disposition then the link count should drop + to 0 and delete_pending should be 1 */ + + return ret; +} + +/* basic testing of all RAW_FILEINFO_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +bool torture_raw_qfileinfo(struct torture_context *torture, + struct smbcli_state *cli) +{ + int fnum; + bool ret; + const char *fname = "\\torture_qfileinfo.txt"; + + fnum = create_complex_file(cli, torture, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree)); + return false; + } + + ret = torture_raw_qfileinfo_internals(torture, torture, cli->tree, fnum, fname, false /* is_ipc */); + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + return ret; +} + +bool torture_raw_qfileinfo_pipe(struct torture_context *torture, + struct smbcli_state *cli) +{ + bool ret = true; + int fnum; + const char *fname = "\\lsass"; + union smb_open op; + NTSTATUS status; + + op.ntcreatex.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.root_fid.fnum = 0; + op.ntcreatex.in.access_mask = + SEC_STD_READ_CONTROL | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_WRITE_EA | + SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA; + op.ntcreatex.in.file_attr = 0; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + op.ntcreatex.in.create_options = 0; + op.ntcreatex.in.impersonation = + NTCREATEX_IMPERSONATION_IMPERSONATION; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = fname; + + status = smb_raw_open(cli->tree, torture, &op); + torture_assert_ntstatus_ok(torture, status, "smb_raw_open failed"); + + fnum = op.ntcreatex.out.file.fnum; + + ret = torture_raw_qfileinfo_internals(torture, torture, cli->tree, + fnum, fname, + true /* is_ipc */); + + smbcli_close(cli->tree, fnum); + return ret; +} |