1/* 2 Unix SMB/CIFS implementation. 3 4 test suite for delayed write update 5 6 Copyright (C) Volker Lendecke 2004 7 Copyright (C) Andrew Tridgell 2004 8 Copyright (C) Jeremy Allison 2004 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 "torture/torture.h" 26#include "libcli/raw/libcliraw.h" 27#include "libcli/raw/raw_proto.h" 28#include "system/time.h" 29#include "system/filesys.h" 30#include "libcli/libcli.h" 31#include "torture/util.h" 32 33#define W2K8R2_TIMEDELAY_SECS 1 34#define W2K3_TIMEDELAY_SECS 2 35#define TIMEDELAY_SECS W2K3_TIMEDELAY_SECS 36 37#define BASEDIR "\\delaywrite" 38 39static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli) 40{ 41 union smb_fileinfo finfo1, finfo2; 42 const char *fname = BASEDIR "\\torture_file.txt"; 43 NTSTATUS status; 44 int fnum1 = -1; 45 bool ret = true; 46 ssize_t written; 47 struct timeval start; 48 struct timeval end; 49 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 50 int normal_delay = 2000000; 51 double sec = ((double)used_delay) / ((double)normal_delay); 52 int msec = 1000 * sec; 53 54 torture_comment(tctx, "\nRunning test_delayed_write_update\n"); 55 56 if (!torture_setup_dir(cli, BASEDIR)) { 57 return false; 58 } 59 60 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 61 if (fnum1 == -1) { 62 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname); 63 return false; 64 } 65 66 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; 67 finfo1.basic_info.in.file.fnum = fnum1; 68 finfo2 = finfo1; 69 70 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); 71 72 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); 73 74 torture_comment(tctx, "Initial write time %s\n", 75 nt_time_string(tctx, finfo1.basic_info.out.write_time)); 76 77 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 78 79 if (written != 1) { 80 torture_result(tctx, TORTURE_FAIL, 81 "write failed - wrote %d bytes (%s)\n", 82 (int)written, __location__); 83 return false; 84 } 85 86 start = timeval_current(); 87 end = timeval_add(&start, (120*sec), 0); 88 while (!timeval_expired(&end)) { 89 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 90 91 if (!NT_STATUS_IS_OK(status)) { 92 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 93 ret = false; 94 break; 95 } 96 torture_comment(tctx, "write time %s\n", 97 nt_time_string(tctx, finfo2.basic_info.out.write_time)); 98 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { 99 double diff = timeval_elapsed(&start); 100 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */ 101 torture_comment(tctx, "Server updated write_time after %.2f seconds" 102 "(1 sec == %.2f)(wrong!)\n", 103 diff, sec); 104 ret = false; 105 break; 106 } 107 108 torture_comment(tctx, "Server updated write_time after %.2f seconds" 109 "(1 sec == %.2f)(correct)\n", 110 diff, sec); 111 break; 112 } 113 fflush(stdout); 114 msleep(1 * msec); 115 } 116 117 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) { 118 torture_result(tctx, TORTURE_FAIL, 119 "Server did not update write time (wrong!)"); 120 ret = false; 121 } 122 123 124 if (fnum1 != -1) 125 smbcli_close(cli->tree, fnum1); 126 smbcli_unlink(cli->tree, fname); 127 smbcli_deltree(cli->tree, BASEDIR); 128 129 return ret; 130} 131 132static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli) 133{ 134 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4; 135 const char *fname = BASEDIR "\\torture_file1.txt"; 136 NTSTATUS status; 137 int fnum1 = -1; 138 bool ret = true; 139 ssize_t written; 140 struct timeval start; 141 struct timeval end; 142 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 143 int normal_delay = 2000000; 144 double sec = ((double)used_delay) / ((double)normal_delay); 145 int msec = 1000 * sec; 146 char buf[2048]; 147 148 torture_comment(tctx, "\nRunning test_delayed_write_update1\n"); 149 150 if (!torture_setup_dir(cli, BASEDIR)) { 151 return false; 152 } 153 154 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 155 if (fnum1 == -1) { 156 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname); 157 return false; 158 } 159 160 memset(buf, 'x', 2048); 161 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048); 162 163 /* 3 second delay to ensure we get past any 2 second time 164 granularity (older systems may have that) */ 165 msleep(3 * msec); 166 167 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO; 168 finfo1.all_info.in.file.fnum = fnum1; 169 finfo2 = finfo1; 170 finfo3 = finfo1; 171 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO; 172 pinfo4.all_info.in.file.path = fname; 173 174 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); 175 176 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); 177 178 torture_comment(tctx, "Initial write time %s\n", 179 nt_time_string(tctx, finfo1.all_info.out.write_time)); 180 181 /* 3 second delay to ensure we get past any 2 second time 182 granularity (older systems may have that) */ 183 msleep(3 * msec); 184 185 /* Do a zero length SMBwrite call to truncate. */ 186 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0); 187 188 if (written != 0) { 189 torture_result(tctx, TORTURE_FAIL, 190 "write failed - wrote %d bytes (%s)\n", 191 (int)written, __location__); 192 return false; 193 } 194 195 start = timeval_current(); 196 end = timeval_add(&start, (120*sec), 0); 197 while (!timeval_expired(&end)) { 198 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 199 200 if (!NT_STATUS_IS_OK(status)) { 201 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 202 ret = false; 203 break; 204 } 205 206 if (finfo2.all_info.out.size != 1024) { 207 torture_result(tctx, TORTURE_FAIL, 208 "file not truncated, size = %u (should be 1024)", 209 (unsigned int)finfo2.all_info.out.size); 210 ret = false; 211 break; 212 } 213 214 torture_comment(tctx, "write time %s\n", 215 nt_time_string(tctx, finfo2.all_info.out.write_time)); 216 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) { 217 double diff = timeval_elapsed(&start); 218 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */ 219 torture_comment(tctx, "After SMBwrite truncate " 220 "server updated write_time after %.2f seconds" 221 "(1 sec == %.2f)(wrong!)\n", 222 diff, sec); 223 ret = false; 224 break; 225 } 226 227 torture_comment(tctx, "After SMBwrite truncate " 228 "server updated write_time after %.2f seconds" 229 "(1 sec == %.2f)(correct)\n", 230 diff, sec); 231 break; 232 } 233 fflush(stdout); 234 msleep(1 * msec); 235 } 236 237 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) { 238 torture_result(tctx, TORTURE_FAIL, 239 "Server did not update write time (wrong!)"); 240 ret = false; 241 } 242 243 fflush(stdout); 244 msleep(2 * msec); 245 246 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */ 247 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1); 248 249 if (written != 1) { 250 torture_result(tctx, TORTURE_FAIL, 251 "write failed - wrote %d bytes (%s)", 252 (int)written, __location__); 253 return false; 254 } 255 256 start = timeval_current(); 257 end = timeval_add(&start, (10*sec), 0); 258 while (!timeval_expired(&end)) { 259 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3); 260 261 if (!NT_STATUS_IS_OK(status)) { 262 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 263 ret = false; 264 break; 265 } 266 267 if (finfo3.all_info.out.size != 1024) { 268 DEBUG(0, ("file not truncated, size = %u (should be 1024)\n", 269 (unsigned int)finfo3.all_info.out.size)); 270 ret = false; 271 break; 272 } 273 274 torture_comment(tctx, "write time %s\n", 275 nt_time_string(tctx, finfo3.all_info.out.write_time)); 276 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) { 277 double diff = timeval_elapsed(&start); 278 279 torture_comment(tctx, "server updated write_time after %.2f seconds" 280 "(1 sec == %.2f)(wrong)\n", 281 diff, sec); 282 break; 283 } 284 fflush(stdout); 285 msleep(1 * msec); 286 } 287 288 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) { 289 torture_result(tctx, TORTURE_FAIL, 290 "Server updated write time (wrong!)"); 291 ret = false; 292 } 293 294 fflush(stdout); 295 msleep(2 * msec); 296 297 /* the close should trigger an write time update */ 298 smbcli_close(cli->tree, fnum1); 299 fnum1 = -1; 300 301 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4); 302 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed"); 303 304 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) { 305 torture_result(tctx, TORTURE_FAIL, 306 "Server did not update write time on close (wrong!)"); 307 ret = false; 308 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) { 309 torture_comment(tctx, "Server updated write time on close (correct)\n"); 310 } 311 312 if (fnum1 != -1) 313 smbcli_close(cli->tree, fnum1); 314 smbcli_unlink(cli->tree, fname); 315 smbcli_deltree(cli->tree, BASEDIR); 316 317 return ret; 318} 319 320/* Updating with a SMBwrite of zero length 321 * changes the write time immediately - even on expand. */ 322 323static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli) 324{ 325 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4; 326 const char *fname = BASEDIR "\\torture_file1a.txt"; 327 NTSTATUS status; 328 int fnum1 = -1; 329 bool ret = true; 330 ssize_t written; 331 struct timeval start; 332 struct timeval end; 333 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 334 int normal_delay = 2000000; 335 double sec = ((double)used_delay) / ((double)normal_delay); 336 int msec = 1000 * sec; 337 char buf[2048]; 338 339 torture_comment(tctx, "\nRunning test_delayed_write_update1a\n"); 340 341 if (!torture_setup_dir(cli, BASEDIR)) { 342 return false; 343 } 344 345 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 346 if (fnum1 == -1) { 347 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname); 348 return false; 349 } 350 351 memset(buf, 'x', 2048); 352 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048); 353 354 /* 3 second delay to ensure we get past any 2 second time 355 granularity (older systems may have that) */ 356 msleep(3 * msec); 357 358 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO; 359 finfo1.all_info.in.file.fnum = fnum1; 360 finfo2 = finfo1; 361 finfo3 = finfo1; 362 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO; 363 pinfo4.all_info.in.file.path = fname; 364 365 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); 366 367 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); 368 369 torture_comment(tctx, "Initial write time %s\n", 370 nt_time_string(tctx, finfo1.all_info.out.write_time)); 371 372 /* Do a zero length SMBwrite call to truncate. */ 373 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0); 374 375 if (written != 0) { 376 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)", 377 (int)written, __location__); 378 return false; 379 } 380 381 start = timeval_current(); 382 end = timeval_add(&start, (120*sec), 0); 383 while (!timeval_expired(&end)) { 384 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 385 386 if (!NT_STATUS_IS_OK(status)) { 387 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s", 388 nt_errstr(status)); 389 ret = false; 390 break; 391 } 392 393 if (finfo2.all_info.out.size != 10240) { 394 torture_result(tctx, TORTURE_FAIL, 395 "file not truncated, size = %u (should be 10240)", 396 (unsigned int)finfo2.all_info.out.size); 397 ret = false; 398 break; 399 } 400 401 torture_comment(tctx, "write time %s\n", 402 nt_time_string(tctx, finfo2.all_info.out.write_time)); 403 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) { 404 double diff = timeval_elapsed(&start); 405 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */ 406 torture_comment(tctx, "After SMBwrite truncate " 407 "server updated write_time after %.2f seconds" 408 "(1 sec == %.2f)(wrong!)\n", 409 diff, sec); 410 ret = false; 411 break; 412 } 413 414 torture_comment(tctx, "After SMBwrite truncate " 415 "server updated write_time after %.2f seconds" 416 "(1 sec == %.2f)(correct)\n", 417 diff, sec); 418 break; 419 } 420 fflush(stdout); 421 msleep(1 * msec); 422 } 423 424 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) { 425 torture_result(tctx, TORTURE_FAIL, 426 "Server did not update write time (wrong!)"); 427 ret = false; 428 } 429 430 fflush(stdout); 431 msleep(2 * msec); 432 433 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */ 434 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1); 435 436 torture_assert_int_equal(tctx, written, 1, 437 "unexpected number of bytes written"); 438 439 start = timeval_current(); 440 end = timeval_add(&start, (10*sec), 0); 441 while (!timeval_expired(&end)) { 442 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3); 443 444 if (!NT_STATUS_IS_OK(status)) { 445 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s\n", 446 nt_errstr(status)); 447 ret = false; 448 break; 449 } 450 451 if (finfo3.all_info.out.size != 10240) { 452 torture_result(tctx, TORTURE_FAIL, 453 "file not truncated, size = %u (should be 10240)", 454 (unsigned int)finfo3.all_info.out.size); 455 ret = false; 456 break; 457 } 458 459 torture_comment(tctx, "write time %s\n", 460 nt_time_string(tctx, finfo3.all_info.out.write_time)); 461 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) { 462 double diff = timeval_elapsed(&start); 463 464 torture_comment(tctx, "server updated write_time after %.2f seconds" 465 "(1 sec == %.2f)(correct)\n", 466 diff, sec); 467 break; 468 } 469 fflush(stdout); 470 msleep(1 * msec); 471 } 472 473 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) { 474 torture_result(tctx, TORTURE_FAIL, 475 "Server updated write time (wrong!)"); 476 ret = false; 477 } 478 479 /* the close should trigger an write time update */ 480 smbcli_close(cli->tree, fnum1); 481 fnum1 = -1; 482 483 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4); 484 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed"); 485 486 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) { 487 torture_result(tctx, TORTURE_FAIL, 488 "Server did not update write time on close (wrong!)"); 489 ret = false; 490 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) { 491 torture_comment(tctx, "Server updated write time on close (correct)\n"); 492 } 493 494 if (fnum1 != -1) 495 smbcli_close(cli->tree, fnum1); 496 smbcli_unlink(cli->tree, fname); 497 smbcli_deltree(cli->tree, BASEDIR); 498 499 return ret; 500} 501 502/* Updating with a SET_FILE_END_OF_FILE_INFO 503 * changes the write time immediately - even on expand. */ 504 505static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli) 506{ 507 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4; 508 const char *fname = BASEDIR "\\torture_file1b.txt"; 509 NTSTATUS status; 510 int fnum1 = -1; 511 bool ret = true; 512 ssize_t written; 513 struct timeval start; 514 struct timeval end; 515 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 516 int normal_delay = 2000000; 517 double sec = ((double)used_delay) / ((double)normal_delay); 518 int msec = 1000 * sec; 519 char buf[2048]; 520 521 torture_comment(tctx, "\nRunning test_delayed_write_update1b\n"); 522 523 if (!torture_setup_dir(cli, BASEDIR)) { 524 return false; 525 } 526 527 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 528 if (fnum1 == -1) { 529 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname); 530 return false; 531 } 532 533 memset(buf, 'x', 2048); 534 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048); 535 536 /* 3 second delay to ensure we get past any 2 second time 537 granularity (older systems may have that) */ 538 msleep(3 * msec); 539 540 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO; 541 finfo1.all_info.in.file.fnum = fnum1; 542 finfo2 = finfo1; 543 finfo3 = finfo1; 544 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO; 545 pinfo4.all_info.in.file.path = fname; 546 547 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); 548 549 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); 550 551 torture_comment(tctx, "Initial write time %s\n", 552 nt_time_string(tctx, finfo1.all_info.out.write_time)); 553 554 /* Do a SET_END_OF_FILE_INFO call to truncate. */ 555 status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240); 556 557 torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed"); 558 559 start = timeval_current(); 560 end = timeval_add(&start, (120*sec), 0); 561 while (!timeval_expired(&end)) { 562 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 563 564 if (!NT_STATUS_IS_OK(status)) { 565 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 566 ret = false; 567 break; 568 } 569 570 if (finfo2.all_info.out.size != 10240) { 571 torture_result(tctx, TORTURE_FAIL, 572 "file not truncated (size = %u, should be 10240)", 573 (unsigned int)finfo2.all_info.out.size ); 574 ret = false; 575 break; 576 } 577 578 torture_comment(tctx, "write time %s\n", 579 nt_time_string(tctx, finfo2.all_info.out.write_time)); 580 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) { 581 double diff = timeval_elapsed(&start); 582 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */ 583 torture_result(tctx, TORTURE_FAIL, 584 "After SET_END_OF_FILE truncate " 585 "server updated write_time after %.2f seconds" 586 "(1 sec == %.2f)(wrong!)", 587 diff, sec); 588 ret = false; 589 break; 590 } 591 592 torture_comment(tctx, "After SET_END_OF_FILE truncate " 593 "server updated write_time after %.2f seconds" 594 "(1 sec == %.2f)(correct)\n", 595 diff, sec); 596 break; 597 } 598 fflush(stdout); 599 msleep(1 * msec); 600 } 601 602 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) { 603 torture_result(tctx, TORTURE_FAIL, 604 "Server did not update write time (wrong!)"); 605 ret = false; 606 } 607 608 fflush(stdout); 609 msleep(2 * msec); 610 611 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */ 612 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1); 613 614 torture_assert_int_equal(tctx, written, 1, 615 "unexpected number of bytes written"); 616 617 start = timeval_current(); 618 end = timeval_add(&start, (10*sec), 0); 619 while (!timeval_expired(&end)) { 620 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3); 621 622 if (!NT_STATUS_IS_OK(status)) { 623 torture_result(tctx, TORTURE_FAIL, 624 "fileinfo failed: %s", nt_errstr(status)); 625 ret = false; 626 break; 627 } 628 629 if (finfo3.all_info.out.size != 10240) { 630 DEBUG(0, ("file not truncated (size = %u, should be 10240)\n", 631 (unsigned int)finfo3.all_info.out.size )); 632 ret = false; 633 break; 634 } 635 636 torture_comment(tctx, "write time %s\n", 637 nt_time_string(tctx, finfo3.all_info.out.write_time)); 638 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) { 639 double diff = timeval_elapsed(&start); 640 641 torture_comment(tctx, "server updated write_time after %.2f seconds" 642 "(1 sec == %.2f)(correct)\n", 643 diff, sec); 644 break; 645 } 646 fflush(stdout); 647 msleep(1 * msec); 648 } 649 650 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) { 651 torture_result(tctx, TORTURE_FAIL, "Server updated write time (wrong!)\n"); 652 ret = false; 653 } 654 655 /* the close should trigger an write time update */ 656 smbcli_close(cli->tree, fnum1); 657 fnum1 = -1; 658 659 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4); 660 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed"); 661 662 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) { 663 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n"); 664 ret = false; 665 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) { 666 torture_comment(tctx, "Server updated write time on close (correct)\n"); 667 } 668 669 if (fnum1 != -1) 670 smbcli_close(cli->tree, fnum1); 671 smbcli_unlink(cli->tree, fname); 672 smbcli_deltree(cli->tree, BASEDIR); 673 674 return ret; 675} 676 677/* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */ 678 679static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli) 680{ 681 union smb_setfileinfo parms; 682 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4; 683 const char *fname = BASEDIR "\\torture_file1c.txt"; 684 NTSTATUS status; 685 int fnum1 = -1; 686 bool ret = true; 687 ssize_t written; 688 struct timeval start; 689 struct timeval end; 690 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 691 int normal_delay = 2000000; 692 double sec = ((double)used_delay) / ((double)normal_delay); 693 int msec = 1000 * sec; 694 char buf[2048]; 695 696 torture_comment(tctx, "\nRunning test_delayed_write_update1c\n"); 697 698 if (!torture_setup_dir(cli, BASEDIR)) { 699 return false; 700 } 701 702 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 703 if (fnum1 == -1) { 704 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname); 705 return false; 706 } 707 708 memset(buf, 'x', 2048); 709 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048); 710 711 /* 3 second delay to ensure we get past any 2 second time 712 granularity (older systems may have that) */ 713 msleep(3 * msec); 714 715 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO; 716 finfo1.all_info.in.file.fnum = fnum1; 717 finfo2 = finfo1; 718 finfo3 = finfo1; 719 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO; 720 pinfo4.all_info.in.file.path = fname; 721 722 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); 723 724 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); 725 726 torture_comment(tctx, "Initial write time %s\n", 727 nt_time_string(tctx, finfo1.all_info.out.write_time)); 728 729 /* Do a SET_ALLOCATION_SIZE call to truncate. */ 730 parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO; 731 parms.allocation_info.in.file.fnum = fnum1; 732 parms.allocation_info.in.alloc_size = 0; 733 734 status = smb_raw_setfileinfo(cli->tree, &parms); 735 736 torture_assert_ntstatus_ok(tctx, status, 737 "RAW_SFILEINFO_ALLOCATION_INFO failed"); 738 739 start = timeval_current(); 740 end = timeval_add(&start, (120*sec), 0); 741 while (!timeval_expired(&end)) { 742 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 743 744 if (!NT_STATUS_IS_OK(status)) { 745 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s", 746 nt_errstr(status)); 747 ret = false; 748 break; 749 } 750 751 if (finfo2.all_info.out.size != 0) { 752 torture_result(tctx, TORTURE_FAIL, 753 "file not truncated (size = %u, should be 10240)", 754 (unsigned int)finfo2.all_info.out.size); 755 ret = false; 756 break; 757 } 758 759 torture_comment(tctx, "write time %s\n", 760 nt_time_string(tctx, finfo2.all_info.out.write_time)); 761 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) { 762 double diff = timeval_elapsed(&start); 763 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */ 764 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate " 765 "server updated write_time after %.2f seconds" 766 "(1 sec == %.2f)(wrong!)\n", 767 diff, sec); 768 ret = false; 769 break; 770 } 771 772 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate " 773 "server updated write_time after %.2f seconds" 774 "(1 sec == %.2f)(correct)\n", 775 diff, sec); 776 break; 777 } 778 fflush(stdout); 779 msleep(1 * msec); 780 } 781 782 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) { 783 torture_result(tctx, TORTURE_FAIL, 784 "Server did not update write time (wrong!)"); 785 ret = false; 786 } 787 788 fflush(stdout); 789 msleep(2 * msec); 790 791 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */ 792 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1); 793 torture_assert_int_equal(tctx, written, 1, 794 "Unexpected number of bytes written"); 795 796 start = timeval_current(); 797 end = timeval_add(&start, (10*sec), 0); 798 while (!timeval_expired(&end)) { 799 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3); 800 801 if (!NT_STATUS_IS_OK(status)) { 802 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s", 803 nt_errstr(status)); 804 ret = false; 805 break; 806 } 807 808 if (finfo3.all_info.out.size != 1) { 809 torture_result(tctx, TORTURE_FAIL, "file not expanded"); 810 ret = false; 811 break; 812 } 813 814 torture_comment(tctx, "write time %s\n", 815 nt_time_string(tctx, finfo3.all_info.out.write_time)); 816 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) { 817 double diff = timeval_elapsed(&start); 818 819 torture_comment(tctx, "server updated write_time after %.2f seconds" 820 "(1 sec == %.2f)(correct)\n", 821 diff, sec); 822 break; 823 } 824 fflush(stdout); 825 msleep(1 * msec); 826 } 827 828 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) { 829 torture_result(tctx, TORTURE_FAIL, 830 "Server updated write time (wrong!)"); 831 ret = false; 832 } 833 834 /* the close should trigger an write time update */ 835 smbcli_close(cli->tree, fnum1); 836 fnum1 = -1; 837 838 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4); 839 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed"); 840 841 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) { 842 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n"); 843 ret = false; 844 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) { 845 torture_comment(tctx, "Server updated write time on close (correct)\n"); 846 } 847 848 if (fnum1 != -1) 849 smbcli_close(cli->tree, fnum1); 850 smbcli_unlink(cli->tree, fname); 851 smbcli_deltree(cli->tree, BASEDIR); 852 853 return ret; 854} 855 856/* 857 * Do as above, but using 2 connections. 858 */ 859 860static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli, 861 struct smbcli_state *cli2) 862{ 863 union smb_fileinfo finfo1, finfo2; 864 const char *fname = BASEDIR "\\torture_file.txt"; 865 NTSTATUS status; 866 int fnum1 = -1; 867 int fnum2 = -1; 868 bool ret = true; 869 ssize_t written; 870 struct timeval start; 871 struct timeval end; 872 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 873 int normal_delay = 2000000; 874 double sec = ((double)used_delay) / ((double)normal_delay); 875 int msec = 1000 * sec; 876 union smb_flush flsh; 877 878 torture_comment(tctx, "\nRunning test_delayed_write_update2\n"); 879 880 if (!torture_setup_dir(cli, BASEDIR)) { 881 return false; 882 } 883 884 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 885 if (fnum1 == -1) { 886 torture_comment(tctx, "Failed to open %s\n", fname); 887 return false; 888 } 889 890 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; 891 finfo1.basic_info.in.file.fnum = fnum1; 892 finfo2 = finfo1; 893 894 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); 895 896 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); 897 898 torture_comment(tctx, "Initial write time %s\n", 899 nt_time_string(tctx, finfo1.basic_info.out.write_time)); 900 901 /* 3 second delay to ensure we get past any 2 second time 902 granularity (older systems may have that) */ 903 msleep(3 * msec); 904 905 { 906 /* Try using setfileinfo instead of write to update write time. */ 907 union smb_setfileinfo sfinfo; 908 time_t t_set = time(NULL); 909 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; 910 sfinfo.basic_info.in.file.fnum = fnum1; 911 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time; 912 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time; 913 914 /* I tried this with both + and - ve to see if it makes a different. 915 It doesn't - once the filetime is set via setfileinfo it stays that way. */ 916#if 1 917 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000); 918#else 919 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000); 920#endif 921 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time; 922 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; 923 924 status = smb_raw_setfileinfo(cli->tree, &sfinfo); 925 926 torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed"); 927 } 928 929 finfo2.basic_info.in.file.path = fname; 930 931 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2); 932 933 if (!NT_STATUS_IS_OK(status)) { 934 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 935 return false; 936 } 937 torture_comment(tctx, "write time %s\n", 938 nt_time_string(tctx, finfo2.basic_info.out.write_time)); 939 940 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { 941 torture_comment(tctx, "Server updated write_time (correct)\n"); 942 } else { 943 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n"); 944 ret = false; 945 } 946 947 /* Now try a write to see if the write time gets reset. */ 948 949 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; 950 finfo1.basic_info.in.file.fnum = fnum1; 951 finfo2 = finfo1; 952 953 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); 954 955 if (!NT_STATUS_IS_OK(status)) { 956 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 957 return false; 958 } 959 960 torture_comment(tctx, "Modified write time %s\n", 961 nt_time_string(tctx, finfo1.basic_info.out.write_time)); 962 963 964 torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n"); 965 966 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10); 967 968 if (written != 10) { 969 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 970 (int)written, __location__); 971 return false; 972 } 973 974 /* Just to prove to tridge that the an smbflush has no effect on 975 the write time :-). The setfileinfo IS STICKY. JRA. */ 976 977 torture_comment(tctx, "Doing flush after write\n"); 978 979 flsh.flush.level = RAW_FLUSH_FLUSH; 980 flsh.flush.in.file.fnum = fnum1; 981 status = smb_raw_flush(cli->tree, &flsh); 982 if (!NT_STATUS_IS_OK(status)) { 983 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status))); 984 return false; 985 } 986 987 /* Once the time was set using setfileinfo then it stays set - writes 988 don't have any effect. But make sure. */ 989 start = timeval_current(); 990 end = timeval_add(&start, (15*sec), 0); 991 while (!timeval_expired(&end)) { 992 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 993 994 if (!NT_STATUS_IS_OK(status)) { 995 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 996 ret = false; 997 break; 998 } 999 torture_comment(tctx, "write time %s\n", 1000 nt_time_string(tctx, finfo2.basic_info.out.write_time)); 1001 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { 1002 double diff = timeval_elapsed(&start); 1003 torture_comment(tctx, "Server updated write_time after %.2f seconds" 1004 "(1sec == %.2f) (wrong!)\n", 1005 diff, sec); 1006 ret = false; 1007 break; 1008 } 1009 fflush(stdout); 1010 msleep(1 * msec); 1011 } 1012 1013 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) { 1014 torture_comment(tctx, "Server did not update write time (correct)\n"); 1015 } 1016 1017 fflush(stdout); 1018 msleep(2 * msec); 1019 1020 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); 1021 if (fnum2 == -1) { 1022 torture_comment(tctx, "Failed to open %s\n", fname); 1023 return false; 1024 } 1025 1026 torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n"); 1027 1028 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10); 1029 1030 if (written != 10) { 1031 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 1032 (int)written, __location__); 1033 return false; 1034 } 1035 1036 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 1037 1038 if (!NT_STATUS_IS_OK(status)) { 1039 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 1040 return false; 1041 } 1042 torture_comment(tctx, "write time %s\n", 1043 nt_time_string(tctx, finfo2.basic_info.out.write_time)); 1044 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { 1045 torture_comment(tctx, "Server updated write_time (wrong!)\n"); 1046 ret = false; 1047 } 1048 1049 torture_comment(tctx, "Closing the first fd to see if write time updated.\n"); 1050 smbcli_close(cli->tree, fnum1); 1051 fnum1 = -1; 1052 1053 torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n"); 1054 1055 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10); 1056 1057 if (written != 10) { 1058 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 1059 (int)written, __location__); 1060 return false; 1061 } 1062 1063 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1064 finfo1.basic_info.in.file.fnum = fnum2; 1065 finfo2 = finfo1; 1066 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 1067 1068 if (!NT_STATUS_IS_OK(status)) { 1069 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 1070 return false; 1071 } 1072 torture_comment(tctx, "write time %s\n", 1073 nt_time_string(tctx, finfo2.basic_info.out.write_time)); 1074 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { 1075 torture_comment(tctx, "Server updated write_time (wrong!)\n"); 1076 ret = false; 1077 } 1078 1079 /* Once the time was set using setfileinfo then it stays set - writes 1080 don't have any effect. But make sure. */ 1081 start = timeval_current(); 1082 end = timeval_add(&start, (15*sec), 0); 1083 while (!timeval_expired(&end)) { 1084 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 1085 1086 if (!NT_STATUS_IS_OK(status)) { 1087 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 1088 ret = false; 1089 break; 1090 } 1091 torture_comment(tctx, "write time %s\n", 1092 nt_time_string(tctx, finfo2.basic_info.out.write_time)); 1093 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { 1094 double diff = timeval_elapsed(&start); 1095 torture_comment(tctx, "Server updated write_time after %.2f seconds " 1096 "(1sec == %.2f) (wrong!)\n", 1097 diff, sec); 1098 ret = false; 1099 break; 1100 } 1101 fflush(stdout); 1102 msleep(1 * msec); 1103 } 1104 1105 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) { 1106 torture_comment(tctx, "Server did not update write time (correct)\n"); 1107 } 1108 1109 torture_comment(tctx, "Closing second fd to see if write time updated.\n"); 1110 1111 smbcli_close(cli->tree, fnum2); 1112 fnum2 = -1; 1113 1114 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); 1115 if (fnum1 == -1) { 1116 torture_comment(tctx, "Failed to open %s\n", fname); 1117 return false; 1118 } 1119 1120 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1121 finfo1.basic_info.in.file.fnum = fnum1; 1122 finfo2 = finfo1; 1123 1124 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); 1125 1126 if (!NT_STATUS_IS_OK(status)) { 1127 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 1128 return false; 1129 } 1130 1131 torture_comment(tctx, "Second open initial write time %s\n", 1132 nt_time_string(tctx, finfo1.basic_info.out.write_time)); 1133 1134 msleep(10 * msec); 1135 torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n"); 1136 1137 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10); 1138 1139 if (written != 10) { 1140 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 1141 (int)written, __location__); 1142 return false; 1143 } 1144 1145 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1146 finfo1.basic_info.in.file.fnum = fnum1; 1147 finfo2 = finfo1; 1148 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 1149 1150 if (!NT_STATUS_IS_OK(status)) { 1151 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 1152 return false; 1153 } 1154 torture_comment(tctx, "write time %s\n", 1155 nt_time_string(tctx, finfo2.basic_info.out.write_time)); 1156 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { 1157 torture_comment(tctx, "Server updated write_time (wrong!)\n"); 1158 ret = false; 1159 } 1160 1161 /* Now the write time should be updated again */ 1162 start = timeval_current(); 1163 end = timeval_add(&start, (15*sec), 0); 1164 while (!timeval_expired(&end)) { 1165 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 1166 1167 if (!NT_STATUS_IS_OK(status)) { 1168 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status))); 1169 ret = false; 1170 break; 1171 } 1172 torture_comment(tctx, "write time %s\n", 1173 nt_time_string(tctx, finfo2.basic_info.out.write_time)); 1174 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { 1175 double diff = timeval_elapsed(&start); 1176 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */ 1177 torture_comment(tctx, "Server updated write_time after %.2f seconds" 1178 "(1sec == %.2f) (wrong!)\n", 1179 diff, sec); 1180 ret = false; 1181 break; 1182 } 1183 1184 torture_comment(tctx, "Server updated write_time after %.2f seconds" 1185 "(1sec == %.2f) (correct)\n", 1186 diff, sec); 1187 break; 1188 } 1189 fflush(stdout); 1190 msleep(1*msec); 1191 } 1192 1193 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) { 1194 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n"); 1195 ret = false; 1196 } 1197 1198 1199 /* One more test to do. We should read the filetime via findfirst on the 1200 second connection to ensure it's the same. This is very easy for a Windows 1201 server but a bastard to get right on a POSIX server. JRA. */ 1202 1203 if (fnum1 != -1) 1204 smbcli_close(cli->tree, fnum1); 1205 smbcli_unlink(cli->tree, fname); 1206 smbcli_deltree(cli->tree, BASEDIR); 1207 1208 return ret; 1209} 1210 1211 1212/* Windows does obviously not update the stat info during a write call. I 1213 * *think* this is the problem causing a spurious Excel 2003 on XP error 1214 * message when saving a file. Excel does a setfileinfo, writes, and then does 1215 * a getpath(!)info. Or so... For Samba sometimes it displays an error message 1216 * that the file might have been changed in between. What i've been able to 1217 * trace down is that this happens if the getpathinfo after the write shows a 1218 * different last write time than the setfileinfo showed. This is really 1219 * nasty.... 1220 */ 1221 1222static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli, 1223 struct smbcli_state *cli2) 1224{ 1225 union smb_fileinfo finfo1, finfo2; 1226 const char *fname = BASEDIR "\\torture_file.txt"; 1227 NTSTATUS status; 1228 int fnum1 = -1; 1229 int fnum2; 1230 bool ret = true; 1231 ssize_t written; 1232 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 1233 int normal_delay = 2000000; 1234 double sec = ((double)used_delay) / ((double)normal_delay); 1235 int msec = 1000 * sec; 1236 1237 torture_comment(tctx, "\nRunning test_finfo_after_write\n"); 1238 1239 if (!torture_setup_dir(cli, BASEDIR)) { 1240 return false; 1241 } 1242 1243 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 1244 if (fnum1 == -1) { 1245 ret = false; 1246 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); 1247 goto done; 1248 } 1249 1250 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1251 finfo1.basic_info.in.file.fnum = fnum1; 1252 1253 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); 1254 1255 if (!NT_STATUS_IS_OK(status)) { 1256 ret = false; 1257 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status)); 1258 goto done; 1259 } 1260 1261 msleep(1 * msec); 1262 1263 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 1264 1265 if (written != 1) { 1266 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 1267 ret = false; 1268 goto done; 1269 } 1270 1271 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); 1272 if (fnum2 == -1) { 1273 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s", 1274 smbcli_errstr(cli2->tree)); 1275 ret = false; 1276 goto done; 1277 } 1278 1279 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1); 1280 1281 if (written != 1) { 1282 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", 1283 (int)written); 1284 ret = false; 1285 goto done; 1286 } 1287 1288 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1289 finfo2.basic_info.in.file.path = fname; 1290 1291 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2); 1292 1293 if (!NT_STATUS_IS_OK(status)) { 1294 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", 1295 nt_errstr(status)); 1296 ret = false; 1297 goto done; 1298 } 1299 1300 if (finfo1.basic_info.out.create_time != 1301 finfo2.basic_info.out.create_time) { 1302 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed"); 1303 ret = false; 1304 goto done; 1305 } 1306 1307 if (finfo1.basic_info.out.access_time != 1308 finfo2.basic_info.out.access_time) { 1309 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed"); 1310 ret = false; 1311 goto done; 1312 } 1313 1314 if (finfo1.basic_info.out.write_time != 1315 finfo2.basic_info.out.write_time) { 1316 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n" 1317 "write time conn 1 = %s, conn 2 = %s", 1318 nt_time_string(tctx, finfo1.basic_info.out.write_time), 1319 nt_time_string(tctx, finfo2.basic_info.out.write_time)); 1320 ret = false; 1321 goto done; 1322 } 1323 1324 if (finfo1.basic_info.out.change_time != 1325 finfo2.basic_info.out.change_time) { 1326 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed"); 1327 ret = false; 1328 goto done; 1329 } 1330 1331 /* One of the two following calls updates the qpathinfo. */ 1332 1333 /* If you had skipped the smbcli_write on fnum2, it would 1334 * *not* have updated the stat on disk */ 1335 1336 smbcli_close(cli2->tree, fnum2); 1337 cli2 = NULL; 1338 1339 /* This call is only for the people looking at ethereal :-) */ 1340 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1341 finfo2.basic_info.in.file.path = fname; 1342 1343 status = smb_raw_pathinfo(cli->tree, tctx, &finfo2); 1344 1345 if (!NT_STATUS_IS_OK(status)) { 1346 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status)); 1347 ret = false; 1348 goto done; 1349 } 1350 1351 done: 1352 if (fnum1 != -1) 1353 smbcli_close(cli->tree, fnum1); 1354 smbcli_unlink(cli->tree, fname); 1355 smbcli_deltree(cli->tree, BASEDIR); 1356 1357 return ret; 1358} 1359 1360#define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \ 1361 uint64_t r = 10*1000*1000; \ 1362 NTTIME g = (given).basic_info.out.write_time; \ 1363 NTTIME gr = (g / r) * r; \ 1364 NTTIME c = (correct).basic_info.out.write_time; \ 1365 NTTIME cr = (c / r) * r; \ 1366 bool strict = torture_setting_bool(tctx, "strict mode", false); \ 1367 bool err = false; \ 1368 if (strict && (g cmp c)) { \ 1369 err = true; \ 1370 } else if ((g cmp c) && (gr cmp cr)) { \ 1371 /* handle filesystem without high resolution timestamps */ \ 1372 err = true; \ 1373 } \ 1374 if (err) { \ 1375 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \ 1376 #given, nt_time_string(tctx, g), (unsigned long long)g, \ 1377 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \ 1378 ret = false; \ 1379 goto done; \ 1380 } \ 1381} while (0) 1382#define COMPARE_WRITE_TIME_EQUAL(given,correct) \ 1383 COMPARE_WRITE_TIME_CMP(given,correct,!=) 1384#define COMPARE_WRITE_TIME_GREATER(given,correct) \ 1385 COMPARE_WRITE_TIME_CMP(given,correct,<=) 1386#define COMPARE_WRITE_TIME_LESS(given,correct) \ 1387 COMPARE_WRITE_TIME_CMP(given,correct,>=) 1388 1389#define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \ 1390 NTTIME g = (given).basic_info.out.access_time; \ 1391 NTTIME c = (correct).basic_info.out.access_time; \ 1392 if (g cmp c) { \ 1393 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \ 1394 #given, nt_time_string(tctx, g), \ 1395 #cmp, #correct, nt_time_string(tctx, c)); \ 1396 ret = false; \ 1397 goto done; \ 1398 } \ 1399} while (0) 1400#define COMPARE_ACCESS_TIME_EQUAL(given,correct) \ 1401 COMPARE_ACCESS_TIME_CMP(given,correct,!=) 1402 1403#define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \ 1404 COMPARE_ACCESS_TIME_EQUAL(given,correct); \ 1405 COMPARE_WRITE_TIME_EQUAL(given,correct); \ 1406} while (0) 1407 1408#define GET_INFO_FILE(finfo) do { \ 1409 NTSTATUS _status; \ 1410 _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \ 1411 if (!NT_STATUS_IS_OK(_status)) { \ 1412 ret = false; \ 1413 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \ 1414 nt_errstr(_status)); \ 1415 goto done; \ 1416 } \ 1417 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \ 1418 nt_time_string(tctx, finfo.basic_info.out.access_time), \ 1419 nt_time_string(tctx, finfo.basic_info.out.write_time)); \ 1420} while (0) 1421#define GET_INFO_FILE2(finfo) do { \ 1422 NTSTATUS _status; \ 1423 _status = smb_raw_fileinfo(cli2->tree, tctx, &finfo); \ 1424 if (!NT_STATUS_IS_OK(_status)) { \ 1425 ret = false; \ 1426 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \ 1427 nt_errstr(_status)); \ 1428 goto done; \ 1429 } \ 1430 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \ 1431 nt_time_string(tctx, finfo.basic_info.out.access_time), \ 1432 nt_time_string(tctx, finfo.basic_info.out.write_time)); \ 1433} while (0) 1434#define GET_INFO_PATH(pinfo) do { \ 1435 NTSTATUS _status; \ 1436 _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \ 1437 if (!NT_STATUS_IS_OK(_status)) { \ 1438 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \ 1439 nt_errstr(_status)); \ 1440 ret = false; \ 1441 goto done; \ 1442 } \ 1443 torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \ 1444 nt_time_string(tctx, pinfo.basic_info.out.access_time), \ 1445 nt_time_string(tctx, pinfo.basic_info.out.write_time)); \ 1446} while (0) 1447#define GET_INFO_BOTH(finfo,pinfo) do { \ 1448 GET_INFO_FILE(finfo); \ 1449 GET_INFO_PATH(pinfo); \ 1450 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \ 1451} while (0) 1452 1453#define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \ 1454 NTSTATUS _status; \ 1455 union smb_setfileinfo sfinfo; \ 1456 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \ 1457 sfinfo.basic_info.in.file.fnum = tfnum; \ 1458 sfinfo.basic_info.in.create_time = 0; \ 1459 sfinfo.basic_info.in.access_time = 0; \ 1460 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \ 1461 sfinfo.basic_info.in.change_time = 0; \ 1462 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \ 1463 _status = smb_raw_setfileinfo(tree, &sfinfo); \ 1464 if (!NT_STATUS_IS_OK(_status)) { \ 1465 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \ 1466 nt_errstr(_status)); \ 1467 ret = false; \ 1468 goto done; \ 1469 } \ 1470} while (0) 1471#define SET_INFO_FILE(finfo, wrtime) \ 1472 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1) 1473 1474#define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \ 1475 NTSTATUS _status; \ 1476 union smb_setfileinfo sfinfo; \ 1477 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \ 1478 sfinfo.basic_info.in.file.fnum = tfnum; \ 1479 sfinfo.basic_info.in.create_time = 0; \ 1480 sfinfo.basic_info.in.access_time = 0; \ 1481 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \ 1482 sfinfo.basic_info.in.write_time += (ns); \ 1483 sfinfo.basic_info.in.change_time = 0; \ 1484 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \ 1485 _status = smb_raw_setfileinfo(tree, &sfinfo); \ 1486 if (!NT_STATUS_IS_OK(_status)) { \ 1487 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \ 1488 nt_errstr(_status)); \ 1489 ret = false; \ 1490 goto done; \ 1491 } \ 1492} while (0) 1493 1494static bool test_delayed_write_update3(struct torture_context *tctx, 1495 struct smbcli_state *cli, 1496 struct smbcli_state *cli2) 1497{ 1498 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4; 1499 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5; 1500 const char *fname = BASEDIR "\\torture_file3.txt"; 1501 int fnum1 = -1; 1502 bool ret = true; 1503 ssize_t written; 1504 struct timeval start; 1505 struct timeval end; 1506 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 1507 int normal_delay = 2000000; 1508 double sec = ((double)used_delay) / ((double)normal_delay); 1509 int msec = 1000 * sec; 1510 1511 torture_comment(tctx, "\nRunning test_delayed_write_update3\n"); 1512 1513 if (!torture_setup_dir(cli, BASEDIR)) { 1514 return false; 1515 } 1516 1517 torture_comment(tctx, "Open the file handle\n"); 1518 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 1519 if (fnum1 == -1) { 1520 ret = false; 1521 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); 1522 goto done; 1523 } 1524 1525 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1526 finfo0.basic_info.in.file.fnum = fnum1; 1527 finfo1 = finfo0; 1528 finfo2 = finfo0; 1529 finfo3 = finfo0; 1530 finfo4 = finfo0; 1531 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1532 pinfo0.basic_info.in.file.path = fname; 1533 pinfo1 = pinfo0; 1534 pinfo2 = pinfo0; 1535 pinfo3 = pinfo0; 1536 pinfo4 = pinfo0; 1537 pinfo5 = pinfo0; 1538 1539 /* get the initial times */ 1540 GET_INFO_BOTH(finfo0,pinfo0); 1541 1542 /* 1543 * make sure the write time is updated 2 seconds later 1544 * calcuated from the first write 1545 * (but expect upto 5 seconds extra time for a busy server) 1546 */ 1547 start = timeval_current(); 1548 end = timeval_add(&start, 7 * sec, 0); 1549 while (!timeval_expired(&end)) { 1550 /* do a write */ 1551 torture_comment(tctx, "Do a write on the file handle\n"); 1552 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 1553 if (written != 1) { 1554 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 1555 ret = false; 1556 goto done; 1557 } 1558 /* get the times after the write */ 1559 GET_INFO_FILE(finfo1); 1560 1561 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) { 1562 double diff = timeval_elapsed(&start); 1563 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */ 1564 torture_comment(tctx, "Server updated write_time after %.2f seconds " 1565 "(1sec == %.2f) (wrong!)\n", 1566 diff, sec); 1567 ret = false; 1568 break; 1569 } 1570 1571 torture_comment(tctx, "Server updated write_time after %.2f seconds " 1572 "(1sec == %.2f) (correct)\n", 1573 diff, sec); 1574 break; 1575 } 1576 msleep(0.5 * msec); 1577 } 1578 1579 GET_INFO_BOTH(finfo1,pinfo1); 1580 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0); 1581 1582 /* sure any further write doesn't update the write time */ 1583 start = timeval_current(); 1584 end = timeval_add(&start, 15 * sec, 0); 1585 while (!timeval_expired(&end)) { 1586 /* do a write */ 1587 torture_comment(tctx, "Do a write on the file handle\n"); 1588 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 1589 if (written != 1) { 1590 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 1591 ret = false; 1592 goto done; 1593 } 1594 /* get the times after the write */ 1595 GET_INFO_BOTH(finfo2,pinfo2); 1596 1597 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { 1598 double diff = timeval_elapsed(&start); 1599 torture_comment(tctx, "Server updated write_time after %.2f seconds " 1600 "(1sec == %.2f) (wrong!)\n", 1601 diff, sec); 1602 ret = false; 1603 break; 1604 } 1605 msleep(1 * msec); 1606 } 1607 1608 GET_INFO_BOTH(finfo2,pinfo2); 1609 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); 1610 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { 1611 torture_comment(tctx, "Server did not update write_time (correct)\n"); 1612 } 1613 1614 /* sleep */ 1615 msleep(5 * msec); 1616 1617 GET_INFO_BOTH(finfo3,pinfo3); 1618 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); 1619 1620 /* 1621 * the close updates the write time to the time of the close 1622 * and not to the time of the last write! 1623 */ 1624 torture_comment(tctx, "Close the file handle\n"); 1625 smbcli_close(cli->tree, fnum1); 1626 fnum1 = -1; 1627 1628 GET_INFO_PATH(pinfo4); 1629 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3); 1630 1631 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) { 1632 torture_comment(tctx, "Server updated the write_time on close (correct)\n"); 1633 } 1634 1635 done: 1636 if (fnum1 != -1) 1637 smbcli_close(cli->tree, fnum1); 1638 smbcli_unlink(cli->tree, fname); 1639 smbcli_deltree(cli->tree, BASEDIR); 1640 1641 return ret; 1642} 1643 1644/* 1645 * Show that a truncate write always updates the write time even 1646 * if an initial write has already updated the write time. 1647 */ 1648 1649static bool test_delayed_write_update3a(struct torture_context *tctx, 1650 struct smbcli_state *cli, 1651 struct smbcli_state *cli2) 1652{ 1653 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4; 1654 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5; 1655 const char *fname = BASEDIR "\\torture_file3a.txt"; 1656 int fnum1 = -1; 1657 bool ret = true; 1658 ssize_t written; 1659 int i; 1660 struct timeval start; 1661 struct timeval end; 1662 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 1663 int normal_delay = 2000000; 1664 double sec = ((double)used_delay) / ((double)normal_delay); 1665 int msec = 1000 * sec; 1666 1667 torture_comment(tctx, "\nRunning test_delayed_write_update3a\n"); 1668 1669 if (!torture_setup_dir(cli, BASEDIR)) { 1670 return false; 1671 } 1672 1673 torture_comment(tctx, "Open the file handle\n"); 1674 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 1675 if (fnum1 == -1) { 1676 ret = false; 1677 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); 1678 goto done; 1679 } 1680 1681 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1682 finfo0.basic_info.in.file.fnum = fnum1; 1683 finfo1 = finfo0; 1684 finfo2 = finfo0; 1685 finfo3 = finfo0; 1686 finfo4 = finfo0; 1687 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1688 pinfo0.basic_info.in.file.path = fname; 1689 pinfo1 = pinfo0; 1690 pinfo2 = pinfo0; 1691 pinfo3 = pinfo0; 1692 pinfo4 = pinfo0; 1693 pinfo5 = pinfo0; 1694 1695 /* get the initial times */ 1696 GET_INFO_BOTH(finfo0,pinfo0); 1697 1698 /* 1699 * sleep some time, to demonstrate the handling of write times 1700 * doesn't depend on the time since the open 1701 */ 1702 msleep(5 * msec); 1703 1704 /* get the initial times */ 1705 GET_INFO_BOTH(finfo1,pinfo1); 1706 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); 1707 1708 /* 1709 * make sure the write time is updated 2 seconds later 1710 * calcuated from the first write 1711 * (but expect upto 5 seconds extra time for a busy server) 1712 */ 1713 start = timeval_current(); 1714 end = timeval_add(&start, 7 * sec, 0); 1715 while (!timeval_expired(&end)) { 1716 /* do a write */ 1717 torture_comment(tctx, "Do a write on the file handle\n"); 1718 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 1719 if (written != 1) { 1720 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 1721 ret = false; 1722 goto done; 1723 } 1724 /* get the times after the write */ 1725 GET_INFO_FILE(finfo1); 1726 1727 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) { 1728 double diff = timeval_elapsed(&start); 1729 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */ 1730 torture_comment(tctx, "Server updated write_time after %.2f seconds " 1731 "(1sec == %.2f) (wrong!)\n", 1732 diff, sec); 1733 ret = false; 1734 break; 1735 } 1736 1737 torture_comment(tctx, "Server updated write_time after %.2f seconds " 1738 "(1sec == %.2f) (correct)\n", 1739 diff, sec); 1740 break; 1741 } 1742 msleep(0.5 * msec); 1743 } 1744 1745 GET_INFO_BOTH(finfo1,pinfo1); 1746 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0); 1747 1748 msleep(3 * msec); 1749 1750 /* 1751 * demonstrate that a truncate write always 1752 * updates the write time immediately 1753 */ 1754 for (i=0; i < 3; i++) { 1755 msleep(2 * msec); 1756 /* do a write */ 1757 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i); 1758 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0); 1759 if (written != 0) { 1760 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written); 1761 ret = false; 1762 goto done; 1763 } 1764 /* get the times after the write */ 1765 GET_INFO_BOTH(finfo2,pinfo2); 1766 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); 1767 finfo1 = finfo2; 1768 } 1769 1770 msleep(3 * msec); 1771 1772 /* sure any further write doesn't update the write time */ 1773 start = timeval_current(); 1774 end = timeval_add(&start, 15 * sec, 0); 1775 while (!timeval_expired(&end)) { 1776 /* do a write */ 1777 torture_comment(tctx, "Do a write on the file handle\n"); 1778 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 1779 if (written != 1) { 1780 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 1781 ret = false; 1782 goto done; 1783 } 1784 /* get the times after the write */ 1785 GET_INFO_BOTH(finfo2,pinfo2); 1786 1787 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { 1788 double diff = timeval_elapsed(&start); 1789 torture_comment(tctx, "Server updated write_time after %.2f seconds " 1790 "(1sec == %.2f) (wrong!)\n", 1791 diff, sec); 1792 ret = false; 1793 break; 1794 } 1795 msleep(1 * msec); 1796 } 1797 1798 GET_INFO_BOTH(finfo2,pinfo2); 1799 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); 1800 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { 1801 torture_comment(tctx, "Server did not update write_time (correct)\n"); 1802 } 1803 1804 /* sleep */ 1805 msleep(3 * msec); 1806 1807 /* get the initial times */ 1808 GET_INFO_BOTH(finfo1,pinfo1); 1809 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2); 1810 1811 /* 1812 * demonstrate that a truncate write always 1813 * updates the write time immediately 1814 */ 1815 for (i=0; i < 3; i++) { 1816 msleep(2 * msec); 1817 /* do a write */ 1818 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i); 1819 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0); 1820 if (written != 0) { 1821 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written); 1822 ret = false; 1823 goto done; 1824 } 1825 /* get the times after the write */ 1826 GET_INFO_BOTH(finfo2,pinfo2); 1827 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); 1828 finfo1 = finfo2; 1829 } 1830 1831 /* sleep */ 1832 msleep(3 * msec); 1833 1834 GET_INFO_BOTH(finfo3,pinfo3); 1835 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); 1836 1837 /* 1838 * the close doesn't update the write time 1839 */ 1840 torture_comment(tctx, "Close the file handle\n"); 1841 smbcli_close(cli->tree, fnum1); 1842 fnum1 = -1; 1843 1844 GET_INFO_PATH(pinfo4); 1845 COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3); 1846 1847 if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) { 1848 torture_comment(tctx, "Server did not update the write_time on close (correct)\n"); 1849 } 1850 1851 done: 1852 if (fnum1 != -1) 1853 smbcli_close(cli->tree, fnum1); 1854 smbcli_unlink(cli->tree, fname); 1855 smbcli_deltree(cli->tree, BASEDIR); 1856 1857 return ret; 1858} 1859 1860/* 1861 * Show a close after write updates the write timestamp to 1862 * the close time, not the last write time. 1863 */ 1864 1865static bool test_delayed_write_update3b(struct torture_context *tctx, 1866 struct smbcli_state *cli, 1867 struct smbcli_state *cli2) 1868{ 1869 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4; 1870 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5; 1871 const char *fname = BASEDIR "\\torture_file3b.txt"; 1872 int fnum1 = -1; 1873 bool ret = true; 1874 ssize_t written; 1875 struct timeval start; 1876 struct timeval end; 1877 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 1878 int normal_delay = 2000000; 1879 double sec = ((double)used_delay) / ((double)normal_delay); 1880 int msec = 1000 * sec; 1881 1882 torture_comment(tctx, "\nRunning test_delayed_write_update3b\n"); 1883 1884 if (!torture_setup_dir(cli, BASEDIR)) { 1885 return false; 1886 } 1887 1888 torture_comment(tctx, "Open the file handle\n"); 1889 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 1890 if (fnum1 == -1) { 1891 ret = false; 1892 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); 1893 goto done; 1894 } 1895 1896 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1897 finfo0.basic_info.in.file.fnum = fnum1; 1898 finfo1 = finfo0; 1899 finfo2 = finfo0; 1900 finfo3 = finfo0; 1901 finfo4 = finfo0; 1902 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 1903 pinfo0.basic_info.in.file.path = fname; 1904 pinfo1 = pinfo0; 1905 pinfo2 = pinfo0; 1906 pinfo3 = pinfo0; 1907 pinfo4 = pinfo0; 1908 pinfo5 = pinfo0; 1909 1910 /* get the initial times */ 1911 GET_INFO_BOTH(finfo0,pinfo0); 1912 1913 /* 1914 * sleep some time, to demonstrate the handling of write times 1915 * doesn't depend on the time since the open 1916 */ 1917 msleep(5 * msec); 1918 1919 /* get the initial times */ 1920 GET_INFO_BOTH(finfo1,pinfo1); 1921 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); 1922 1923 /* 1924 * make sure the write time is updated 2 seconds later 1925 * calcuated from the first write 1926 * (but expect upto 5 seconds extra time for a busy server) 1927 */ 1928 start = timeval_current(); 1929 end = timeval_add(&start, 7 * sec, 0); 1930 while (!timeval_expired(&end)) { 1931 /* do a write */ 1932 torture_comment(tctx, "Do a write on the file handle\n"); 1933 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 1934 if (written != 1) { 1935 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 1936 ret = false; 1937 goto done; 1938 } 1939 /* get the times after the write */ 1940 GET_INFO_FILE(finfo1); 1941 1942 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) { 1943 double diff = timeval_elapsed(&start); 1944 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */ 1945 torture_comment(tctx, "Server updated write_time after %.2f seconds " 1946 "(1sec == %.2f) (wrong!)\n", 1947 diff, sec); 1948 ret = false; 1949 break; 1950 } 1951 1952 torture_comment(tctx, "Server updated write_time after %.2f seconds " 1953 "(1sec == %.2f) (correct)\n", 1954 diff, sec); 1955 break; 1956 } 1957 msleep(0.5 * msec); 1958 } 1959 1960 GET_INFO_BOTH(finfo1,pinfo1); 1961 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0); 1962 1963 /* sure any further write doesn't update the write time */ 1964 start = timeval_current(); 1965 end = timeval_add(&start, 15 * sec, 0); 1966 while (!timeval_expired(&end)) { 1967 /* do a write */ 1968 torture_comment(tctx, "Do a write on the file handle\n"); 1969 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 1970 if (written != 1) { 1971 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 1972 ret = false; 1973 goto done; 1974 } 1975 /* get the times after the write */ 1976 GET_INFO_BOTH(finfo2,pinfo2); 1977 1978 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { 1979 double diff = timeval_elapsed(&start); 1980 torture_comment(tctx, "Server updated write_time after %.2f seconds " 1981 "(1sec == %.2f) (wrong!)\n", 1982 diff, sec); 1983 ret = false; 1984 break; 1985 } 1986 msleep(1 * msec); 1987 } 1988 1989 GET_INFO_BOTH(finfo2,pinfo2); 1990 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); 1991 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { 1992 torture_comment(tctx, "Server did not update write_time (correct)\n"); 1993 } 1994 1995 /* sleep */ 1996 msleep(5 * msec); 1997 1998 GET_INFO_BOTH(finfo3,pinfo3); 1999 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); 2000 2001 /* 2002 * the close updates the write time to the time of the close 2003 * and not to the time of the last write! 2004 */ 2005 torture_comment(tctx, "Close the file handle\n"); 2006 smbcli_close(cli->tree, fnum1); 2007 fnum1 = -1; 2008 2009 GET_INFO_PATH(pinfo4); 2010 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3); 2011 2012 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) { 2013 torture_comment(tctx, "Server updated the write_time on close (correct)\n"); 2014 } 2015 2016 done: 2017 if (fnum1 != -1) 2018 smbcli_close(cli->tree, fnum1); 2019 smbcli_unlink(cli->tree, fname); 2020 smbcli_deltree(cli->tree, BASEDIR); 2021 2022 return ret; 2023} 2024 2025/* 2026 * Check that a write after a truncate write doesn't update 2027 * the timestamp, but a truncate write after a write does. 2028 * Also prove that a close after a truncate write updates the 2029 * timestamp to current, not the time of last write. 2030 */ 2031 2032static bool test_delayed_write_update3c(struct torture_context *tctx, 2033 struct smbcli_state *cli, 2034 struct smbcli_state *cli2) 2035{ 2036 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4; 2037 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5; 2038 const char *fname = BASEDIR "\\torture_file3c.txt"; 2039 int fnum1 = -1; 2040 bool ret = true; 2041 ssize_t written; 2042 int i; 2043 struct timeval start; 2044 struct timeval end; 2045 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 2046 int normal_delay = 2000000; 2047 double sec = ((double)used_delay) / ((double)normal_delay); 2048 int msec = 1000 * sec; 2049 2050 torture_comment(tctx, "\nRunning test_delayed_write_update3c\n"); 2051 2052 if (!torture_setup_dir(cli, BASEDIR)) { 2053 return false; 2054 } 2055 2056 torture_comment(tctx, "Open the file handle\n"); 2057 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 2058 if (fnum1 == -1) { 2059 ret = false; 2060 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); 2061 goto done; 2062 } 2063 2064 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 2065 finfo0.basic_info.in.file.fnum = fnum1; 2066 finfo1 = finfo0; 2067 finfo2 = finfo0; 2068 finfo3 = finfo0; 2069 finfo4 = finfo0; 2070 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 2071 pinfo0.basic_info.in.file.path = fname; 2072 pinfo1 = pinfo0; 2073 pinfo2 = pinfo0; 2074 pinfo3 = pinfo0; 2075 pinfo4 = pinfo0; 2076 pinfo5 = pinfo0; 2077 2078 /* get the initial times */ 2079 GET_INFO_BOTH(finfo0,pinfo0); 2080 2081 /* 2082 * sleep some time, to demonstrate the handling of write times 2083 * doesn't depend on the time since the open 2084 */ 2085 msleep(5 * msec); 2086 2087 /* get the initial times */ 2088 GET_INFO_BOTH(finfo1,pinfo1); 2089 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); 2090 2091 /* 2092 * demonstrate that a truncate write always 2093 * updates the write time immediately 2094 */ 2095 for (i=0; i < 3; i++) { 2096 msleep(2 * msec); 2097 /* do a write */ 2098 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i); 2099 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0); 2100 if (written != 0) { 2101 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written); 2102 ret = false; 2103 goto done; 2104 } 2105 /* get the times after the write */ 2106 GET_INFO_BOTH(finfo2,pinfo2); 2107 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); 2108 finfo1 = finfo2; 2109 } 2110 2111 start = timeval_current(); 2112 end = timeval_add(&start, 7 * sec, 0); 2113 while (!timeval_expired(&end)) { 2114 /* do a write */ 2115 torture_comment(tctx, "Do a write on the file handle\n"); 2116 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 2117 if (written != 1) { 2118 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2119 ret = false; 2120 goto done; 2121 } 2122 /* get the times after the write */ 2123 GET_INFO_FILE(finfo2); 2124 2125 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { 2126 double diff = timeval_elapsed(&start); 2127 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2128 "(1sec == %.2f) (wrong!)\n", 2129 diff, sec); 2130 ret = false; 2131 break; 2132 } 2133 msleep(1 * msec); 2134 } 2135 2136 GET_INFO_BOTH(finfo2,pinfo2); 2137 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); 2138 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { 2139 torture_comment(tctx, "Server did not update write_time (correct)\n"); 2140 } 2141 2142 /* sleep */ 2143 msleep(5 * msec); 2144 2145 /* get the initial times */ 2146 GET_INFO_BOTH(finfo1,pinfo1); 2147 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2); 2148 2149 /* 2150 * demonstrate that a truncate write always 2151 * updates the write time immediately 2152 */ 2153 for (i=0; i < 3; i++) { 2154 msleep(2 * msec); 2155 /* do a write */ 2156 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i); 2157 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0); 2158 if (written != 0) { 2159 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written); 2160 ret = false; 2161 goto done; 2162 } 2163 /* get the times after the write */ 2164 GET_INFO_BOTH(finfo2,pinfo2); 2165 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); 2166 finfo1 = finfo2; 2167 } 2168 2169 /* sleep */ 2170 msleep(5 * msec); 2171 2172 GET_INFO_BOTH(finfo2,pinfo2); 2173 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); 2174 2175 /* sure any further write doesn't update the write time */ 2176 start = timeval_current(); 2177 end = timeval_add(&start, 15 * sec, 0); 2178 while (!timeval_expired(&end)) { 2179 /* do a write */ 2180 torture_comment(tctx, "Do a write on the file handle\n"); 2181 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 2182 if (written != 1) { 2183 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2184 ret = false; 2185 goto done; 2186 } 2187 /* get the times after the write */ 2188 GET_INFO_BOTH(finfo2,pinfo2); 2189 2190 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { 2191 double diff = timeval_elapsed(&start); 2192 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2193 "(1sec == %.2f) (wrong!)\n", 2194 diff, sec); 2195 ret = false; 2196 break; 2197 } 2198 msleep(1 * msec); 2199 } 2200 2201 GET_INFO_BOTH(finfo2,pinfo2); 2202 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); 2203 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { 2204 torture_comment(tctx, "Server did not update write_time (correct)\n"); 2205 } 2206 2207 /* sleep */ 2208 msleep(5 * msec); 2209 2210 GET_INFO_BOTH(finfo3,pinfo3); 2211 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); 2212 2213 /* 2214 * the close updates the write time to the time of the close 2215 * and not to the time of the last write! 2216 */ 2217 torture_comment(tctx, "Close the file handle\n"); 2218 smbcli_close(cli->tree, fnum1); 2219 fnum1 = -1; 2220 2221 GET_INFO_PATH(pinfo4); 2222 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3); 2223 2224 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) { 2225 torture_comment(tctx, "Server updated the write_time on close (correct)\n"); 2226 } 2227 2228 done: 2229 if (fnum1 != -1) 2230 smbcli_close(cli->tree, fnum1); 2231 smbcli_unlink(cli->tree, fname); 2232 smbcli_deltree(cli->tree, BASEDIR); 2233 2234 return ret; 2235} 2236 2237/* 2238 * Show only the first write updates the timestamp, and a close 2239 * after writes updates to current (I think this is the same 2240 * as test 3b. JRA). 2241 */ 2242 2243static bool test_delayed_write_update4(struct torture_context *tctx, 2244 struct smbcli_state *cli, 2245 struct smbcli_state *cli2) 2246{ 2247 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4; 2248 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5; 2249 const char *fname = BASEDIR "\\torture_file4.txt"; 2250 int fnum1 = -1; 2251 bool ret = true; 2252 ssize_t written; 2253 struct timeval start; 2254 struct timeval end; 2255 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 2256 int normal_delay = 2000000; 2257 double sec = ((double)used_delay) / ((double)normal_delay); 2258 int msec = 1000 * sec; 2259 2260 torture_comment(tctx, "\nRunning test_delayed_write_update4\n"); 2261 2262 if (!torture_setup_dir(cli, BASEDIR)) { 2263 return false; 2264 } 2265 2266 torture_comment(tctx, "Open the file handle\n"); 2267 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 2268 if (fnum1 == -1) { 2269 ret = false; 2270 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); 2271 goto done; 2272 } 2273 2274 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 2275 finfo0.basic_info.in.file.fnum = fnum1; 2276 finfo1 = finfo0; 2277 finfo2 = finfo0; 2278 finfo3 = finfo0; 2279 finfo4 = finfo0; 2280 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 2281 pinfo0.basic_info.in.file.path = fname; 2282 pinfo1 = pinfo0; 2283 pinfo2 = pinfo0; 2284 pinfo3 = pinfo0; 2285 pinfo4 = pinfo0; 2286 pinfo5 = pinfo0; 2287 2288 /* get the initial times */ 2289 GET_INFO_BOTH(finfo0,pinfo0); 2290 2291 /* sleep a bit */ 2292 msleep(5 * msec); 2293 2294 /* do a write */ 2295 torture_comment(tctx, "Do a write on the file handle\n"); 2296 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 2297 if (written != 1) { 2298 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2299 ret = false; 2300 goto done; 2301 } 2302 2303 GET_INFO_BOTH(finfo1,pinfo1); 2304 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0); 2305 2306 /* 2307 * make sure the write time is updated 2 seconds later 2308 * calcuated from the first write 2309 * (but expect upto 3 seconds extra time for a busy server) 2310 */ 2311 start = timeval_current(); 2312 end = timeval_add(&start, 5 * sec, 0); 2313 while (!timeval_expired(&end)) { 2314 /* get the times after the first write */ 2315 GET_INFO_FILE(finfo1); 2316 2317 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) { 2318 double diff = timeval_elapsed(&start); 2319 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */ 2320 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2321 "(1sec == %.2f) (wrong!)\n", 2322 diff, sec); 2323 ret = false; 2324 break; 2325 } 2326 2327 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2328 "(1sec == %.2f) (correct)\n", 2329 diff, sec); 2330 break; 2331 } 2332 msleep(0.5 * msec); 2333 } 2334 2335 GET_INFO_BOTH(finfo1,pinfo1); 2336 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0); 2337 2338 /* sure any further write doesn't update the write time */ 2339 start = timeval_current(); 2340 end = timeval_add(&start, 15 * sec, 0); 2341 while (!timeval_expired(&end)) { 2342 /* do a write */ 2343 torture_comment(tctx, "Do a write on the file handle\n"); 2344 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 2345 if (written != 1) { 2346 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2347 ret = false; 2348 goto done; 2349 } 2350 /* get the times after the write */ 2351 GET_INFO_BOTH(finfo2,pinfo2); 2352 2353 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { 2354 double diff = timeval_elapsed(&start); 2355 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2356 "(1sec == %.2f) (wrong!)\n", 2357 diff, sec); 2358 ret = false; 2359 break; 2360 } 2361 msleep(1 * msec); 2362 } 2363 2364 GET_INFO_BOTH(finfo2,pinfo2); 2365 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); 2366 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { 2367 torture_comment(tctx, "Server did not updatewrite_time (correct)\n"); 2368 } 2369 2370 /* sleep */ 2371 msleep(5 * msec); 2372 2373 GET_INFO_BOTH(finfo3,pinfo3); 2374 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); 2375 2376 /* 2377 * the close updates the write time to the time of the close 2378 * and not to the time of the last write! 2379 */ 2380 torture_comment(tctx, "Close the file handle\n"); 2381 smbcli_close(cli->tree, fnum1); 2382 fnum1 = -1; 2383 2384 GET_INFO_PATH(pinfo4); 2385 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3); 2386 2387 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) { 2388 torture_comment(tctx, "Server updated the write_time on close (correct)\n"); 2389 } 2390 2391 done: 2392 if (fnum1 != -1) 2393 smbcli_close(cli->tree, fnum1); 2394 smbcli_unlink(cli->tree, fname); 2395 smbcli_deltree(cli->tree, BASEDIR); 2396 2397 return ret; 2398} 2399 2400/* 2401 * Show writes and closes have no effect on updating times once a SETWRITETIME is done. 2402 */ 2403 2404static bool test_delayed_write_update5(struct torture_context *tctx, 2405 struct smbcli_state *cli, 2406 struct smbcli_state *cli2) 2407{ 2408 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5; 2409 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6; 2410 const char *fname = BASEDIR "\\torture_file5.txt"; 2411 int fnum1 = -1; 2412 bool ret = true; 2413 ssize_t written; 2414 struct timeval start; 2415 struct timeval end; 2416 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 2417 int normal_delay = 2000000; 2418 double sec = ((double)used_delay) / ((double)normal_delay); 2419 int msec = 1000 * sec; 2420 2421 torture_comment(tctx, "\nRunning test_delayed_write_update5\n"); 2422 2423 if (!torture_setup_dir(cli, BASEDIR)) { 2424 return false; 2425 } 2426 2427 torture_comment(tctx, "Open the file handle\n"); 2428 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 2429 if (fnum1 == -1) { 2430 ret = false; 2431 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); 2432 goto done; 2433 } 2434 2435 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 2436 finfo0.basic_info.in.file.fnum = fnum1; 2437 finfo1 = finfo0; 2438 finfo2 = finfo0; 2439 finfo3 = finfo0; 2440 finfo4 = finfo0; 2441 finfo5 = finfo0; 2442 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 2443 pinfo0.basic_info.in.file.path = fname; 2444 pinfo1 = pinfo0; 2445 pinfo2 = pinfo0; 2446 pinfo3 = pinfo0; 2447 pinfo4 = pinfo0; 2448 pinfo5 = pinfo0; 2449 pinfo6 = pinfo0; 2450 2451 /* get the initial times */ 2452 GET_INFO_BOTH(finfo0,pinfo0); 2453 2454 /* do a write */ 2455 torture_comment(tctx, "Do a write on the file handle\n"); 2456 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 2457 if (written != 1) { 2458 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2459 ret = false; 2460 goto done; 2461 } 2462 2463 GET_INFO_BOTH(finfo1,pinfo1); 2464 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); 2465 2466 torture_comment(tctx, "Set write time in the future on the file handle\n"); 2467 SET_INFO_FILE(finfo0, time(NULL) + 86400); 2468 GET_INFO_BOTH(finfo2,pinfo2); 2469 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); 2470 2471 torture_comment(tctx, "Set write time in the past on the file handle\n"); 2472 SET_INFO_FILE(finfo0, time(NULL) - 86400); 2473 GET_INFO_BOTH(finfo2,pinfo2); 2474 COMPARE_WRITE_TIME_LESS(finfo2, finfo1); 2475 2476 /* make sure the 2 second delay from the first write are canceled */ 2477 start = timeval_current(); 2478 end = timeval_add(&start, 15 * sec, 0); 2479 while (!timeval_expired(&end)) { 2480 2481 /* get the times after the first write */ 2482 GET_INFO_BOTH(finfo3,pinfo3); 2483 2484 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) { 2485 double diff = timeval_elapsed(&start); 2486 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2487 "(1sec == %.2f) (wrong!)\n", 2488 diff, sec); 2489 ret = false; 2490 break; 2491 } 2492 msleep(1 * msec); 2493 } 2494 2495 GET_INFO_BOTH(finfo3,pinfo3); 2496 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); 2497 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) { 2498 torture_comment(tctx, "Server did not update write_time (correct)\n"); 2499 } 2500 2501 /* sure any further write doesn't update the write time */ 2502 start = timeval_current(); 2503 end = timeval_add(&start, 15 * sec, 0); 2504 while (!timeval_expired(&end)) { 2505 /* do a write */ 2506 torture_comment(tctx, "Do a write on the file handle\n"); 2507 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 2508 if (written != 1) { 2509 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2510 ret = false; 2511 goto done; 2512 } 2513 /* get the times after the write */ 2514 GET_INFO_BOTH(finfo4,pinfo4); 2515 2516 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) { 2517 double diff = timeval_elapsed(&start); 2518 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2519 "(1sec == %.2f) (wrong!)\n", 2520 diff, sec); 2521 ret = false; 2522 break; 2523 } 2524 msleep(1 * msec); 2525 } 2526 2527 GET_INFO_BOTH(finfo4,pinfo4); 2528 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3); 2529 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) { 2530 torture_comment(tctx, "Server did not update write_time (correct)\n"); 2531 } 2532 2533 /* sleep */ 2534 msleep(5 * msec); 2535 2536 GET_INFO_BOTH(finfo5,pinfo5); 2537 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4); 2538 2539 /* 2540 * the close doesn't update the write time 2541 */ 2542 torture_comment(tctx, "Close the file handle\n"); 2543 smbcli_close(cli->tree, fnum1); 2544 fnum1 = -1; 2545 2546 GET_INFO_PATH(pinfo6); 2547 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5); 2548 2549 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) { 2550 torture_comment(tctx, "Server did not update the write_time on close (correct)\n"); 2551 } 2552 2553 done: 2554 if (fnum1 != -1) 2555 smbcli_close(cli->tree, fnum1); 2556 smbcli_unlink(cli->tree, fname); 2557 smbcli_deltree(cli->tree, BASEDIR); 2558 2559 return ret; 2560} 2561 2562/* 2563 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done. 2564 */ 2565 2566static bool test_delayed_write_update5b(struct torture_context *tctx, 2567 struct smbcli_state *cli, 2568 struct smbcli_state *cli2) 2569{ 2570 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5; 2571 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6; 2572 const char *fname = BASEDIR "\\torture_fileb.txt"; 2573 int fnum1 = -1; 2574 bool ret = true; 2575 ssize_t written; 2576 struct timeval start; 2577 struct timeval end; 2578 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 2579 int normal_delay = 2000000; 2580 double sec = ((double)used_delay) / ((double)normal_delay); 2581 int msec = 1000 * sec; 2582 2583 torture_comment(tctx, "\nRunning test_delayed_write_update5b\n"); 2584 2585 if (!torture_setup_dir(cli, BASEDIR)) { 2586 return false; 2587 } 2588 2589 torture_comment(tctx, "Open the file handle\n"); 2590 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 2591 if (fnum1 == -1) { 2592 ret = false; 2593 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); 2594 goto done; 2595 } 2596 2597 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 2598 finfo0.basic_info.in.file.fnum = fnum1; 2599 finfo1 = finfo0; 2600 finfo2 = finfo0; 2601 finfo3 = finfo0; 2602 finfo4 = finfo0; 2603 finfo5 = finfo0; 2604 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 2605 pinfo0.basic_info.in.file.path = fname; 2606 pinfo1 = pinfo0; 2607 pinfo2 = pinfo0; 2608 pinfo3 = pinfo0; 2609 pinfo4 = pinfo0; 2610 pinfo5 = pinfo0; 2611 pinfo6 = pinfo0; 2612 2613 /* get the initial times */ 2614 GET_INFO_BOTH(finfo0,pinfo0); 2615 2616 /* do a write */ 2617 torture_comment(tctx, "Do a write on the file handle\n"); 2618 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 2619 if (written != 1) { 2620 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2621 ret = false; 2622 goto done; 2623 } 2624 2625 GET_INFO_BOTH(finfo1,pinfo1); 2626 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); 2627 2628 torture_comment(tctx, "Set write time in the future on the file handle\n"); 2629 SET_INFO_FILE(finfo0, time(NULL) + 86400); 2630 GET_INFO_BOTH(finfo2,pinfo2); 2631 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); 2632 2633 torture_comment(tctx, "Set write time in the past on the file handle\n"); 2634 SET_INFO_FILE(finfo0, time(NULL) - 86400); 2635 GET_INFO_BOTH(finfo2,pinfo2); 2636 COMPARE_WRITE_TIME_LESS(finfo2, finfo1); 2637 2638 /* make sure the 2 second delay from the first write are canceled */ 2639 start = timeval_current(); 2640 end = timeval_add(&start, 15 * sec, 0); 2641 while (!timeval_expired(&end)) { 2642 2643 /* get the times after the first write */ 2644 GET_INFO_BOTH(finfo3,pinfo3); 2645 2646 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) { 2647 double diff = timeval_elapsed(&start); 2648 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2649 "(1sec == %.2f) (wrong!)\n", 2650 diff, sec); 2651 ret = false; 2652 break; 2653 } 2654 msleep(1 * msec); 2655 } 2656 2657 GET_INFO_BOTH(finfo3,pinfo3); 2658 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); 2659 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) { 2660 torture_comment(tctx, "Server did not update write_time (correct)\n"); 2661 } 2662 2663 /* Do any further write (truncates) update the write time ? */ 2664 start = timeval_current(); 2665 end = timeval_add(&start, 15 * sec, 0); 2666 while (!timeval_expired(&end)) { 2667 /* do a write */ 2668 torture_comment(tctx, "Do a truncate write on the file handle\n"); 2669 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0); 2670 if (written != 0) { 2671 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2672 ret = false; 2673 goto done; 2674 } 2675 /* get the times after the write */ 2676 GET_INFO_BOTH(finfo4,pinfo4); 2677 2678 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) { 2679 double diff = timeval_elapsed(&start); 2680 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2681 "(1sec == %.2f) (wrong!)\n", 2682 diff, sec); 2683 ret = false; 2684 break; 2685 } 2686 msleep(1 * msec); 2687 } 2688 2689 GET_INFO_BOTH(finfo4,pinfo4); 2690 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3); 2691 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) { 2692 torture_comment(tctx, "Server did not update write_time (correct)\n"); 2693 } 2694 2695 /* sleep */ 2696 msleep(5 * msec); 2697 2698 GET_INFO_BOTH(finfo5,pinfo5); 2699 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4); 2700 2701 /* 2702 * the close doesn't update the write time 2703 */ 2704 torture_comment(tctx, "Close the file handle\n"); 2705 smbcli_close(cli->tree, fnum1); 2706 fnum1 = -1; 2707 2708 GET_INFO_PATH(pinfo6); 2709 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5); 2710 2711 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) { 2712 torture_comment(tctx, "Server did not update the write_time on close (correct)\n"); 2713 } 2714 2715 done: 2716 if (fnum1 != -1) 2717 smbcli_close(cli->tree, fnum1); 2718 smbcli_unlink(cli->tree, fname); 2719 smbcli_deltree(cli->tree, BASEDIR); 2720 2721 return ret; 2722} 2723 2724/* 2725 * Open 2 handles on a file. Write one one and then set the 2726 * WRITE TIME explicitly on the other. Ensure the write time 2727 * update is cancelled. Ensure the write time is updated to 2728 * the close time when the non-explicit set handle is closed. 2729 * 2730 */ 2731 2732static bool test_delayed_write_update6(struct torture_context *tctx, 2733 struct smbcli_state *cli, 2734 struct smbcli_state *cli2) 2735{ 2736 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5; 2737 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7; 2738 const char *fname = BASEDIR "\\torture_file6.txt"; 2739 int fnum1 = -1; 2740 int fnum2 = -1; 2741 bool ret = true; 2742 ssize_t written; 2743 struct timeval start; 2744 struct timeval end; 2745 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); 2746 int normal_delay = 2000000; 2747 double sec = ((double)used_delay) / ((double)normal_delay); 2748 int msec = 1000 * sec; 2749 bool first = true; 2750 2751 torture_comment(tctx, "\nRunning test_delayed_write_update6\n"); 2752 2753 if (!torture_setup_dir(cli, BASEDIR)) { 2754 return false; 2755 } 2756again: 2757 torture_comment(tctx, "Open the file handle\n"); 2758 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 2759 if (fnum1 == -1) { 2760 ret = false; 2761 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); 2762 goto done; 2763 } 2764 2765 if (fnum2 == -1) { 2766 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n"); 2767 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 2768 if (fnum2 == -1) { 2769 ret = false; 2770 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); 2771 goto done; 2772 } 2773 } 2774 2775 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 2776 finfo0.basic_info.in.file.fnum = fnum1; 2777 finfo1 = finfo0; 2778 finfo2 = finfo0; 2779 finfo3 = finfo0; 2780 finfo4 = finfo0; 2781 finfo5 = finfo0; 2782 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; 2783 pinfo0.basic_info.in.file.path = fname; 2784 pinfo1 = pinfo0; 2785 pinfo2 = pinfo0; 2786 pinfo3 = pinfo0; 2787 pinfo4 = pinfo0; 2788 pinfo5 = pinfo0; 2789 pinfo6 = pinfo0; 2790 pinfo7 = pinfo0; 2791 2792 /* get the initial times */ 2793 GET_INFO_BOTH(finfo0,pinfo0); 2794 2795 /* do a write */ 2796 torture_comment(tctx, "Do a write on the file handle\n"); 2797 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 2798 if (written != 1) { 2799 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2800 ret = false; 2801 goto done; 2802 } 2803 2804 GET_INFO_BOTH(finfo1,pinfo1); 2805 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0); 2806 2807 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n"); 2808 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2); 2809 GET_INFO_BOTH(finfo2,pinfo2); 2810 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1); 2811 2812 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n"); 2813 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2); 2814 GET_INFO_BOTH(finfo2,pinfo2); 2815 COMPARE_WRITE_TIME_LESS(finfo2, finfo1); 2816 2817 /* make sure the 2 second delay from the first write are canceled */ 2818 start = timeval_current(); 2819 end = timeval_add(&start, 10 * sec, 0); 2820 while (!timeval_expired(&end)) { 2821 2822 /* get the times after the first write */ 2823 GET_INFO_BOTH(finfo3,pinfo3); 2824 2825 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) { 2826 double diff = timeval_elapsed(&start); 2827 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2828 "(1sec == %.2f) (wrong!)\n", 2829 diff, sec); 2830 ret = false; 2831 break; 2832 } 2833 msleep(1 * msec); 2834 } 2835 2836 GET_INFO_BOTH(finfo3,pinfo3); 2837 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); 2838 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) { 2839 torture_comment(tctx, "Server did not update write_time (correct)\n"); 2840 } 2841 2842 /* sure any further write doesn't update the write time */ 2843 start = timeval_current(); 2844 end = timeval_add(&start, 10 * sec, 0); 2845 while (!timeval_expired(&end)) { 2846 /* do a write */ 2847 torture_comment(tctx, "Do a write on the file handle\n"); 2848 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); 2849 if (written != 1) { 2850 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2851 ret = false; 2852 goto done; 2853 } 2854 /* get the times after the write */ 2855 GET_INFO_BOTH(finfo4,pinfo4); 2856 2857 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) { 2858 double diff = timeval_elapsed(&start); 2859 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2860 "(1sec == %.2f) (wrong!)\n", 2861 diff, sec); 2862 ret = false; 2863 break; 2864 } 2865 msleep(1 * msec); 2866 } 2867 2868 GET_INFO_BOTH(finfo4,pinfo4); 2869 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3); 2870 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) { 2871 torture_comment(tctx, "Server did not update write_time (correct)\n"); 2872 } 2873 2874 /* sleep */ 2875 msleep(5 * msec); 2876 2877 GET_INFO_BOTH(finfo5,pinfo5); 2878 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4); 2879 2880 /* 2881 * the close updates the write time to the time of the close 2882 * as the write time was set on the 2nd handle 2883 */ 2884 torture_comment(tctx, "Close the file handle\n"); 2885 smbcli_close(cli->tree, fnum1); 2886 fnum1 = -1; 2887 2888 GET_INFO_PATH(pinfo6); 2889 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5); 2890 2891 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) { 2892 torture_comment(tctx, "Server updated the write_time on close (correct)\n"); 2893 } 2894 2895 /* See what the second write handle thinks the time is ? */ 2896 finfo5.basic_info.in.file.fnum = fnum2; 2897 GET_INFO_FILE2(finfo5); 2898 COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6); 2899 2900 /* See if we have lost the sticky write time on handle2 */ 2901 msleep(3 * msec); 2902 torture_comment(tctx, "Have we lost the sticky write time ?\n"); 2903 2904 /* Make sure any further normal write doesn't update the write time */ 2905 start = timeval_current(); 2906 end = timeval_add(&start, 10 * sec, 0); 2907 while (!timeval_expired(&end)) { 2908 /* do a write */ 2909 torture_comment(tctx, "Do a write on the second file handle\n"); 2910 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1); 2911 if (written != 1) { 2912 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2913 ret = false; 2914 goto done; 2915 } 2916 /* get the times after the write */ 2917 GET_INFO_FILE2(finfo5); 2918 GET_INFO_PATH(pinfo6); 2919 2920 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) { 2921 double diff = timeval_elapsed(&start); 2922 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2923 "(1sec == %.2f) (wrong!)\n", 2924 diff, sec); 2925 ret = false; 2926 break; 2927 } 2928 msleep(1 * msec); 2929 } 2930 2931 /* What about a truncate write ? */ 2932 start = timeval_current(); 2933 end = timeval_add(&start, 10 * sec, 0); 2934 while (!timeval_expired(&end)) { 2935 /* do a write */ 2936 torture_comment(tctx, "Do a truncate write on the second file handle\n"); 2937 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0); 2938 if (written != 0) { 2939 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); 2940 ret = false; 2941 goto done; 2942 } 2943 /* get the times after the write */ 2944 GET_INFO_FILE2(finfo5); 2945 GET_INFO_PATH(pinfo6); 2946 2947 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) { 2948 double diff = timeval_elapsed(&start); 2949 torture_comment(tctx, "Server updated write_time after %.2f seconds " 2950 "(1sec == %.2f) (wrong!)\n", 2951 diff, sec); 2952 ret = false; 2953 break; 2954 } 2955 msleep(1 * msec); 2956 } 2957 2958 2959 /* keep the 2nd handle open and rerun tests */ 2960 if (first) { 2961 first = false; 2962 goto again; 2963 } 2964 2965 /* 2966 * closing the 2nd handle will cause no write time update 2967 * as the write time was explicit set on this handle 2968 */ 2969 torture_comment(tctx, "Close the 2nd file handle\n"); 2970 smbcli_close(cli2->tree, fnum2); 2971 fnum2 = -1; 2972 2973 GET_INFO_PATH(pinfo7); 2974 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6); 2975 2976 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) { 2977 torture_comment(tctx, "Server did not update the write_time on close (correct)\n"); 2978 } 2979 2980 done: 2981 if (fnum1 != -1) 2982 smbcli_close(cli->tree, fnum1); 2983 if (fnum2 != -1) 2984 smbcli_close(cli2->tree, fnum2); 2985 smbcli_unlink(cli->tree, fname); 2986 smbcli_deltree(cli->tree, BASEDIR); 2987 2988 return ret; 2989} 2990 2991static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli) 2992{ 2993 union smb_open open_parms; 2994 union smb_fileinfo finfo1, finfo2, finfo3; 2995 const char *fname = BASEDIR "\\torture_file7.txt"; 2996 NTSTATUS status; 2997 int fnum1 = -1; 2998 bool ret = true; 2999 TALLOC_CTX *mem_ctx; 3000 3001 torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n"); 3002 3003 mem_ctx = talloc_init("test_delayed_write_update7"); 3004 if (!mem_ctx) return false; 3005 3006 ZERO_STRUCT(finfo1); 3007 ZERO_STRUCT(finfo2); 3008 ZERO_STRUCT(finfo3); 3009 ZERO_STRUCT(open_parms); 3010 3011 if (!torture_setup_dir(cli, BASEDIR)) { 3012 return false; 3013 } 3014 3015 /* Create the file. */ 3016 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); 3017 if (fnum1 == -1) { 3018 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname); 3019 return false; 3020 } 3021 3022 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; 3023 finfo1.basic_info.in.file.fnum = fnum1; 3024 finfo2 = finfo1; 3025 finfo3 = finfo1; 3026 3027 /* Get the initial timestamps. */ 3028 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); 3029 3030 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); 3031 3032 /* Set the pending write time to a value with ns. */ 3033 SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1); 3034 3035 /* Get the current pending write time by fnum. */ 3036 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2); 3037 3038 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); 3039 3040 /* Ensure the time is actually different. */ 3041 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) { 3042 torture_result(tctx, TORTURE_FAIL, 3043 "setfileinfo time matches original fileinfo time"); 3044 ret = false; 3045 } 3046 3047 /* Get the current pending write time by path. */ 3048 finfo3.basic_info.in.file.path = fname; 3049 status = smb_raw_pathinfo(cli->tree, tctx, &finfo3); 3050 3051 if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) { 3052 torture_result(tctx, TORTURE_FAIL, 3053 "qpathinfo time doens't match fileinfo time"); 3054 ret = false; 3055 } 3056 3057 /* Now close the file. Re-open and check that the write 3058 time is identical to the one we wrote. */ 3059 3060 smbcli_close(cli->tree, fnum1); 3061 3062 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX; 3063 open_parms.ntcreatex.in.flags = 0; 3064 open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ; 3065 open_parms.ntcreatex.in.file_attr = 0; 3066 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE| 3067 NTCREATEX_SHARE_ACCESS_READ| 3068 NTCREATEX_SHARE_ACCESS_WRITE; 3069 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; 3070 open_parms.ntcreatex.in.create_options = 0; 3071 open_parms.ntcreatex.in.fname = fname; 3072 3073 status = smb_raw_open(cli->tree, mem_ctx, &open_parms); 3074 talloc_free(mem_ctx); 3075 3076 if (!NT_STATUS_IS_OK(status)) { 3077 torture_result(tctx, TORTURE_FAIL, 3078 "setfileinfo time matches original fileinfo time"); 3079 ret = false; 3080 } 3081 3082 fnum1 = open_parms.ntcreatex.out.file.fnum; 3083 3084 /* Check the returned time matches. */ 3085 if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) { 3086 torture_result(tctx, TORTURE_FAIL, 3087 "final open time does not match set time"); 3088 ret = false; 3089 } 3090 3091 done: 3092 3093 smbcli_close(cli->tree, fnum1); 3094 3095 smbcli_unlink(cli->tree, fname); 3096 smbcli_deltree(cli->tree, BASEDIR); 3097 return ret; 3098} 3099 3100/* 3101 testing of delayed update of write_time 3102*/ 3103struct torture_suite *torture_delay_write(void) 3104{ 3105 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE"); 3106 3107 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write); 3108 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update); 3109 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1); 3110 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a); 3111 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b); 3112 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c); 3113 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2); 3114 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3); 3115 torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a); 3116 torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b); 3117 torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c); 3118 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4); 3119 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5); 3120 torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b); 3121 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6); 3122 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7); 3123 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7); 3124 3125 return suite; 3126} 3127