1package ProFTPD::Tests::Modules::mod_statcache::sftp; 2 3use lib qw(t/lib); 4use base qw(ProFTPD::TestSuite::Child); 5use strict; 6 7use Cwd; 8use File::Path qw(mkpath); 9use File::Spec; 10use IO::Handle; 11use POSIX qw(:fcntl_h); 12 13use ProFTPD::TestSuite::FTP; 14use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); 15 16$| = 1; 17 18my $order = 0; 19 20my $TESTS = { 21 statcache_sftp_stat_file => { 22 order => ++$order, 23 test_class => [qw(forking sftp)], 24 }, 25 26 statcache_sftp_stat_dir => { 27 order => ++$order, 28 test_class => [qw(forking sftp)], 29 }, 30 31 statcache_sftp_upload_file => { 32 order => ++$order, 33 test_class => [qw(forking sftp)], 34 }, 35 36}; 37 38sub new { 39 return shift()->SUPER::new(@_); 40} 41 42sub list_tests { 43 # Check for the required Perl modules: 44 # 45 # Net-SSH2 46 # Net-SSH2-SFTP 47 48 my $required = [qw( 49 Net::SSH2 50 Net::SSH2::SFTP 51 )]; 52 53 foreach my $req (@$required) { 54 eval "use $req"; 55 if ($@) { 56 print STDERR "\nWARNING:\n + Module '$req' not found, skipping all tests\n"; 57 58 if ($ENV{TEST_VERBOSE}) { 59 print STDERR "Unable to load $req: $@\n"; 60 } 61 62 return qw(testsuite_empty_test); 63 } 64 } 65 66 return testsuite_get_runnable_tests($TESTS); 67} 68 69sub set_up { 70 my $self = shift; 71 $self->SUPER::set_up(@_); 72 73 # Make sure that mod_sftp does not complain about permissions on the hostkey 74 # files. 75 76 my $rsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_statcache/ssh_host_rsa_key'); 77 78 unless (chmod(0400, $rsa_host_key)) { 79 die("Can't set perms on $rsa_host_key: $!"); 80 } 81} 82 83sub statcache_sftp_stat_file { 84 my $self = shift; 85 my $tmpdir = $self->{tmpdir}; 86 my $setup = test_setup($tmpdir, 'statcache'); 87 88 my $test_file = File::Spec->rel2abs("$setup->{home_dir}/test.txt"); 89 if (open(my $fh, "> $test_file")) { 90 print $fh "Hello, World!\n"; 91 unless (close($fh)) { 92 die("Can't write $test_file: $!"); 93 } 94 95 } else { 96 die("Can't open $test_file: $!"); 97 } 98 99 my $test_filemode = (stat($test_file))[2]; 100 if ($ENV{TEST_VERBOSE}) { 101 print STDERR "# $test_file mode: $test_filemode\n"; 102 } 103 104 my $test_filesize = (stat($test_file))[7]; 105 106 my $statcache_tab = File::Spec->rel2abs("$tmpdir/statcache.tab"); 107 108 my $rsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_statcache/ssh_host_rsa_key'); 109 110 my $config = { 111 PidFile => $setup->{pid_file}, 112 ScoreboardFile => $setup->{scoreboard_file}, 113 SystemLog => $setup->{log_file}, 114 TraceLog => $setup->{log_file}, 115 Trace => 'fsio:10 sftp:20 statcache:20', 116 117 AuthUserFile => $setup->{auth_user_file}, 118 AuthGroupFile => $setup->{auth_group_file}, 119 120 IfModules => { 121 'mod_delay.c' => { 122 DelayEngine => 'off', 123 }, 124 125 'mod_sftp.c' => { 126 SFTPEngine => 'on', 127 SFTPLog => $setup->{log_file}, 128 SFTPHostKey => $rsa_host_key, 129 }, 130 131 'mod_statcache.c' => { 132 StatCacheEngine => 'on', 133 StatCacheTable => $statcache_tab, 134 }, 135 }, 136 }; 137 138 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 139 $config); 140 141 # Open pipes, for use between the parent and child processes. Specifically, 142 # the child will indicate when it's done with its test by writing a message 143 # to the parent. 144 my ($rfh, $wfh); 145 unless (pipe($rfh, $wfh)) { 146 die("Can't open pipe: $!"); 147 } 148 149 require Net::SSH2; 150 151 my $ex; 152 153 # Fork child 154 $self->handle_sigchld(); 155 defined(my $pid = fork()) or die("Can't fork: $!"); 156 if ($pid) { 157 eval { 158 my $ssh2 = Net::SSH2->new(); 159 160 sleep(1); 161 162 unless ($ssh2->connect('127.0.0.1', $port)) { 163 my ($err_code, $err_name, $err_str) = $ssh2->error(); 164 die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); 165 } 166 167 unless ($ssh2->auth_password($setup->{user}, $setup->{passwd})) { 168 my ($err_code, $err_name, $err_str) = $ssh2->error(); 169 die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); 170 } 171 172 my $sftp = $ssh2->sftp(); 173 unless ($sftp) { 174 my ($err_code, $err_name, $err_str) = $ssh2->error(); 175 die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); 176 } 177 178 my $path = 'test.txt'; 179 my $attrs = $sftp->stat($path, 1); 180 unless ($attrs) { 181 my ($err_code, $err_name) = $sftp->error(); 182 die("STAT $path failed: [$err_name] ($err_code)"); 183 } 184 185 my $expected = $test_filesize; 186 my $file_size = $attrs->{size}; 187 $self->assert($expected == $file_size, 188 test_msg("Expected file size '$expected', got '$file_size'")); 189 190 $expected = $<; 191 my $file_uid = $attrs->{uid}; 192 $self->assert($expected == $file_uid, 193 test_msg("Expected file UID '$expected', got '$file_uid'")); 194 195 $expected = $(; 196 my $file_gid = $attrs->{gid}; 197 $self->assert($expected == $file_gid, 198 test_msg("Expected file GID '$expected', got '$file_gid'")); 199 200 $expected = $test_filemode; 201 my $file_mode = $attrs->{mode}; 202 $self->assert($expected == $file_mode, 203 test_msg("Expected file mode '$expected', got '$file_mode'")); 204 205 # Do the stat again; we'll check the logs to see if mod_statcache 206 # did its job. 207 $attrs = $sftp->stat($path, 1); 208 unless ($attrs) { 209 my ($err_code, $err_name) = $sftp->error(); 210 die("STAT $path failed: [$err_name] ($err_code)"); 211 } 212 213 $expected = $test_filesize; 214 $file_size = $attrs->{size}; 215 $self->assert($expected == $file_size, 216 test_msg("Expected file size '$expected', got '$file_size'")); 217 218 $expected = $<; 219 $file_uid = $attrs->{uid}; 220 $self->assert($expected == $file_uid, 221 test_msg("Expected file UID '$expected', got '$file_uid'")); 222 223 $expected = $(; 224 $file_gid = $attrs->{gid}; 225 $self->assert($expected == $file_gid, 226 test_msg("Expected file GID '$expected', got '$file_gid'")); 227 228 $expected = $test_filemode; 229 $file_mode = $attrs->{mode}; 230 $self->assert($expected == $file_mode, 231 test_msg("Expected file mode '$expected', got '$file_mode'")); 232 233 $sftp = undef; 234 $ssh2->disconnect(); 235 236 # Now connect again, do another stat, and see if we're still using 237 # the cached entry. 238 $ssh2 = Net::SSH2->new(); 239 unless ($ssh2->connect('127.0.0.1', $port)) { 240 my ($err_code, $err_name, $err_str) = $ssh2->error(); 241 die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); 242 } 243 244 unless ($ssh2->auth_password($setup->{user}, $setup->{passwd})) { 245 my ($err_code, $err_name, $err_str) = $ssh2->error(); 246 die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); 247 } 248 249 $sftp = $ssh2->sftp(); 250 unless ($sftp) { 251 my ($err_code, $err_name, $err_str) = $ssh2->error(); 252 die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); 253 } 254 255 $attrs = $sftp->stat($path, 1); 256 unless ($attrs) { 257 my ($err_code, $err_name) = $sftp->error(); 258 die("STAT $path failed: [$err_name] ($err_code)"); 259 } 260 261 $expected = $test_filesize; 262 $file_size = $attrs->{size}; 263 $self->assert($expected == $file_size, 264 test_msg("Expected file size '$expected', got '$file_size'")); 265 266 $expected = $<; 267 $file_uid = $attrs->{uid}; 268 $self->assert($expected == $file_uid, 269 test_msg("Expected file UID '$expected', got '$file_uid'")); 270 271 $expected = $(; 272 $file_gid = $attrs->{gid}; 273 $self->assert($expected == $file_gid, 274 test_msg("Expected file GID '$expected', got '$file_gid'")); 275 276 $expected = $test_filemode; 277 $file_mode = $attrs->{mode}; 278 $self->assert($expected == $file_mode, 279 test_msg("Expected file mode '$expected', got '$file_mode'")); 280 281 $sftp = undef; 282 $ssh2->disconnect(); 283 }; 284 if ($@) { 285 $ex = $@; 286 } 287 288 $wfh->print("done\n"); 289 $wfh->flush(); 290 291 } else { 292 eval { server_wait($setup->{config_file}, $rfh) }; 293 if ($@) { 294 warn($@); 295 exit 1; 296 } 297 298 exit 0; 299 } 300 301 # Stop server 302 server_stop($setup->{pid_file}); 303 $self->assert_child_ok($pid); 304 305 eval { 306 if (open(my $fh, "< $setup->{log_file}")) { 307 my $adding_entry = 0; 308 my $cached_stat = 0; 309 my $cached_lstat = 0; 310 311 if ($^O eq 'darwin') { 312 # MacOSX-specific hack 313 $test_file = '/private' . $test_file; 314 } 315 316 while (my $line = <$fh>) { 317 chomp($line); 318 319 if ($ENV{TEST_VERBOSE}) { 320 print STDERR "# line: $line\n"; 321 } 322 323 if ($line =~ /<statcache:9>/) { 324 if ($line =~ /adding entry.*?type file/) { 325 $adding_entry++; 326 next; 327 } 328 } 329 330 if ($line =~ /<statcache:11>/) { 331 if ($cached_stat == 0 && 332 $line =~ /using cached stat.*?path '$test_file'/) { 333 $cached_stat++; 334 next; 335 } 336 337 if ($cached_lstat == 0 && 338 $line =~ /using cached lstat.*?path '$test_file'/) { 339 $cached_lstat++; 340 next; 341 } 342 } 343 344 if ($adding_entry >= 2 && 345 $cached_stat == 1 && 346 $cached_lstat == 1) { 347 last; 348 } 349 } 350 351 close($fh); 352 353 $self->assert($adding_entry >= 2 && 354 $cached_stat == 1 && 355 $cached_lstat == 1, 356 test_msg("Did not see expected 'statcache' TraceLog messages")); 357 358 } else { 359 die("Can't read $setup->{log_file}: $!"); 360 } 361 }; 362 if ($@) { 363 $ex = $@; 364 } 365 366 test_cleanup($setup->{log_file}, $ex); 367} 368 369sub statcache_sftp_stat_dir { 370 my $self = shift; 371 my $tmpdir = $self->{tmpdir}; 372 my $setup = test_setup($tmpdir, 'statcache'); 373 374 my $test_dir = File::Spec->rel2abs("$setup->{home_dir}/test.d"); 375 mkpath($test_dir); 376 377 my $test_filemode = (stat($test_dir))[2]; 378 if ($ENV{TEST_VERBOSE}) { 379 print STDERR "# $test_dir mode: $test_filemode\n"; 380 } 381 382 my $statcache_tab = File::Spec->rel2abs("$tmpdir/statcache.tab"); 383 384 my $rsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_statcache/ssh_host_rsa_key'); 385 386 my $config = { 387 PidFile => $setup->{pid_file}, 388 ScoreboardFile => $setup->{scoreboard_file}, 389 SystemLog => $setup->{log_file}, 390 TraceLog => $setup->{log_file}, 391 Trace => 'fsio:10 sftp:20 statcache:20', 392 393 AuthUserFile => $setup->{auth_user_file}, 394 AuthGroupFile => $setup->{auth_group_file}, 395 396 IfModules => { 397 'mod_delay.c' => { 398 DelayEngine => 'off', 399 }, 400 401 'mod_sftp.c' => { 402 SFTPEngine => 'on', 403 SFTPLog => $setup->{log_file}, 404 SFTPHostKey => $rsa_host_key, 405 }, 406 407 'mod_statcache.c' => { 408 StatCacheEngine => 'on', 409 StatCacheTable => $statcache_tab, 410 }, 411 }, 412 }; 413 414 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 415 $config); 416 417 # Open pipes, for use between the parent and child processes. Specifically, 418 # the child will indicate when it's done with its test by writing a message 419 # to the parent. 420 my ($rfh, $wfh); 421 unless (pipe($rfh, $wfh)) { 422 die("Can't open pipe: $!"); 423 } 424 425 require Net::SSH2; 426 427 my $ex; 428 429 # Fork child 430 $self->handle_sigchld(); 431 defined(my $pid = fork()) or die("Can't fork: $!"); 432 if ($pid) { 433 eval { 434 my $ssh2 = Net::SSH2->new(); 435 436 sleep(1); 437 438 unless ($ssh2->connect('127.0.0.1', $port)) { 439 my ($err_code, $err_name, $err_str) = $ssh2->error(); 440 die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); 441 } 442 443 unless ($ssh2->auth_password($setup->{user}, $setup->{passwd})) { 444 my ($err_code, $err_name, $err_str) = $ssh2->error(); 445 die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); 446 } 447 448 my $sftp = $ssh2->sftp(); 449 unless ($sftp) { 450 my ($err_code, $err_name, $err_str) = $ssh2->error(); 451 die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); 452 } 453 454 my $path = 'test.d'; 455 my $attrs = $sftp->stat($path, 1); 456 unless ($attrs) { 457 my ($err_code, $err_name) = $sftp->error(); 458 die("STAT $path failed: [$err_name] ($err_code)"); 459 } 460 461 my $expected = $<; 462 my $file_uid = $attrs->{uid}; 463 $self->assert($expected == $file_uid, 464 test_msg("Expected file UID '$expected', got '$file_uid'")); 465 466 $expected = $(; 467 my $file_gid = $attrs->{gid}; 468 $self->assert($expected == $file_gid, 469 test_msg("Expected file GID '$expected', got '$file_gid'")); 470 471 $expected = $test_filemode; 472 my $file_mode = $attrs->{mode}; 473 $self->assert($expected == $file_mode, 474 test_msg("Expected file mode '$expected', got '$file_mode'")); 475 476 # Do the stat again; we'll check the logs to see if mod_statcache 477 # did its job. 478 $attrs = $sftp->stat($path, 1); 479 unless ($attrs) { 480 my ($err_code, $err_name) = $sftp->error(); 481 die("STAT $path failed: [$err_name] ($err_code)"); 482 } 483 484 $expected = $<; 485 $file_uid = $attrs->{uid}; 486 $self->assert($expected == $file_uid, 487 test_msg("Expected file UID '$expected', got '$file_uid'")); 488 489 $expected = $(; 490 $file_gid = $attrs->{gid}; 491 $self->assert($expected == $file_gid, 492 test_msg("Expected file GID '$expected', got '$file_gid'")); 493 494 $expected = $test_filemode; 495 $file_mode = $attrs->{mode}; 496 $self->assert($expected == $file_mode, 497 test_msg("Expected file mode '$expected', got '$file_mode'")); 498 499 $sftp = undef; 500 $ssh2->disconnect(); 501 502 # Now connect again, do another stat, and see if we're still using 503 # the cached entry. 504 $ssh2 = Net::SSH2->new(); 505 unless ($ssh2->connect('127.0.0.1', $port)) { 506 my ($err_code, $err_name, $err_str) = $ssh2->error(); 507 die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); 508 } 509 510 unless ($ssh2->auth_password($setup->{user}, $setup->{passwd})) { 511 my ($err_code, $err_name, $err_str) = $ssh2->error(); 512 die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); 513 } 514 515 $sftp = $ssh2->sftp(); 516 unless ($sftp) { 517 my ($err_code, $err_name, $err_str) = $ssh2->error(); 518 die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); 519 } 520 521 $attrs = $sftp->stat($path, 1); 522 unless ($attrs) { 523 my ($err_code, $err_name) = $sftp->error(); 524 die("STAT $path failed: [$err_name] ($err_code)"); 525 } 526 527 $expected = $<; 528 $file_uid = $attrs->{uid}; 529 $self->assert($expected == $file_uid, 530 test_msg("Expected file UID '$expected', got '$file_uid'")); 531 532 $expected = $(; 533 $file_gid = $attrs->{gid}; 534 $self->assert($expected == $file_gid, 535 test_msg("Expected file GID '$expected', got '$file_gid'")); 536 537 $expected = $test_filemode; 538 $file_mode = $attrs->{mode}; 539 $self->assert($expected == $file_mode, 540 test_msg("Expected file mode '$expected', got '$file_mode'")); 541 542 $sftp = undef; 543 $ssh2->disconnect(); 544 }; 545 if ($@) { 546 $ex = $@; 547 } 548 549 $wfh->print("done\n"); 550 $wfh->flush(); 551 552 } else { 553 eval { server_wait($setup->{config_file}, $rfh) }; 554 if ($@) { 555 warn($@); 556 exit 1; 557 } 558 559 exit 0; 560 } 561 562 # Stop server 563 server_stop($setup->{pid_file}); 564 $self->assert_child_ok($pid); 565 566 eval { 567 if (open(my $fh, "< $setup->{log_file}")) { 568 my $adding_entry = 0; 569 my $cached_stat = 0; 570 my $cached_lstat = 0; 571 572 if ($^O eq 'darwin') { 573 # MacOSX-specific hack 574 $test_dir = '/private' . $test_dir; 575 } 576 577 while (my $line = <$fh>) { 578 chomp($line); 579 580 if ($ENV{TEST_VERBOSE}) { 581 print STDERR "# line: $line\n"; 582 } 583 584 if ($line =~ /<statcache:9>/) { 585 if ($line =~ /adding entry.*?type dir/) { 586 $adding_entry++; 587 next; 588 } 589 } 590 591 if ($line =~ /<statcache:11>/) { 592 if ($cached_stat == 0 && 593 $line =~ /using cached stat.*?path '$test_dir'/) { 594 $cached_stat++; 595 next; 596 } 597 598 if ($cached_lstat == 0 && 599 $line =~ /using cached lstat.*?path '$test_dir'/) { 600 $cached_lstat++; 601 next; 602 } 603 } 604 605 if ($adding_entry >= 2 && 606 $cached_stat == 1 && 607 $cached_lstat == 1) { 608 last; 609 } 610 } 611 612 close($fh); 613 614 $self->assert($adding_entry >= 2 && 615 $cached_stat == 1 && 616 $cached_lstat == 1, 617 test_msg("Did not see expected 'statcache' TraceLog messages")); 618 619 } else { 620 die("Can't read $setup->{log_file}: $!"); 621 } 622 }; 623 if ($@) { 624 $ex = $@; 625 } 626 627 test_cleanup($setup->{log_file}, $ex); 628} 629 630sub statcache_sftp_upload_file { 631 my $self = shift; 632 my $tmpdir = $self->{tmpdir}; 633 my $setup = test_setup($tmpdir, 'statcache'); 634 635 my $test_file = File::Spec->rel2abs("$setup->{home_dir}/test.txt"); 636 my $test_filemode = 33188; 637 my $test_filesize = 14; 638 639 my $statcache_tab = File::Spec->rel2abs("$tmpdir/statcache.tab"); 640 641 my $rsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_statcache/ssh_host_rsa_key'); 642 643 my $config = { 644 PidFile => $setup->{pid_file}, 645 ScoreboardFile => $setup->{scoreboard_file}, 646 SystemLog => $setup->{log_file}, 647 TraceLog => $setup->{log_file}, 648 Trace => 'fsio:10 sftp:20 statcache:20', 649 650 AuthUserFile => $setup->{auth_user_file}, 651 AuthGroupFile => $setup->{auth_group_file}, 652 653 IfModules => { 654 'mod_delay.c' => { 655 DelayEngine => 'off', 656 }, 657 658 'mod_sftp.c' => { 659 SFTPEngine => 'on', 660 SFTPLog => $setup->{log_file}, 661 SFTPHostKey => $rsa_host_key, 662 }, 663 664 'mod_statcache.c' => { 665 StatCacheEngine => 'on', 666 StatCacheTable => $statcache_tab, 667 }, 668 }, 669 }; 670 671 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 672 $config); 673 674 # Open pipes, for use between the parent and child processes. Specifically, 675 # the child will indicate when it's done with its test by writing a message 676 # to the parent. 677 my ($rfh, $wfh); 678 unless (pipe($rfh, $wfh)) { 679 die("Can't open pipe: $!"); 680 } 681 682 require Net::SSH2; 683 684 my $ex; 685 686 # Fork child 687 $self->handle_sigchld(); 688 defined(my $pid = fork()) or die("Can't fork: $!"); 689 if ($pid) { 690 eval { 691 my $ssh2 = Net::SSH2->new(); 692 693 sleep(1); 694 695 unless ($ssh2->connect('127.0.0.1', $port)) { 696 my ($err_code, $err_name, $err_str) = $ssh2->error(); 697 die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); 698 } 699 700 unless ($ssh2->auth_password($setup->{user}, $setup->{passwd})) { 701 my ($err_code, $err_name, $err_str) = $ssh2->error(); 702 die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); 703 } 704 705 my $sftp = $ssh2->sftp(); 706 unless ($sftp) { 707 my ($err_code, $err_name, $err_str) = $ssh2->error(); 708 die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); 709 } 710 711 my $path = 'test.txt'; 712 my $fh = $sftp->open($path, O_WRONLY|O_CREAT|O_TRUNC, 0644); 713 unless ($fh) { 714 my ($err_code, $err_name) = $sftp->error(); 715 die("Can't open test.txt: [$err_name] ($err_code)"); 716 } 717 718 print $fh "Hello, World!\n"; 719 720 # To issue the FXP_CLOSE, we have to explicitly destroy the filehandle 721 $fh = undef; 722 723 my $attrs = $sftp->stat($path, 1); 724 unless ($attrs) { 725 my ($err_code, $err_name) = $sftp->error(); 726 die("STAT $path failed: [$err_name] ($err_code)"); 727 } 728 729 my $expected = $test_filesize; 730 my $file_size = $attrs->{size}; 731 $self->assert($expected == $file_size, 732 test_msg("Expected file size '$expected', got '$file_size'")); 733 734 $expected = $<; 735 my $file_uid = $attrs->{uid}; 736 $self->assert($expected == $file_uid, 737 test_msg("Expected file UID '$expected', got '$file_uid'")); 738 739 $expected = $(; 740 my $file_gid = $attrs->{gid}; 741 $self->assert($expected == $file_gid, 742 test_msg("Expected file GID '$expected', got '$file_gid'")); 743 744 $expected = $test_filemode; 745 my $file_mode = $attrs->{mode}; 746 $self->assert($expected == $file_mode, 747 test_msg("Expected file mode '$expected', got '$file_mode'")); 748 749 # Do the stat again; we'll check the logs to see if mod_statcache 750 # did its job. 751 $attrs = $sftp->stat($path, 1); 752 unless ($attrs) { 753 my ($err_code, $err_name) = $sftp->error(); 754 die("STAT $path failed: [$err_name] ($err_code)"); 755 } 756 757 $expected = $test_filesize; 758 $file_size = $attrs->{size}; 759 $self->assert($expected == $file_size, 760 test_msg("Expected file size '$expected', got '$file_size'")); 761 762 $expected = $<; 763 $file_uid = $attrs->{uid}; 764 $self->assert($expected == $file_uid, 765 test_msg("Expected file UID '$expected', got '$file_uid'")); 766 767 $expected = $(; 768 $file_gid = $attrs->{gid}; 769 $self->assert($expected == $file_gid, 770 test_msg("Expected file GID '$expected', got '$file_gid'")); 771 772 $expected = $test_filemode; 773 $file_mode = $attrs->{mode}; 774 $self->assert($expected == $file_mode, 775 test_msg("Expected file mode '$expected', got '$file_mode'")); 776 777 $sftp = undef; 778 $ssh2->disconnect(); 779 780 # Now connect again, do another stat, and see if we're still using 781 # the cached entry. 782 $ssh2 = Net::SSH2->new(); 783 unless ($ssh2->connect('127.0.0.1', $port)) { 784 my ($err_code, $err_name, $err_str) = $ssh2->error(); 785 die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); 786 } 787 788 unless ($ssh2->auth_password($setup->{user}, $setup->{passwd})) { 789 my ($err_code, $err_name, $err_str) = $ssh2->error(); 790 die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); 791 } 792 793 $sftp = $ssh2->sftp(); 794 unless ($sftp) { 795 my ($err_code, $err_name, $err_str) = $ssh2->error(); 796 die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); 797 } 798 799 $attrs = $sftp->stat($path, 1); 800 unless ($attrs) { 801 my ($err_code, $err_name) = $sftp->error(); 802 die("STAT $path failed: [$err_name] ($err_code)"); 803 } 804 805 $expected = $test_filesize; 806 $file_size = $attrs->{size}; 807 $self->assert($expected == $file_size, 808 test_msg("Expected file size '$expected', got '$file_size'")); 809 810 $expected = $<; 811 $file_uid = $attrs->{uid}; 812 $self->assert($expected == $file_uid, 813 test_msg("Expected file UID '$expected', got '$file_uid'")); 814 815 $expected = $(; 816 $file_gid = $attrs->{gid}; 817 $self->assert($expected == $file_gid, 818 test_msg("Expected file GID '$expected', got '$file_gid'")); 819 820 $expected = $test_filemode; 821 $file_mode = $attrs->{mode}; 822 $self->assert($expected == $file_mode, 823 test_msg("Expected file mode '$expected', got '$file_mode'")); 824 825 $sftp = undef; 826 $ssh2->disconnect(); 827 }; 828 if ($@) { 829 $ex = $@; 830 } 831 832 $wfh->print("done\n"); 833 $wfh->flush(); 834 835 } else { 836 eval { server_wait($setup->{config_file}, $rfh) }; 837 if ($@) { 838 warn($@); 839 exit 1; 840 } 841 842 exit 0; 843 } 844 845 # Stop server 846 server_stop($setup->{pid_file}); 847 $self->assert_child_ok($pid); 848 849 eval { 850 if (open(my $fh, "< $setup->{log_file}")) { 851 my $adding_entry = 0; 852 my $cached_stat = 0; 853 my $cached_lstat = 0; 854 855 if ($^O eq 'darwin') { 856 # MacOSX-specific hack 857 $test_file = '/private' . $test_file; 858 } 859 860 while (my $line = <$fh>) { 861 chomp($line); 862 863 if ($ENV{TEST_VERBOSE}) { 864 print STDERR "# line: $line\n"; 865 } 866 867 if ($line =~ /<statcache:9>/) { 868 if ($line =~ /adding entry.*?type file/) { 869 $adding_entry++; 870 next; 871 } 872 } 873 874 if ($line =~ /<statcache:11>/) { 875 if ($cached_stat == 0 && 876 $line =~ /using cached stat.*?path '$test_file'/) { 877 $cached_stat++; 878 next; 879 } 880 881 if ($cached_lstat == 0 && 882 $line =~ /using cached lstat.*?path '$test_file'/) { 883 $cached_lstat++; 884 next; 885 } 886 } 887 888 if ($adding_entry >= 2 && 889 $cached_stat == 1 && 890 $cached_lstat == 1) { 891 last; 892 } 893 } 894 895 close($fh); 896 897 $self->assert($adding_entry >= 2 && 898 $cached_stat == 1 && 899 $cached_lstat == 1, 900 test_msg("Did not see expected 'statcache' TraceLog messages")); 901 902 } else { 903 die("Can't read $setup->{log_file}: $!"); 904 } 905 }; 906 if ($@) { 907 $ex = $@; 908 } 909 910 test_cleanup($setup->{log_file}, $ex); 911} 912 9131; 914