/**************************************************************************** Open a file using TRANSACT2_OPEN - async send ****************************************************************************/ static struct smbcli_request *smb_raw_t2open_send(struct smbcli_tree *tree, union smb_open *parms) { struct smb_trans2 t2; uint16_t setup = TRANSACT2_OPEN; TALLOC_CTX *mem_ctx = talloc_init("smb_raw_t2open"); struct smbcli_request *req; uint16_t list_size; list_size = ea_list_size(parms->t2open.in.num_eas, parms->t2open.in.eas); t2.in.max_param = 30; t2.in.max_data = 0; t2.in.max_setup = 0; t2.in.flags = 0; t2.in.timeout = 0; t2.in.setup_count = 1; t2.in.setup = &setup; t2.in.params = data_blob_talloc(mem_ctx, NULL, 28); t2.in.data = data_blob_talloc(mem_ctx, NULL, list_size); SSVAL(t2.in.params.data, VWV(0), parms->t2open.in.flags); SSVAL(t2.in.params.data, VWV(1), parms->t2open.in.open_mode); SSVAL(t2.in.params.data, VWV(2), parms->t2open.in.search_attrs); SSVAL(t2.in.params.data, VWV(3), parms->t2open.in.file_attrs); raw_push_dos_date(tree->session->transport, t2.in.params.data, VWV(4), parms->t2open.in.write_time); SSVAL(t2.in.params.data, VWV(6), parms->t2open.in.open_func); SIVAL(t2.in.params.data, VWV(7), parms->t2open.in.size); SIVAL(t2.in.params.data, VWV(9), parms->t2open.in.timeout); SIVAL(t2.in.params.data, VWV(11), 0); SSVAL(t2.in.params.data, VWV(13), 0); smbcli_blob_append_string(tree->session, mem_ctx, &t2.in.params, parms->t2open.in.fname, STR_TERMINATE); ea_put_list(t2.in.data.data, parms->t2open.in.num_eas, parms->t2open.in.eas); req = smb_raw_trans2_send(tree, &t2); talloc_free(mem_ctx); return req; }
/**************************************************************************** create a directory using TRANSACT2_MKDIR - async interface ****************************************************************************/ static struct smbcli_request *smb_raw_t2mkdir_send(struct smbcli_tree *tree, union smb_mkdir *parms) { struct smb_trans2 t2; uint16_t setup = TRANSACT2_MKDIR; TALLOC_CTX *mem_ctx; struct smbcli_request *req; uint16_t data_total; mem_ctx = talloc_init("t2mkdir"); data_total = ea_list_size(parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas); t2.in.max_param = 2; t2.in.max_data = 0; t2.in.max_setup = 0; t2.in.flags = 0; t2.in.timeout = 0; t2.in.setup_count = 1; t2.in.setup = &setup; t2.in.params = data_blob_talloc(mem_ctx, NULL, 4); t2.in.data = data_blob_talloc(mem_ctx, NULL, data_total); SIVAL(t2.in.params.data, VWV(0), 0); /* reserved */ smbcli_blob_append_string(tree->session, mem_ctx, &t2.in.params, parms->t2mkdir.in.path, STR_TERMINATE); ea_put_list(t2.in.data.data, parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas); req = smb_raw_trans2_send(tree, &t2); talloc_free(mem_ctx); return req; }
/* 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) { int i; bool ret = true; int 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("%d 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; }
/* fill a single entry in a trans2 find reply */ static NTSTATUS find_fill_info(struct find_state *state, const union smb_search_data *file) { struct smbsrv_request *req = state->op->req; struct smb_trans2 *trans = state->op->trans; uint8_t *data; unsigned int ofs = trans->out.data.length; uint32_t ea_size; switch (state->data_level) { case RAW_SEARCH_DATA_GENERIC: case RAW_SEARCH_DATA_SEARCH: /* handled elsewhere */ return NT_STATUS_INVALID_LEVEL; case RAW_SEARCH_DATA_STANDARD: if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 27)); SIVAL(trans->out.data.data, ofs, file->standard.resume_key); ofs += 4; } else { TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 23)); } data = trans->out.data.data + ofs; srv_push_dos_date2(req->smb_conn, data, 0, file->standard.create_time); srv_push_dos_date2(req->smb_conn, data, 4, file->standard.access_time); srv_push_dos_date2(req->smb_conn, data, 8, file->standard.write_time); SIVAL(data, 12, file->standard.size); SIVAL(data, 16, file->standard.alloc_size); SSVAL(data, 20, file->standard.attrib); TRANS2_CHECK(smbsrv_blob_append_string(trans, &trans->out.data, file->standard.name.s, ofs + 22, SMBSRV_REQ_DEFAULT_STR_FLAGS(req), STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM)); break; case RAW_SEARCH_DATA_EA_SIZE: if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 31)); SIVAL(trans->out.data.data, ofs, file->ea_size.resume_key); ofs += 4; } else { TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 27)); } data = trans->out.data.data + ofs; srv_push_dos_date2(req->smb_conn, data, 0, file->ea_size.create_time); srv_push_dos_date2(req->smb_conn, data, 4, file->ea_size.access_time); srv_push_dos_date2(req->smb_conn, data, 8, file->ea_size.write_time); SIVAL(data, 12, file->ea_size.size); SIVAL(data, 16, file->ea_size.alloc_size); SSVAL(data, 20, file->ea_size.attrib); SIVAL(data, 22, file->ea_size.ea_size); TRANS2_CHECK(smbsrv_blob_append_string(trans, &trans->out.data, file->ea_size.name.s, ofs + 26, SMBSRV_REQ_DEFAULT_STR_FLAGS(req), STR_LEN8BIT | STR_NOALIGN)); TRANS2_CHECK(smbsrv_blob_fill_data(trans, &trans->out.data, trans->out.data.length + 1)); break; case RAW_SEARCH_DATA_EA_LIST: ea_size = ea_list_size(file->ea_list.eas.num_eas, file->ea_list.eas.eas); if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 27 + ea_size)); SIVAL(trans->out.data.data, ofs, file->ea_list.resume_key); ofs += 4; } else { TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 23 + ea_size)); } data = trans->out.data.data + ofs; srv_push_dos_date2(req->smb_conn, data, 0, file->ea_list.create_time); srv_push_dos_date2(req->smb_conn, data, 4, file->ea_list.access_time); srv_push_dos_date2(req->smb_conn, data, 8, file->ea_list.write_time); SIVAL(data, 12, file->ea_list.size); SIVAL(data, 16, file->ea_list.alloc_size); SSVAL(data, 20, file->ea_list.attrib); ea_put_list(data+22, file->ea_list.eas.num_eas, file->ea_list.eas.eas); TRANS2_CHECK(smbsrv_blob_append_string(trans, &trans->out.data, file->ea_list.name.s, ofs + 22 + ea_size, SMBSRV_REQ_DEFAULT_STR_FLAGS(req), STR_LEN8BIT | STR_NOALIGN)); TRANS2_CHECK(smbsrv_blob_fill_data(trans, &trans->out.data, trans->out.data.length + 1)); break; case RAW_SEARCH_DATA_DIRECTORY_INFO: case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO: case RAW_SEARCH_DATA_NAME_INFO: case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO: case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO: case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO: return smbsrv_push_passthru_search(trans, &trans->out.data, state->data_level, file, SMBSRV_REQ_DEFAULT_STR_FLAGS(req)); case RAW_SEARCH_DATA_UNIX_INFO: case RAW_SEARCH_DATA_UNIX_INFO2: return NT_STATUS_INVALID_LEVEL; } return NT_STATUS_OK; }
static NTSTATUS trans2_push_fileinfo(struct smbsrv_connection *smb_conn, TALLOC_CTX *mem_ctx, DATA_BLOB *blob, union smb_fileinfo *st, int default_str_flags) { uint32_t list_size; enum smb_fileinfo_level passthru_level; switch (st->generic.level) { case RAW_FILEINFO_GENERIC: case RAW_FILEINFO_GETATTR: case RAW_FILEINFO_GETATTRE: case RAW_FILEINFO_SEC_DESC: case RAW_FILEINFO_SMB2_ALL_EAS: case RAW_FILEINFO_SMB2_ALL_INFORMATION: /* handled elsewhere */ return NT_STATUS_INVALID_LEVEL; case RAW_FILEINFO_UNIX_BASIC: case RAW_FILEINFO_UNIX_LINK: /* not implemented yet */ return NT_STATUS_INVALID_LEVEL; case RAW_FILEINFO_STANDARD: TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 22)); srv_push_dos_date2(smb_conn, blob->data, 0, st->standard.out.create_time); srv_push_dos_date2(smb_conn, blob->data, 4, st->standard.out.access_time); srv_push_dos_date2(smb_conn, blob->data, 8, st->standard.out.write_time); SIVAL(blob->data, 12, st->standard.out.size); SIVAL(blob->data, 16, st->standard.out.alloc_size); SSVAL(blob->data, 20, st->standard.out.attrib); return NT_STATUS_OK; case RAW_FILEINFO_EA_SIZE: TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 26)); srv_push_dos_date2(smb_conn, blob->data, 0, st->ea_size.out.create_time); srv_push_dos_date2(smb_conn, blob->data, 4, st->ea_size.out.access_time); srv_push_dos_date2(smb_conn, blob->data, 8, st->ea_size.out.write_time); SIVAL(blob->data, 12, st->ea_size.out.size); SIVAL(blob->data, 16, st->ea_size.out.alloc_size); SSVAL(blob->data, 20, st->ea_size.out.attrib); SIVAL(blob->data, 22, st->ea_size.out.ea_size); return NT_STATUS_OK; case RAW_FILEINFO_EA_LIST: list_size = ea_list_size(st->ea_list.out.num_eas, st->ea_list.out.eas); TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, list_size)); ea_put_list(blob->data, st->ea_list.out.num_eas, st->ea_list.out.eas); return NT_STATUS_OK; case RAW_FILEINFO_ALL_EAS: list_size = ea_list_size(st->all_eas.out.num_eas, st->all_eas.out.eas); TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, list_size)); ea_put_list(blob->data, st->all_eas.out.num_eas, st->all_eas.out.eas); return NT_STATUS_OK; case RAW_FILEINFO_IS_NAME_VALID: return NT_STATUS_OK; case RAW_FILEINFO_BASIC_INFO: passthru_level = RAW_FILEINFO_BASIC_INFORMATION; break; case RAW_FILEINFO_STANDARD_INFO: passthru_level = RAW_FILEINFO_STANDARD_INFORMATION; break; case RAW_FILEINFO_EA_INFO: passthru_level = RAW_FILEINFO_EA_INFORMATION; break; case RAW_FILEINFO_COMPRESSION_INFO: passthru_level = RAW_FILEINFO_COMPRESSION_INFORMATION; break; case RAW_FILEINFO_ALL_INFO: passthru_level = RAW_FILEINFO_ALL_INFORMATION; break; case RAW_FILEINFO_NAME_INFO: passthru_level = RAW_FILEINFO_NAME_INFORMATION; break; case RAW_FILEINFO_ALT_NAME_INFO: passthru_level = RAW_FILEINFO_ALT_NAME_INFORMATION; break; case RAW_FILEINFO_STREAM_INFO: passthru_level = RAW_FILEINFO_STREAM_INFORMATION; break; default: passthru_level = st->generic.level; break; } return smbsrv_push_passthru_fileinfo(mem_ctx, blob, passthru_level, st, default_str_flags); }