1/* 2 Unix SMB/CIFS implementation. 3 4 SMB2 dir list test suite 5 6 Copyright (C) Andrew Tridgell 2005 7 Copyright (C) Zachary Loafman 2009 8 Copyright (C) Aravind Srinivasan 2009 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 3 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program. If not, see <http://www.gnu.org/licenses/>. 22*/ 23 24#include "includes.h" 25#include "libcli/smb2/smb2.h" 26#include "libcli/smb2/smb2_calls.h" 27#include "libcli/smb_composite/smb_composite.h" 28#include "libcli/raw/libcliraw.h" 29#include "libcli/raw/raw_proto.h" 30#include "libcli/libcli.h" 31 32#include "torture/torture.h" 33#include "torture/smb2/proto.h" 34#include "torture/util.h" 35 36#include "system/filesys.h" 37 38#define DNAME "smb2_dir" 39#define NFILES 100 40 41struct file_elem { 42 char *name; 43 bool found; 44}; 45 46static NTSTATUS populate_tree(struct torture_context *tctx, 47 TALLOC_CTX *mem_ctx, 48 struct smb2_tree *tree, 49 struct file_elem *files, 50 int nfiles, 51 struct smb2_handle *h_out) 52{ 53 struct smb2_create create; 54 char **strs = NULL; 55 NTSTATUS status; 56 bool ret; 57 int i; 58 59 smb2_deltree(tree, DNAME); 60 61 ZERO_STRUCT(create); 62 create.in.desired_access = SEC_RIGHTS_DIR_ALL; 63 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; 64 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; 65 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | 66 NTCREATEX_SHARE_ACCESS_WRITE | 67 NTCREATEX_SHARE_ACCESS_DELETE; 68 create.in.create_disposition = NTCREATEX_DISP_CREATE; 69 create.in.fname = DNAME; 70 71 status = smb2_create(tree, mem_ctx, &create); 72 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 73 *h_out = create.out.file.handle; 74 75 ZERO_STRUCT(create); 76 create.in.desired_access = SEC_RIGHTS_FILE_ALL; 77 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; 78 create.in.create_disposition = NTCREATEX_DISP_CREATE; 79 80 strs = generate_unique_strs(mem_ctx, 8, nfiles); 81 if (strs == NULL) { 82 status = NT_STATUS_OBJECT_NAME_COLLISION; 83 goto done; 84 } 85 for (i = 0; i < nfiles; i++) { 86 files[i].name = strs[i]; 87 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", 88 DNAME, files[i].name); 89 status = smb2_create(tree, mem_ctx, &create); 90 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 91 smb2_util_close(tree, create.out.file.handle); 92 } 93 done: 94 return status; 95} 96 97/* 98 test find continue 99*/ 100 101static bool test_find(struct torture_context *tctx, 102 struct smb2_tree *tree) 103{ 104 TALLOC_CTX *mem_ctx = talloc_new(tctx); 105 struct smb2_handle h; 106 struct smb2_find f; 107 union smb_search_data *d; 108 struct file_elem files[NFILES] = {}; 109 NTSTATUS status; 110 bool ret = true; 111 uint_t count; 112 int i, j, file_count = 0; 113 114 status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h); 115 116 ZERO_STRUCT(f); 117 f.in.file.handle = h; 118 f.in.pattern = "*"; 119 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; 120 f.in.max_response_size = 0x100; 121 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; 122 123 do { 124 status = smb2_find_level(tree, tree, &f, &count, &d); 125 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) 126 break; 127 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 128 129 for (i = 0; i < count; i++) { 130 bool expected; 131 const char *found = d[i].both_directory_info.name.s; 132 133 if (!strcmp(found, ".") || !strcmp(found, "..")) 134 continue; 135 136 expected = false; 137 for (j = 0; j < NFILES; j++) { 138 if (!strcmp(files[j].name, found)) { 139 files[j].found = true; 140 expected = true; 141 break; 142 } 143 } 144 145 if (expected) 146 continue; 147 148 torture_result(tctx, TORTURE_FAIL, 149 "(%s): didn't expect %s\n", 150 __location__, found); 151 ret = false; 152 goto done; 153 } 154 155 file_count = file_count + i; 156 f.in.continue_flags = 0; 157 f.in.max_response_size = 4096; 158 } while (count != 0); 159 160 torture_assert_int_equal_goto(tctx, file_count, NFILES + 2, ret, done, 161 ""); 162 163 for (i = 0; i < NFILES; i++) { 164 if (files[j].found) 165 continue; 166 167 torture_result(tctx, TORTURE_FAIL, 168 "(%s): expected to find %s, but didn't\n", 169 __location__, files[j].name); 170 ret = false; 171 goto done; 172 } 173 174 done: 175 smb2_deltree(tree, DNAME); 176 talloc_free(mem_ctx); 177 178 return ret; 179} 180 181/* 182 test fixed enumeration 183*/ 184 185static bool test_fixed(struct torture_context *tctx, 186 struct smb2_tree *tree) 187{ 188 TALLOC_CTX *mem_ctx = talloc_new(tctx); 189 struct smb2_create create; 190 struct smb2_handle h, h2; 191 struct smb2_find f; 192 union smb_search_data *d; 193 struct file_elem files[NFILES] = {}; 194 NTSTATUS status; 195 bool ret = true; 196 uint_t count; 197 int i; 198 199 status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h); 200 201 ZERO_STRUCT(create); 202 create.in.desired_access = SEC_RIGHTS_DIR_ALL; 203 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; 204 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; 205 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | 206 NTCREATEX_SHARE_ACCESS_WRITE | 207 NTCREATEX_SHARE_ACCESS_DELETE; 208 create.in.create_disposition = NTCREATEX_DISP_OPEN; 209 create.in.fname = DNAME; 210 211 status = smb2_create(tree, mem_ctx, &create); 212 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 213 h2 = create.out.file.handle; 214 215 ZERO_STRUCT(f); 216 f.in.file.handle = h; 217 f.in.pattern = "*"; 218 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; 219 f.in.max_response_size = 0x100; 220 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; 221 222 /* Start enumeration on h, then delete all from h2 */ 223 status = smb2_find_level(tree, tree, &f, &count, &d); 224 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 225 226 f.in.file.handle = h2; 227 228 do { 229 status = smb2_find_level(tree, tree, &f, &count, &d); 230 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) 231 break; 232 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 233 234 for (i = 0; i < count; i++) { 235 const char *found = d[i].both_directory_info.name.s; 236 char *path = talloc_asprintf(mem_ctx, "%s\\%s", 237 DNAME, found); 238 239 if (!strcmp(found, ".") || !strcmp(found, "..")) 240 continue; 241 242 status = smb2_util_unlink(tree, path); 243 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, 244 ""); 245 246 talloc_free(path); 247 } 248 249 f.in.continue_flags = 0; 250 f.in.max_response_size = 4096; 251 } while (count != 0); 252 253 /* Now finish h enumeration. */ 254 f.in.file.handle = h; 255 256 do { 257 status = smb2_find_level(tree, tree, &f, &count, &d); 258 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) 259 break; 260 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 261 262 for (i = 0; i < count; i++) { 263 const char *found = d[i].both_directory_info.name.s; 264 265 if (!strcmp(found, ".") || !strcmp(found, "..")) 266 continue; 267 268 torture_result(tctx, TORTURE_FAIL, 269 "(%s): didn't expect %s\n", 270 __location__, found); 271 ret = false; 272 goto done; 273 } 274 275 f.in.continue_flags = 0; 276 f.in.max_response_size = 4096; 277 } while (count != 0); 278 279 done: 280 smb2_util_close(tree, h); 281 smb2_util_close(tree, h2); 282 smb2_deltree(tree, DNAME); 283 talloc_free(mem_ctx); 284 285 return ret; 286} 287 288static struct { 289 const char *name; 290 uint8_t level; 291 enum smb_search_data_level data_level; 292 int name_offset; 293 int resume_key_offset; 294 uint32_t capability_mask; 295 NTSTATUS status; 296 union smb_search_data data; 297} levels[] = { 298 {"SMB2_FIND_DIRECTORY_INFO", 299 SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, 300 offsetof(union smb_search_data, directory_info.name.s), 301 offsetof(union smb_search_data, directory_info.file_index), 302 }, 303 {"SMB2_FIND_FULL_DIRECTORY_INFO", 304 SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, 305 offsetof(union smb_search_data, full_directory_info.name.s), 306 offsetof(union smb_search_data, full_directory_info.file_index), 307 }, 308 {"SMB2_FIND_NAME_INFO", 309 SMB2_FIND_NAME_INFO, RAW_SEARCH_DATA_NAME_INFO, 310 offsetof(union smb_search_data, name_info.name.s), 311 offsetof(union smb_search_data, name_info.file_index), 312 }, 313 {"SMB2_FIND_BOTH_DIRECTORY_INFO", 314 SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, 315 offsetof(union smb_search_data, both_directory_info.name.s), 316 offsetof(union smb_search_data, both_directory_info.file_index), 317 }, 318 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", 319 SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, 320 offsetof(union smb_search_data, id_full_directory_info.name.s), 321 offsetof(union smb_search_data, id_full_directory_info.file_index), 322 }, 323 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", 324 SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, 325 offsetof(union smb_search_data, id_both_directory_info.name.s), 326 offsetof(union smb_search_data, id_both_directory_info.file_index), 327 } 328}; 329 330/* 331 extract the name from a smb_data structure and level 332*/ 333static const char *extract_name(union smb_search_data *data, 334 uint8_t level, 335 enum smb_search_data_level data_level) 336{ 337 int i; 338 for (i=0;i<ARRAY_SIZE(levels);i++) { 339 if (level == levels[i].level && 340 data_level == levels[i].data_level) { 341 return *(const char **)(levels[i].name_offset + (char *)data); 342 } 343 } 344 return NULL; 345} 346 347/* find a level in the table by name */ 348static union smb_search_data *find(const char *name) 349{ 350 int i; 351 for (i=0;i<ARRAY_SIZE(levels);i++) { 352 if (NT_STATUS_IS_OK(levels[i].status) && 353 strcmp(levels[i].name, name) == 0) { 354 return &levels[i].data; 355 } 356 } 357 return NULL; 358} 359 360static bool fill_level_data(TALLOC_CTX *mem_ctx, 361 union smb_search_data *data, 362 union smb_search_data *d, 363 uint_t count, 364 uint8_t level, 365 enum smb_search_data_level data_level) 366{ 367 int i; 368 const char *sname = NULL; 369 for (i=0; i < count ; i++) { 370 sname = extract_name(&d[i], level, data_level); 371 if (sname == NULL) 372 return false; 373 if (!strcmp(sname, ".") || !strcmp(sname, "..")) 374 continue; 375 *data = d[i]; 376 } 377 return true; 378} 379 380 381NTSTATUS torture_single_file_search(struct smb2_tree *tree, 382 TALLOC_CTX *mem_ctx, 383 const char *pattern, 384 uint8_t level, 385 enum smb_search_data_level data_level, 386 int idx, 387 union smb_search_data *d, 388 uint_t *count, 389 struct smb2_handle *h) 390{ 391 struct smb2_find f; 392 NTSTATUS status; 393 394 ZERO_STRUCT(f); 395 f.in.file.handle = *h; 396 f.in.pattern = pattern; 397 f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART; 398 f.in.max_response_size = 0x100; 399 f.in.level = level; 400 401 status = smb2_find_level(tree, tree, &f, count, &d); 402 if (NT_STATUS_IS_OK(status)) 403 fill_level_data(mem_ctx, &levels[idx].data, d, *count, level, 404 data_level); 405 return status; 406} 407 408/* 409 basic testing of all File Information Classes using a single file 410*/ 411static bool test_one_file(struct torture_context *tctx, 412 struct smb2_tree *tree) 413{ 414 TALLOC_CTX *mem_ctx = talloc_new(tctx); 415 bool ret = true; 416 const char *fname = "torture_search.txt"; 417 NTSTATUS status; 418 int i; 419 uint_t count; 420 union smb_fileinfo all_info2, alt_info, internal_info; 421 union smb_search_data *s; 422 union smb_search_data d; 423 struct smb2_handle h, h2; 424 425 status = torture_smb2_testdir(tree, DNAME, &h); 426 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 427 428 status = smb2_create_complex_file(tree, DNAME "\\torture_search.txt", 429 &h2); 430 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 431 432 /* call all the File Information Classes */ 433 for (i=0;i<ARRAY_SIZE(levels);i++) { 434 torture_comment(tctx, "testing %s %d\n", levels[i].name, 435 levels[i].level); 436 437 levels[i].status = torture_single_file_search(tree, mem_ctx, 438 fname, levels[i].level, levels[i].data_level, 439 i, &d, &count, &h); 440 torture_assert_ntstatus_ok_goto(tctx, levels[i].status, ret, 441 done, ""); 442 } 443 444 /* get the all_info file into to check against */ 445 all_info2.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; 446 all_info2.generic.in.file.handle = h2; 447 status = smb2_getinfo_file(tree, tctx, &all_info2); 448 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, 449 "RAW_FILEINFO_ALL_INFO failed"); 450 451 alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFORMATION; 452 alt_info.generic.in.file.handle = h2; 453 status = smb2_getinfo_file(tree, tctx, &alt_info); 454 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, 455 "RAW_FILEINFO_ALT_NAME_INFO failed"); 456 457 internal_info.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION; 458 internal_info.generic.in.file.handle = h2; 459 status = smb2_getinfo_file(tree, tctx, &internal_info); 460 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, 461 "RAW_FILEINFO_INTERNAL_INFORMATION " 462 "failed"); 463 464#define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \ 465 s = find(name); \ 466 if (s) { \ 467 if ((s->sname1.field1) != (v.sname2.out.field2)) { \ 468 torture_result(tctx, TORTURE_FAIL, \ 469 "(%s) %s/%s [0x%x] != %s/%s [0x%x]\n", \ 470 __location__, \ 471 #sname1, #field1, (int)s->sname1.field1, \ 472 #sname2, #field2, (int)v.sname2.out.field2); \ 473 ret = false; \ 474 } \ 475 }} while (0) 476 477#define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \ 478 s = find(name); \ 479 if (s) { \ 480 if (s->sname1.field1 != \ 481 (~1 & nt_time_to_unix(v.sname2.out.field2))) { \ 482 torture_result(tctx, TORTURE_FAIL, \ 483 "(%s) %s/%s [%s] != %s/%s [%s]\n", \ 484 __location__, \ 485 #sname1, #field1, \ 486 timestring(tctx, s->sname1.field1), \ 487 #sname2, #field2, \ 488 nt_time_string(tctx, v.sname2.out.field2)); \ 489 ret = false; \ 490 } \ 491 }} while (0) 492 493#define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \ 494 s = find(name); \ 495 if (s) { \ 496 if (s->sname1.field1 != v.sname2.out.field2) { \ 497 torture_result(tctx, TORTURE_FAIL, \ 498 "(%s) %s/%s [%s] != %s/%s [%s]\n", \ 499 __location__, \ 500 #sname1, #field1, \ 501 nt_time_string(tctx, s->sname1.field1), \ 502 #sname2, #field2, \ 503 nt_time_string(tctx, v.sname2.out.field2)); \ 504 ret = false; \ 505 } \ 506 }} while (0) 507 508#define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \ 509 s = find(name); \ 510 if (s) { \ 511 if (!s->sname1.field1 || \ 512 strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \ 513 torture_result(tctx, TORTURE_FAIL, \ 514 "(%s) %s/%s [%s] != %s/%s [%s]\n", \ 515 __location__, \ 516 #sname1, #field1, s->sname1.field1, \ 517 #sname2, #field2, v.sname2.out.field2.s); \ 518 ret = false; \ 519 } \ 520 }} while (0) 521 522#define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \ 523 s = find(name); \ 524 if (s) { \ 525 if (!s->sname1.field1.s || \ 526 strcmp(s->sname1.field1.s, v.sname2.out.field2.s)) { \ 527 torture_result(tctx, TORTURE_FAIL, \ 528 "(%s) %s/%s [%s] != %s/%s [%s]\n", \ 529 __location__, \ 530 #sname1, #field1, s->sname1.field1.s, \ 531 #sname2, #field2, v.sname2.out.field2.s); \ 532 ret = false; \ 533 } \ 534 }} while (0) 535 536#define CHECK_NAME(name, sname1, field1, fname, flags) do { \ 537 s = find(name); \ 538 if (s) { \ 539 if (!s->sname1.field1.s || \ 540 strcmp(s->sname1.field1.s, fname)) { \ 541 torture_result(tctx, TORTURE_FAIL, \ 542 "(%s) %s/%s [%s] != %s\n", \ 543 __location__, \ 544 #sname1, #field1, s->sname1.field1.s, fname); \ 545 ret = false; \ 546 } \ 547 }} while (0) 548 549#define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \ 550 s = find(name); \ 551 if (s) { \ 552 if (!s->sname1.field1 || \ 553 strcmp(s->sname1.field1, fname)) { \ 554 torture_result(tctx, TORTURE_FAIL, \ 555 "(%s) %s/%s [%s] != %s\n", \ 556 __location__, \ 557 #sname1, #field1, s->sname1.field1, fname); \ 558 ret = false; \ 559 } \ 560 }} while (0) 561 562 /* check that all the results are as expected */ 563 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, attrib, all_info2, all_info2, attrib); 564 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info2, all_info2, attrib); 565 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info2, all_info2, attrib); 566 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, attrib, all_info2, all_info2, attrib); 567 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, attrib, all_info2, all_info2, attrib); 568 569 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, write_time, all_info2, all_info2, write_time); 570 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info2, all_info2, write_time); 571 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info2, all_info2, write_time); 572 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, write_time, all_info2, all_info2, write_time); 573 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, write_time, all_info2, all_info2, write_time); 574 575 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, create_time, all_info2, all_info2, create_time); 576 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info2, all_info2, create_time); 577 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info2, all_info2, create_time); 578 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, create_time, all_info2, all_info2, create_time); 579 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, create_time, all_info2, all_info2, create_time); 580 581 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, access_time, all_info2, all_info2, access_time); 582 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info2, all_info2, access_time); 583 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info2, all_info2, access_time); 584 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, access_time, all_info2, all_info2, access_time); 585 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, access_time, all_info2, all_info2, access_time); 586 587 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, change_time, all_info2, all_info2, change_time); 588 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, change_time, all_info2, all_info2, change_time); 589 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, change_time, all_info2, all_info2, change_time); 590 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, change_time, all_info2, all_info2, change_time); 591 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, change_time, all_info2, all_info2, change_time); 592 593 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, size, all_info2, all_info2, size); 594 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, size, all_info2, all_info2, size); 595 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, size, all_info2, all_info2, size); 596 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, size, all_info2, all_info2, size); 597 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, size, all_info2, all_info2, size); 598 599 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, alloc_size, all_info2, all_info2, alloc_size); 600 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info2, all_info2, alloc_size); 601 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info2, all_info2, alloc_size); 602 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, alloc_size, all_info2, all_info2, alloc_size); 603 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, alloc_size, all_info2, all_info2, alloc_size); 604 605 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info2, all_info2, ea_size); 606 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info2, all_info2, ea_size); 607 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, ea_size, all_info2, all_info2, ea_size); 608 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, ea_size, all_info2, all_info2, ea_size); 609 610 CHECK_NAME("SMB2_FIND_DIRECTORY_INFO", directory_info, name, fname, STR_TERMINATE_ASCII); 611 CHECK_NAME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, name, fname, STR_TERMINATE_ASCII); 612 CHECK_NAME("SMB2_FIND_NAME_INFO", name_info, name, fname, STR_TERMINATE_ASCII); 613 CHECK_NAME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, name, fname, STR_TERMINATE_ASCII); 614 CHECK_NAME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, name, fname, STR_TERMINATE_ASCII); 615 CHECK_NAME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, name, fname, STR_TERMINATE_ASCII); 616 617 CHECK_WSTR("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE); 618 619 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, file_id, internal_info, internal_information, file_id); 620 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, file_id, internal_info, internal_information, file_id); 621 622done: 623 smb2_util_close(tree, h); 624 smb2_util_unlink(tree, fname); 625 talloc_free(mem_ctx); 626 627 return ret; 628} 629 630 631struct multiple_result { 632 TALLOC_CTX *tctx; 633 int count; 634 union smb_search_data *list; 635}; 636 637bool fill_result(void *private_data, 638 union smb_search_data *file, 639 int count, 640 uint8_t level, 641 enum smb_search_data_level data_level) 642{ 643 int i; 644 const char *sname; 645 struct multiple_result *data = (struct multiple_result *)private_data; 646 647 for (i=0; i<count; i++) { 648 sname = extract_name(&file[i], level, data_level); 649 if (!strcmp(sname, ".") || !(strcmp(sname, ".."))) 650 continue; 651 data->count++; 652 data->list = talloc_realloc(data->tctx, 653 data->list, 654 union smb_search_data, 655 data->count); 656 data->list[data->count-1] = file[i]; 657 } 658 return true; 659} 660 661enum continue_type {CONT_SINGLE, CONT_INDEX, CONT_RESTART}; 662 663static NTSTATUS multiple_smb2_search(struct smb2_tree *tree, 664 TALLOC_CTX *tctx, 665 const char *pattern, 666 uint8_t level, 667 enum smb_search_data_level data_level, 668 enum continue_type cont_type, 669 void *data, 670 struct smb2_handle *h) 671{ 672 struct smb2_find f; 673 bool ret = true; 674 uint_t count = 0; 675 union smb_search_data *d; 676 NTSTATUS status; 677 struct multiple_result *result = (struct multiple_result *)data; 678 679 ZERO_STRUCT(f); 680 f.in.file.handle = *h; 681 f.in.pattern = pattern; 682 f.in.max_response_size = 0x1000; 683 f.in.level = level; 684 685 /* The search should start from the beginning everytime */ 686 f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART; 687 688 do { 689 status = smb2_find_level(tree, tree, &f, &count, &d); 690 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) 691 break; 692 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 693 if (!fill_result(result, d, count, level, data_level)) { 694 return NT_STATUS_UNSUCCESSFUL; 695 } 696 697 /* 698 * After the first iteration is complete set the CONTINUE 699 * FLAGS appropriately 700 */ 701 switch (cont_type) { 702 case CONT_INDEX: 703 f.in.continue_flags = SMB2_CONTINUE_FLAG_INDEX; 704 break; 705 case CONT_SINGLE: 706 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; 707 break; 708 case CONT_RESTART: 709 default: 710 /* we should prevent staying in the loop 711 * forever */ 712 f.in.continue_flags = 0; 713 break; 714 } 715 } while (count != 0); 716done: 717 return status; 718} 719 720 721static enum smb_search_data_level compare_data_level; 722uint8_t level_sort; 723 724static int search_compare(union smb_search_data *d1, 725 union smb_search_data *d2) 726{ 727 const char *s1, *s2; 728 729 s1 = extract_name(d1, level_sort, compare_data_level); 730 s2 = extract_name(d2, level_sort, compare_data_level); 731 return strcmp_safe(s1, s2); 732} 733 734/* 735 basic testing of search calls using many files 736*/ 737static bool test_many_files(struct torture_context *tctx, 738 struct smb2_tree *tree) 739{ 740 TALLOC_CTX *mem_ctx = talloc_new(tctx); 741 const int num_files = 700; 742 int i, t; 743 char *fname; 744 bool ret = true; 745 NTSTATUS status; 746 struct multiple_result result; 747 struct smb2_create create; 748 struct smb2_handle h; 749 struct { 750 const char *name; 751 const char *cont_name; 752 uint8_t level; 753 enum smb_search_data_level data_level; 754 enum continue_type cont_type; 755 } search_types[] = { 756 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_SINGLE}, 757 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_INDEX}, 758 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_RESTART}, 759 {"SMB2_FIND_DIRECTORY_INFO", "SINGLE", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_SINGLE}, 760 {"SMB2_FIND_DIRECTORY_INFO", "INDEX", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_INDEX}, 761 {"SMB2_FIND_DIRECTORY_INFO", "RESTART", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_RESTART}, 762 {"SMB2_FIND_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_SINGLE}, 763 {"SMB2_FIND_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_INDEX}, 764 {"SMB2_FIND_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_RESTART}, 765 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_SINGLE}, 766 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_INDEX}, 767 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESTART}, 768 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_SINGLE}, 769 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_INDEX}, 770 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESTART} 771 }; 772 773 smb2_deltree(tree, DNAME); 774 status = torture_smb2_testdir(tree, DNAME, &h); 775 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 776 777 torture_comment(tctx, "Testing with %d files\n", num_files); 778 ZERO_STRUCT(create); 779 create.in.desired_access = SEC_RIGHTS_FILE_ALL; 780 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; 781 create.in.create_disposition = NTCREATEX_DISP_CREATE; 782 783 for (i=num_files-1;i>=0;i--) { 784 fname = talloc_asprintf(mem_ctx, DNAME "\\t%03d-%d.txt", i, i); 785 create.in.fname = talloc_asprintf(mem_ctx, "%s", fname); 786 status = smb2_create(tree, mem_ctx, &create); 787 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 788 smb2_util_close(tree, create.out.file.handle); 789 talloc_free(fname); 790 } 791 792 for (t=0;t<ARRAY_SIZE(search_types);t++) { 793 ZERO_STRUCT(result); 794 result.tctx = talloc_new(tctx); 795 796 torture_comment(tctx, 797 "Continue %s via %s\n", search_types[t].name, 798 search_types[t].cont_name); 799 status = multiple_smb2_search(tree, tctx, "*", 800 search_types[t].level, 801 search_types[t].data_level, 802 search_types[t].cont_type, 803 &result, &h); 804 805 torture_assert_int_equal_goto(tctx, result.count, num_files, 806 ret, done, ""); 807 808 compare_data_level = search_types[t].data_level; 809 level_sort = search_types[t].level; 810 811 qsort(result.list, result.count, sizeof(result.list[0]), 812 QSORT_CAST search_compare); 813 814 for (i=0;i<result.count;i++) { 815 const char *s; 816 enum smb_search_level level; 817 level = RAW_SEARCH_SMB2; 818 s = extract_name(&result.list[i], 819 search_types[t].level, 820 compare_data_level); 821 fname = talloc_asprintf(mem_ctx, "t%03d-%d.txt", i, i); 822 torture_assert_str_equal_goto(tctx, s, fname, ret, 823 done, "Incorrect name"); 824 talloc_free(fname); 825 } 826 talloc_free(result.tctx); 827 } 828 829done: 830 smb2_util_close(tree, h); 831 smb2_deltree(tree, DNAME); 832 talloc_free(mem_ctx); 833 834 return ret; 835} 836 837/* 838 check an individual file result 839*/ 840static bool check_result(struct torture_context *tctx, 841 struct multiple_result *result, 842 const char *name, 843 bool exist, 844 uint32_t attrib) 845{ 846 int i; 847 for (i=0;i<result->count;i++) { 848 if (strcmp(name, 849 result->list[i].both_directory_info.name.s) == 0) { 850 break; 851 } 852 } 853 if (i == result->count) { 854 if (exist) { 855 torture_result(tctx, TORTURE_FAIL, 856 "failed: '%s' should exist with attribute %s\n", 857 name, attrib_string(result->list, attrib)); 858 return false; 859 } 860 return true; 861 } 862 863 if (!exist) { 864 torture_result(tctx, TORTURE_FAIL, 865 "failed: '%s' should NOT exist (has attribute %s)\n", 866 name, attrib_string(result->list, 867 result->list[i].both_directory_info.attrib)); 868 return false; 869 } 870 871 if ((result->list[i].both_directory_info.attrib&0xFFF) != attrib) { 872 torture_result(tctx, TORTURE_FAIL, 873 "failed: '%s' should have attribute 0x%x (has 0x%x)\n", 874 name, attrib, result->list[i].both_directory_info.attrib); 875 return false; 876 } 877 return true; 878} 879 880/* 881 test what happens when the directory is modified during a search 882*/ 883static bool test_modify_search(struct torture_context *tctx, 884 struct smb2_tree *tree) 885{ 886 int num_files = 700; 887 struct multiple_result result; 888 union smb_setfileinfo sfinfo; 889 TALLOC_CTX *mem_ctx = talloc_new(tctx); 890 struct smb2_create create; 891 struct smb2_handle h; 892 struct smb2_find f; 893 union smb_search_data *d; 894 struct file_elem files[700] = {}; 895 NTSTATUS status; 896 bool ret = true; 897 int i; 898 uint_t count; 899 900 smb2_deltree(tree, DNAME); 901 902 status = torture_smb2_testdir(tree, DNAME, &h); 903 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 904 905 torture_comment(tctx, "Creating %d files\n", num_files); 906 907 ZERO_STRUCT(create); 908 create.in.desired_access = SEC_RIGHTS_FILE_ALL; 909 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; 910 create.in.create_disposition = NTCREATEX_DISP_CREATE; 911 912 for (i = num_files-1; i >= 0; i--) { 913 files[i].name = talloc_asprintf(mem_ctx, "t%03d-%d.txt", i, i); 914 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", 915 DNAME, files[i].name); 916 status = smb2_create(tree, mem_ctx, &create); 917 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 918 smb2_util_close(tree, create.out.file.handle); 919 } 920 921 torture_comment(tctx, "pulling the first two files\n"); 922 ZERO_STRUCT(result); 923 result.tctx = talloc_new(tctx); 924 925 ZERO_STRUCT(f); 926 f.in.file.handle = h; 927 f.in.pattern = "*"; 928 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; 929 f.in.max_response_size = 0x100; 930 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; 931 932 do { 933 status = smb2_find_level(tree, tree, &f, &count, &d); 934 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) 935 break; 936 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 937 if (!fill_result(&result, d, count, f.in.level, 938 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO)) { 939 ret = false; 940 goto done; 941 } 942 } while (result.count < 2); 943 944 torture_comment(tctx, "Changing attributes and deleting\n"); 945 946 ZERO_STRUCT(create); 947 create.in.desired_access = SEC_RIGHTS_FILE_ALL; 948 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; 949 create.in.create_disposition = NTCREATEX_DISP_CREATE; 950 951 files[num_files].name = talloc_asprintf(mem_ctx, "T003-03.txt.2"); 952 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", DNAME, 953 files[num_files].name); 954 status = smb2_create(tree, mem_ctx, &create); 955 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 956 smb2_util_close(tree, create.out.file.handle); 957 958 ZERO_STRUCT(create); 959 create.in.desired_access = SEC_RIGHTS_FILE_ALL; 960 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; 961 create.in.create_disposition = NTCREATEX_DISP_CREATE; 962 963 files[num_files + 1].name = talloc_asprintf(mem_ctx, "T013-13.txt.2"); 964 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", DNAME, 965 files[num_files + 1].name); 966 status = smb2_create(tree, mem_ctx, &create); 967 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 968 smb2_util_close(tree, create.out.file.handle); 969 970 files[num_files + 2].name = talloc_asprintf(mem_ctx, "T013-13.txt.3"); 971 status = smb2_create_complex_file(tree, DNAME "\\T013-13.txt.3", &h); 972 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 973 974 smb2_util_unlink(tree, DNAME "\\T014-14.txt"); 975 smb2_util_setatr(tree, DNAME "\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN); 976 smb2_util_setatr(tree, DNAME "\\T016-16.txt", FILE_ATTRIBUTE_NORMAL); 977 smb2_util_setatr(tree, DNAME "\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM); 978 smb2_util_setatr(tree, DNAME "\\T018-18.txt", 0); 979 smb2_util_setatr(tree, DNAME "\\T039-39.txt", FILE_ATTRIBUTE_HIDDEN); 980 smb2_util_setatr(tree, DNAME "\\T000-0.txt", FILE_ATTRIBUTE_HIDDEN); 981 sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION; 982 sfinfo.generic.in.file.path = DNAME "\\T013-13.txt.3"; 983 sfinfo.disposition_info.in.delete_on_close = 1; 984 status = smb2_composite_setpathinfo(tree, &sfinfo); 985 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 986 987 /* Reset the numfiles to include the new files and start the 988 * search from the beginning */ 989 num_files = num_files + 2; 990 f.in.pattern = "*"; 991 f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART; 992 result.count = 0; 993 994 do { 995 status = smb2_find_level(tree, tree, &f, &count, &d); 996 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) 997 break; 998 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 999 if (!fill_result(&result, d, count, f.in.level, 1000 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO)) { 1001 ret = false; 1002 goto done; 1003 } 1004 f.in.continue_flags = 0; 1005 f.in.max_response_size = 4096; 1006 } while (count != 0); 1007 1008 1009 ret &= check_result(tctx, &result, "t039-39.txt", true, FILE_ATTRIBUTE_HIDDEN); 1010 ret &= check_result(tctx, &result, "t000-0.txt", true, FILE_ATTRIBUTE_HIDDEN); 1011 ret &= check_result(tctx, &result, "t014-14.txt", false, 0); 1012 ret &= check_result(tctx, &result, "t015-15.txt", true, FILE_ATTRIBUTE_HIDDEN); 1013 ret &= check_result(tctx, &result, "t016-16.txt", true, FILE_ATTRIBUTE_NORMAL); 1014 ret &= check_result(tctx, &result, "t017-17.txt", true, FILE_ATTRIBUTE_SYSTEM); 1015 ret &= check_result(tctx, &result, "t018-18.txt", true, FILE_ATTRIBUTE_ARCHIVE); 1016 ret &= check_result(tctx, &result, "t019-19.txt", true, FILE_ATTRIBUTE_ARCHIVE); 1017 ret &= check_result(tctx, &result, "T013-13.txt.2", true, FILE_ATTRIBUTE_ARCHIVE); 1018 ret &= check_result(tctx, &result, "T003-3.txt.2", false, 0); 1019 ret &= check_result(tctx, &result, "T013-13.txt.3", true, FILE_ATTRIBUTE_NORMAL); 1020 1021 if (!ret) { 1022 for (i=0;i<result.count;i++) { 1023 torture_warning(tctx, "%s %s (0x%x)\n", 1024 result.list[i].both_directory_info.name.s, 1025 attrib_string(tctx, 1026 result.list[i].both_directory_info.attrib), 1027 result.list[i].both_directory_info.attrib); 1028 } 1029 } 1030 done: 1031 smb2_util_close(tree, h); 1032 smb2_deltree(tree, DNAME); 1033 talloc_free(mem_ctx); 1034 1035 return ret; 1036} 1037 1038/* 1039 testing if directories always come back sorted 1040*/ 1041static bool test_sorted(struct torture_context *tctx, 1042 struct smb2_tree *tree) 1043{ 1044 TALLOC_CTX *mem_ctx = talloc_new(tctx); 1045 const int num_files = 700; 1046 int i; 1047 struct file_elem files[700] = {}; 1048 bool ret = true; 1049 NTSTATUS status; 1050 struct multiple_result result; 1051 struct smb2_handle h; 1052 1053 torture_comment(tctx, "Testing if directories always come back " 1054 "sorted\n"); 1055 status = populate_tree(tctx, mem_ctx, tree, files, num_files, &h); 1056 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 1057 1058 ZERO_STRUCT(result); 1059 result.tctx = tctx; 1060 1061 status = multiple_smb2_search(tree, tctx, "*", 1062 SMB2_FIND_BOTH_DIRECTORY_INFO, 1063 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, 1064 SMB2_CONTINUE_FLAG_SINGLE, 1065 &result, &h); 1066 1067 torture_assert_int_equal_goto(tctx, result.count, num_files, ret, done, 1068 ""); 1069 1070 for (i=0;i<num_files-1;i++) { 1071 const char *name1, *name2; 1072 name1 = result.list[i].both_directory_info.name.s; 1073 name2 = result.list[i+1].both_directory_info.name.s; 1074 if (strcasecmp_m(name1, name2) > 0) { 1075 torture_comment(tctx, "non-alphabetical order at entry " 1076 "%d '%s' '%s'\n", i, name1, name2); 1077 torture_comment(tctx, 1078 "Server does not produce sorted directory listings" 1079 "(not an error)\n"); 1080 goto done; 1081 } 1082 } 1083 talloc_free(result.list); 1084done: 1085 smb2_util_close(tree, h); 1086 smb2_deltree(tree, DNAME); 1087 talloc_free(mem_ctx); 1088 1089 return ret; 1090} 1091 1092/* test the behavior of file_index field in the SMB2_FIND struct */ 1093static bool test_file_index(struct torture_context *tctx, 1094 struct smb2_tree *tree) 1095{ 1096 TALLOC_CTX *mem_ctx = talloc_new(tctx); 1097 const int num_files = 100; 1098 int resume_index = 4; 1099 int i; 1100 char *fname; 1101 bool ret = true; 1102 NTSTATUS status; 1103 struct multiple_result result; 1104 struct smb2_create create; 1105 struct smb2_find f; 1106 struct smb2_handle h; 1107 union smb_search_data *d; 1108 int count; 1109 1110 smb2_deltree(tree, DNAME); 1111 1112 status = torture_smb2_testdir(tree, DNAME, &h); 1113 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 1114 1115 torture_comment(tctx, "Testing the behavior of file_index flag\n"); 1116 1117 ZERO_STRUCT(create); 1118 create.in.desired_access = SEC_RIGHTS_FILE_ALL; 1119 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; 1120 create.in.create_disposition = NTCREATEX_DISP_CREATE; 1121 for (i = num_files-1; i >= 0; i--) { 1122 fname = talloc_asprintf(mem_ctx, DNAME "\\file%u.txt", i); 1123 create.in.fname = fname; 1124 status = smb2_create(tree, mem_ctx, &create); 1125 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 1126 talloc_free(fname); 1127 smb2_util_close(tree, create.out.file.handle); 1128 } 1129 1130 ZERO_STRUCT(result); 1131 result.tctx = tctx; 1132 1133 ZERO_STRUCT(f); 1134 f.in.file.handle = h; 1135 f.in.pattern = "*"; 1136 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; 1137 f.in.max_response_size = 0x1000; 1138 f.in.level = SMB2_FIND_FULL_DIRECTORY_INFO; 1139 1140 do { 1141 status = smb2_find_level(tree, tree, &f, &count, &d); 1142 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) 1143 break; 1144 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 1145 if (!fill_result(&result, d, count, f.in.level, 1146 RAW_SEARCH_DATA_FULL_DIRECTORY_INFO)) { 1147 ret = false; 1148 goto done; 1149 } 1150 } while(result.count < 10); 1151 1152 if (result.list[0].full_directory_info.file_index == 0) { 1153 torture_skip_goto(tctx, done, 1154 "Talking to a server that doesn't provide a " 1155 "file index.\nWindows servers using NTFS do " 1156 "not provide a file_index. Skipping test\n"); 1157 } else { 1158 /* We are not talking to a Windows based server. Windows 1159 * servers using NTFS do not provide a file_index. Windows 1160 * servers using FAT do provide a file index, however in both 1161 * cases they do not honor a file index on a resume request. 1162 * See MS-FSCC <62> and MS-SMB2 <54> for more information. */ 1163 1164 /* Set the file_index flag to point to the fifth file from the 1165 * previous enumeration and try to start the subsequent 1166 * searches from that point */ 1167 f.in.file_index = 1168 result.list[resume_index].full_directory_info.file_index; 1169 f.in.continue_flags = SMB2_CONTINUE_FLAG_INDEX; 1170 1171 /* get the name of the next expected file */ 1172 fname = talloc_asprintf(mem_ctx, DNAME "\\%s", 1173 result.list[resume_index].full_directory_info.name.s); 1174 1175 ZERO_STRUCT(result); 1176 result.tctx = tctx; 1177 status = smb2_find_level(tree, tree, &f, &count, &d); 1178 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) 1179 goto done; 1180 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 1181 if (!fill_result(&result, d, count, f.in.level, 1182 RAW_SEARCH_DATA_FULL_DIRECTORY_INFO)) { 1183 ret = false; 1184 goto done; 1185 } 1186 if (strcmp(fname, 1187 result.list[0].full_directory_info.name.s)) { 1188 torture_comment(tctx, "Next expected file: %s but the " 1189 "server returned %s\n", fname, 1190 result.list[0].full_directory_info.name.s); 1191 torture_comment(tctx, 1192 "Not an error. Resuming using a file " 1193 "index is an optional feature of the " 1194 "protocol.\n"); 1195 goto done; 1196 } 1197 } 1198done: 1199 smb2_util_close(tree, h); 1200 smb2_deltree(tree, DNAME); 1201 talloc_free(mem_ctx); 1202 1203 return ret; 1204} 1205 1206/* 1207 * Tests directory enumeration in a directory containing >1000 files with 1208 * names of varying lengths. 1209 */ 1210static bool test_large_files(struct torture_context *tctx, 1211 struct smb2_tree *tree) 1212{ 1213 TALLOC_CTX *mem_ctx = talloc_new(tctx); 1214 const int num_files = 2000; 1215 int max_len = 200; 1216 /* These should be evenly divisible */ 1217 int num_at_len = num_files / max_len; 1218 struct file_elem files[2000] = {}; 1219 size_t len = 1; 1220 bool ret = true; 1221 NTSTATUS status; 1222 struct smb2_create create; 1223 struct smb2_find f; 1224 struct smb2_handle h; 1225 union smb_search_data *d; 1226 int count, file_count = 0; 1227 char **strs = NULL; 1228 int i, j; 1229 1230 torture_comment(tctx, 1231 "Testing directory enumeration in a directory with >1000 files\n"); 1232 1233 smb2_deltree(tree, DNAME); 1234 1235 ZERO_STRUCT(create); 1236 create.in.desired_access = SEC_RIGHTS_DIR_ALL; 1237 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; 1238 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; 1239 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | 1240 NTCREATEX_SHARE_ACCESS_WRITE | 1241 NTCREATEX_SHARE_ACCESS_DELETE; 1242 create.in.create_disposition = NTCREATEX_DISP_CREATE; 1243 create.in.fname = DNAME; 1244 1245 status = smb2_create(tree, mem_ctx, &create); 1246 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 1247 h = create.out.file.handle; 1248 1249 ZERO_STRUCT(create); 1250 create.in.desired_access = SEC_RIGHTS_FILE_ALL; 1251 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; 1252 create.in.create_disposition = NTCREATEX_DISP_CREATE; 1253 1254 for (i = 0; i < num_files; i++) { 1255 if (i % num_at_len == 0) { 1256 strs = generate_unique_strs(mem_ctx, len, num_at_len); 1257 len++; 1258 } 1259 files[i].name = strs[i % num_at_len]; 1260 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", 1261 DNAME, files[i].name); 1262 status = smb2_create(tree, mem_ctx, &create); 1263 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 1264 smb2_util_close(tree, create.out.file.handle); 1265 } 1266 1267 ZERO_STRUCT(f); 1268 f.in.file.handle = h; 1269 f.in.pattern = "*"; 1270 f.in.max_response_size = 0x100; 1271 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; 1272 1273 do { 1274 status = smb2_find_level(tree, tree, &f, &count, &d); 1275 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) 1276 break; 1277 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); 1278 1279 for (i = 0; i < count; i++) { 1280 bool expected; 1281 const char *found = d[i].both_directory_info.name.s; 1282 1283 if (!strcmp(found, ".") || !strcmp(found, "..")) 1284 continue; 1285 1286 expected = false; 1287 for (j = 0; j < 2000; j++) { 1288 if (!strcmp(files[j].name, found)) { 1289 files[j].found = true; 1290 expected = true; 1291 break; 1292 } 1293 } 1294 1295 if (expected) 1296 continue; 1297 1298 torture_result(tctx, TORTURE_FAIL, 1299 "(%s): didn't expect %s\n", 1300 __location__, found); 1301 ret = false; 1302 goto done; 1303 } 1304 file_count = file_count + i; 1305 f.in.continue_flags = 0; 1306 f.in.max_response_size = 4096; 1307 } while (count != 0); 1308 1309 torture_assert_int_equal_goto(tctx, file_count, num_files + 2, ret, 1310 done, ""); 1311 1312 for (i = 0; i < num_files; i++) { 1313 if (files[j].found) 1314 continue; 1315 1316 torture_result(tctx, TORTURE_FAIL, 1317 "(%s): expected to find %s, but didn't\n", 1318 __location__, files[j].name); 1319 ret = false; 1320 goto done; 1321 } 1322done: 1323 smb2_util_close(tree, h); 1324 smb2_deltree(tree, DNAME); 1325 talloc_free(mem_ctx); 1326 1327 return ret; 1328} 1329 1330struct torture_suite *torture_smb2_dir_init(void) 1331{ 1332 struct torture_suite *suite = 1333 torture_suite_create(talloc_autofree_context(), "DIR"); 1334 1335 torture_suite_add_1smb2_test(suite, "FIND", test_find); 1336 torture_suite_add_1smb2_test(suite, "FIXED", test_fixed); 1337 torture_suite_add_1smb2_test(suite, "ONE", test_one_file); 1338 torture_suite_add_1smb2_test(suite, "MANY", test_many_files); 1339 torture_suite_add_1smb2_test(suite, "MODIFY", test_modify_search); 1340 torture_suite_add_1smb2_test(suite, "SORTED", test_sorted); 1341 torture_suite_add_1smb2_test(suite, "FILE-INDEX", test_file_index); 1342 torture_suite_add_1smb2_test(suite, "LARGE-FILES", test_large_files); 1343 suite->description = talloc_strdup(suite, "SMB2-DIR tests"); 1344 1345 return suite; 1346} 1347