1#################################################################################################################################### 2# Posix Storage 3# 4# Implements storage functions for Posix-compliant file systems. 5#################################################################################################################################### 6package pgBackRestTest::Common::StoragePosix; 7 8use strict; 9use warnings FATAL => qw(all); 10use Carp qw(confess); 11use English '-no_match_vars'; 12 13use Exporter qw(import); 14 our @EXPORT = qw(); 15use File::Basename qw(basename dirname); 16use Fcntl qw(:mode); 17use File::stat qw{lstat}; 18 19use pgBackRestDoc::Common::Exception; 20use pgBackRestDoc::Common::Log; 21 22use pgBackRestTest::Common::StorageBase; 23use pgBackRestTest::Common::StoragePosixRead; 24use pgBackRestTest::Common::StoragePosixWrite; 25 26#################################################################################################################################### 27# Package name constant 28#################################################################################################################################### 29use constant STORAGE_POSIX_DRIVER => __PACKAGE__; 30 push @EXPORT, qw(STORAGE_POSIX_DRIVER); 31 32#################################################################################################################################### 33# new 34#################################################################################################################################### 35sub new 36{ 37 my $class = shift; 38 39 # Create the class hash 40 my $self = {}; 41 bless $self, $class; 42 43 # Assign function parameters, defaults, and log debug info 44 ( 45 my $strOperation, 46 $self->{bFileSync}, 47 $self->{bPathSync}, 48 ) = 49 logDebugParam 50 ( 51 __PACKAGE__ . '->new', \@_, 52 {name => 'bFileSync', optional => true, default => true}, 53 {name => 'bPathSync', optional => true, default => true}, 54 ); 55 56 # Set default temp extension 57 $self->{strTempExtension} = 'tmp'; 58 59 # Return from function and log return values if any 60 return logDebugReturn 61 ( 62 $strOperation, 63 {name => 'self', value => $self, trace => true} 64 ); 65} 66 67#################################################################################################################################### 68# exists - check if a path or file exists 69#################################################################################################################################### 70sub exists 71{ 72 my $self = shift; 73 74 # Assign function parameters, defaults, and log debug info 75 my 76 ( 77 $strOperation, 78 $strFile, 79 ) = 80 logDebugParam 81 ( 82 __PACKAGE__ . '->exists', \@_, 83 {name => 'strFile', trace => true}, 84 ); 85 86 # Does the path/file exist? 87 my $bExists = true; 88 my $oStat = lstat($strFile); 89 90 # Use stat to test if file exists 91 if (defined($oStat)) 92 { 93 # Check that it is actually a file 94 $bExists = !S_ISDIR($oStat->mode) ? true : false; 95 } 96 else 97 { 98 # If the error is not entry missing, then throw error 99 if (!$OS_ERROR{ENOENT}) 100 { 101 logErrorResult(ERROR_FILE_EXISTS, "unable to test if file '${strFile}' exists", $OS_ERROR); 102 } 103 104 $bExists = false; 105 } 106 107 # Return from function and log return values if any 108 return logDebugReturn 109 ( 110 $strOperation, 111 {name => 'bExists', value => $bExists, trace => true} 112 ); 113} 114 115#################################################################################################################################### 116# info - get information for path/file 117#################################################################################################################################### 118sub info 119{ 120 my $self = shift; 121 122 # Assign function parameters, defaults, and log debug info 123 my 124 ( 125 $strOperation, 126 $strPathFile, 127 $bIgnoreMissing, 128 ) = 129 logDebugParam 130 ( 131 __PACKAGE__ . '->info', \@_, 132 {name => 'strFile', trace => true}, 133 {name => 'bIgnoreMissing', optional => true, default => false, trace => true}, 134 ); 135 136 # Stat the path/file 137 my $oInfo = lstat($strPathFile); 138 139 # Check for errors 140 if (!defined($oInfo)) 141 { 142 if (!($OS_ERROR{ENOENT} && $bIgnoreMissing)) 143 { 144 logErrorResult($OS_ERROR{ENOENT} ? ERROR_FILE_MISSING : ERROR_FILE_OPEN, "unable to stat '${strPathFile}'", $OS_ERROR); 145 } 146 } 147 148 # Return from function and log return values if any 149 return logDebugReturn 150 ( 151 $strOperation, 152 {name => 'oInfo', value => $oInfo, trace => true} 153 ); 154} 155 156#################################################################################################################################### 157# linkCreate 158#################################################################################################################################### 159sub linkCreate 160{ 161 my $self = shift; 162 163 # Assign function parameters, defaults, and log debug info 164 my 165 ( 166 $strOperation, 167 $strSourcePathFile, 168 $strDestinationLink, 169 $bHard, 170 $bPathCreate, 171 $bIgnoreExists, 172 ) = 173 logDebugParam 174 ( 175 __PACKAGE__ . '->linkCreate', \@_, 176 {name => 'strSourcePathFile', trace => true}, 177 {name => 'strDestinationLink', trace => true}, 178 {name => 'bHard', optional=> true, default => false, trace => true}, 179 {name => 'bPathCreate', optional=> true, default => true, trace => true}, 180 {name => 'bIgnoreExists', optional => true, default => false, trace => true}, 181 ); 182 183 if (!($bHard ? link($strSourcePathFile, $strDestinationLink) : symlink($strSourcePathFile, $strDestinationLink))) 184 { 185 my $strMessage = "unable to create link '${strDestinationLink}'"; 186 187 # If parent path or source is missing 188 if ($OS_ERROR{ENOENT}) 189 { 190 # Check if source is missing 191 if (!$self->exists($strSourcePathFile)) 192 { 193 confess &log(ERROR, "${strMessage} because source '${strSourcePathFile}' does not exist", ERROR_FILE_MISSING); 194 } 195 196 if (!$bPathCreate) 197 { 198 confess &log(ERROR, "${strMessage} because parent does not exist", ERROR_PATH_MISSING); 199 } 200 201 # Create parent path 202 $self->pathCreate(dirname($strDestinationLink), {bIgnoreExists => true, bCreateParent => true}); 203 204 # Create link 205 $self->linkCreate($strSourcePathFile, $strDestinationLink, {bHard => $bHard}); 206 } 207 # Else if link already exists 208 elsif ($OS_ERROR{EEXIST}) 209 { 210 if (!$bIgnoreExists) 211 { 212 confess &log(ERROR, "${strMessage} because it already exists", ERROR_PATH_EXISTS); 213 } 214 } 215 else 216 { 217 logErrorResult(ERROR_PATH_CREATE, ${strMessage}, $OS_ERROR); 218 } 219 } 220 221 # Return from function and log return values if any 222 return logDebugReturn($strOperation); 223} 224 225#################################################################################################################################### 226# linkDestination - get destination of symlink 227#################################################################################################################################### 228sub linkDestination 229{ 230 my $self = shift; 231 232 # Assign function parameters, defaults, and log debug info 233 my 234 ( 235 $strOperation, 236 $strLink, 237 ) = 238 logDebugParam 239 ( 240 __PACKAGE__ . '->linkDestination', \@_, 241 {name => 'strLink', trace => true}, 242 ); 243 244 # Get link destination 245 my $strLinkDestination = readlink($strLink); 246 247 # Check for errors 248 if (!defined($strLinkDestination)) 249 { 250 logErrorResult( 251 $OS_ERROR{ENOENT} ? ERROR_FILE_MISSING : ERROR_FILE_OPEN, "unable to get destination for link ${strLink}", $OS_ERROR); 252 } 253 254 # Return from function and log return values if any 255 return logDebugReturn 256 ( 257 $strOperation, 258 {name => 'strLinkDestination', value => $strLinkDestination, trace => true} 259 ); 260} 261 262#################################################################################################################################### 263# list - list all files/paths in path 264#################################################################################################################################### 265sub list 266{ 267 my $self = shift; 268 269 # Assign function parameters, defaults, and log debug info 270 my 271 ( 272 $strOperation, 273 $strPath, 274 $bIgnoreMissing, 275 ) = 276 logDebugParam 277 ( 278 __PACKAGE__ . '->list', \@_, 279 {name => 'strPath', trace => true}, 280 {name => 'bIgnoreMissing', optional => true, default => false, trace => true}, 281 ); 282 283 # Working variables 284 my @stryFileList; 285 my $hPath; 286 287 # Attempt to open the path 288 if (opendir($hPath, $strPath)) 289 { 290 @stryFileList = grep(!/^(\.|\.\.)$/m, readdir($hPath)); 291 close($hPath); 292 } 293 # Else process errors 294 else 295 { 296 # Ignore the error if the file is missing and missing files should be ignored 297 if (!($OS_ERROR{ENOENT} && $bIgnoreMissing)) 298 { 299 logErrorResult($OS_ERROR{ENOENT} ? ERROR_FILE_MISSING : ERROR_FILE_OPEN, "unable to read path '${strPath}'", $OS_ERROR); 300 } 301 } 302 303 # Return from function and log return values if any 304 return logDebugReturn 305 ( 306 $strOperation, 307 {name => 'stryFileList', value => \@stryFileList, ref => true, trace => true} 308 ); 309} 310 311#################################################################################################################################### 312# manifest - build path/file/link manifest starting with base path and including all subpaths 313#################################################################################################################################### 314sub manifest 315{ 316 my $self = shift; 317 318 # Assign function parameters, defaults, and log debug info 319 my 320 ( 321 $strOperation, 322 $strPath, 323 $bIgnoreMissing, 324 $strFilter, 325 ) = 326 logDebugParam 327 ( 328 __PACKAGE__ . '->manifest', \@_, 329 {name => 'strPath', trace => true}, 330 {name => 'bIgnoreMissing', optional => true, default => false, trace => true}, 331 {name => 'strFilter', optional => true, trace => true}, 332 ); 333 334 # Generate the manifest 335 my $hManifest = {}; 336 $self->manifestRecurse($strPath, undef, 0, $hManifest, $bIgnoreMissing, $strFilter); 337 338 # Return from function and log return values if any 339 return logDebugReturn 340 ( 341 $strOperation, 342 {name => 'hManifest', value => $hManifest, trace => true} 343 ); 344} 345 346sub manifestRecurse 347{ 348 my $self = shift; 349 350 # Assign function parameters, defaults, and log debug info 351 my 352 ( 353 $strOperation, 354 $strPath, 355 $strSubPath, 356 $iDepth, 357 $hManifest, 358 $bIgnoreMissing, 359 $strFilter, 360 ) = 361 logDebugParam 362 ( 363 __PACKAGE__ . '::manifestRecurse', \@_, 364 {name => 'strPath', trace => true}, 365 {name => 'strSubPath', required => false, trace => true}, 366 {name => 'iDepth', default => 0, trace => true}, 367 {name => 'hManifest', required => false, trace => true}, 368 {name => 'bIgnoreMissing', required => false, default => false, trace => true}, 369 {name => 'strFilter', required => false, trace => true}, 370 ); 371 372 # Set operation and debug strings 373 my $strPathRead = $strPath . (defined($strSubPath) ? "/${strSubPath}" : ''); 374 my $hPath; 375 376 # If this is the top level stat the path to discover if it is actually a file 377 my $oPathInfo = $self->info($strPathRead, {bIgnoreMissing => $bIgnoreMissing}); 378 379 if (defined($oPathInfo)) 380 { 381 # If the initial path passed is a file then generate the manifest for just that file 382 if ($iDepth == 0 && !S_ISDIR($oPathInfo->mode())) 383 { 384 $hManifest->{basename($strPathRead)} = $self->manifestStat($strPathRead); 385 } 386 # Else read as a normal directory 387 else 388 { 389 # Get a list of all files in the path (including .) 390 my @stryFileList = @{$self->list($strPathRead, {bIgnoreMissing => $iDepth != 0})}; 391 unshift(@stryFileList, '.'); 392 my $hFileStat = $self->manifestList($strPathRead, \@stryFileList, $strFilter); 393 394 # Loop through all subpaths/files in the path 395 foreach my $strFile (keys(%{$hFileStat})) 396 { 397 my $strManifestFile = $iDepth == 0 ? $strFile : ($strSubPath . ($strFile eq qw(.) ? '' : "/${strFile}")); 398 $hManifest->{$strManifestFile} = $hFileStat->{$strFile}; 399 400 # Recurse into directories 401 if ($hManifest->{$strManifestFile}{type} eq 'd' && $strFile ne qw(.)) 402 { 403 $self->manifestRecurse($strPath, $strManifestFile, $iDepth + 1, $hManifest); 404 } 405 } 406 } 407 } 408 409 # Return from function and log return values if any 410 return logDebugReturn($strOperation); 411} 412 413sub manifestList 414{ 415 my $self = shift; 416 417 # Assign function parameters, defaults, and log debug info 418 my 419 ( 420 $strOperation, 421 $strPath, 422 $stryFile, 423 $strFilter, 424 ) = 425 logDebugParam 426 ( 427 __PACKAGE__ . '->manifestList', \@_, 428 {name => 'strPath', trace => true}, 429 {name => 'stryFile', trace => true}, 430 {name => 'strFilter', required => false, trace => true}, 431 ); 432 433 my $hFileStat = {}; 434 435 foreach my $strFile (@{$stryFile}) 436 { 437 if ($strFile ne '.' && defined($strFilter) && $strFilter ne $strFile) 438 { 439 next; 440 } 441 442 $hFileStat->{$strFile} = $self->manifestStat("${strPath}" . ($strFile eq qw(.) ? '' : "/${strFile}")); 443 444 if (!defined($hFileStat->{$strFile})) 445 { 446 delete($hFileStat->{$strFile}); 447 } 448 } 449 450 # Return from function and log return values if any 451 return logDebugReturn 452 ( 453 $strOperation, 454 {name => 'hFileStat', value => $hFileStat, trace => true} 455 ); 456} 457 458sub manifestStat 459{ 460 my $self = shift; 461 462 # Assign function parameters, defaults, and log debug info 463 my 464 ( 465 $strOperation, 466 $strFile, 467 ) = 468 logDebugParam 469 ( 470 __PACKAGE__ . '->manifestStat', \@_, 471 {name => 'strFile', trace => true}, 472 ); 473 474 # Stat the path/file, ignoring any that are missing 475 my $oStat = $self->info($strFile, {bIgnoreMissing => true}); 476 477 # Generate file data if stat succeeded (i.e. file exists) 478 my $hFile; 479 480 if (defined($oStat)) 481 { 482 # Check for regular file 483 if (S_ISREG($oStat->mode)) 484 { 485 $hFile->{type} = 'f'; 486 487 # Get size 488 $hFile->{size} = $oStat->size; 489 490 # Get modification time 491 $hFile->{modification_time} = $oStat->mtime; 492 } 493 # Check for directory 494 elsif (S_ISDIR($oStat->mode)) 495 { 496 $hFile->{type} = 'd'; 497 } 498 # Check for link 499 elsif (S_ISLNK($oStat->mode)) 500 { 501 $hFile->{type} = 'l'; 502 $hFile->{link_destination} = $self->linkDestination($strFile); 503 } 504 # Not a recognized type 505 else 506 { 507 confess &log(ERROR, "${strFile} is not of type directory, file, or link", ERROR_FILE_INVALID); 508 } 509 510 # Get user name 511 $hFile->{user} = getpwuid($oStat->uid); 512 513 # Get group name 514 $hFile->{group} = getgrgid($oStat->gid); 515 516 # Get mode 517 if ($hFile->{type} ne 'l') 518 { 519 $hFile->{mode} = sprintf('%04o', S_IMODE($oStat->mode)); 520 } 521 } 522 523 # Return from function and log return values if any 524 return logDebugReturn 525 ( 526 $strOperation, 527 {name => 'hFile', value => $hFile, trace => true} 528 ); 529} 530 531#################################################################################################################################### 532# move - move path/file 533#################################################################################################################################### 534sub move 535{ 536 my $self = shift; 537 538 # Assign function parameters, defaults, and log debug info 539 my 540 ( 541 $strOperation, 542 $strSourceFile, 543 $strDestinationFile, 544 $bPathCreate, 545 ) = 546 logDebugParam 547 ( 548 __PACKAGE__ . '->move', \@_, 549 {name => 'strSourceFile', trace => true}, 550 {name => 'strDestinationFile', trace => true}, 551 {name => 'bPathCreate', default => false, trace => true}, 552 ); 553 554 # Get source and destination paths 555 my $strSourcePathFile = dirname($strSourceFile); 556 my $strDestinationPathFile = dirname($strDestinationFile); 557 558 # Move the file 559 if (!rename($strSourceFile, $strDestinationFile)) 560 { 561 my $strMessage = "unable to move '${strSourceFile}'"; 562 563 # If something is missing determine if it is the source or destination 564 if ($OS_ERROR{ENOENT}) 565 { 566 if (!$self->exists($strSourceFile)) 567 { 568 logErrorResult(ERROR_FILE_MISSING, "${strMessage} because it is missing"); 569 } 570 571 if ($bPathCreate) 572 { 573 # Attempt to create the path - ignore exists here in case another process creates it first 574 $self->pathCreate($strDestinationPathFile, {bCreateParent => true, bIgnoreExists => true}); 575 576 # Try move again 577 $self->move($strSourceFile, $strDestinationFile); 578 } 579 else 580 { 581 logErrorResult(ERROR_PATH_MISSING, "${strMessage} to missing path '${strDestinationPathFile}'"); 582 } 583 } 584 # Else raise the error 585 else 586 { 587 logErrorResult(ERROR_FILE_MOVE, "${strMessage} to '${strDestinationFile}'", $OS_ERROR); 588 } 589 } 590 591 # Return from function and log return values if any 592 return logDebugReturn($strOperation); 593} 594 595#################################################################################################################################### 596# openRead - open file for reading 597#################################################################################################################################### 598sub openRead 599{ 600 my $self = shift; 601 602 # Assign function parameters, defaults, and log debug info 603 my 604 ( 605 $strOperation, 606 $strFile, 607 $bIgnoreMissing, 608 ) = 609 logDebugParam 610 ( 611 __PACKAGE__ . '->openRead', \@_, 612 {name => 'strFile', trace => true}, 613 {name => 'bIgnoreMissing', optional => true, default => false, trace => true}, 614 ); 615 616 my $oFileIO = new pgBackRestTest::Common::StoragePosixRead($self, $strFile, {bIgnoreMissing => $bIgnoreMissing}); 617 618 # Return from function and log return values if any 619 return logDebugReturn 620 ( 621 $strOperation, 622 {name => 'oFileIO', value => $oFileIO, trace => true}, 623 ); 624} 625 626#################################################################################################################################### 627# openWrite - open file for writing 628#################################################################################################################################### 629sub openWrite 630{ 631 my $self = shift; 632 633 # Assign function parameters, defaults, and log debug info 634 my 635 ( 636 $strOperation, 637 $strFile, 638 $strMode, 639 $strUser, 640 $strGroup, 641 $lTimestamp, 642 $bPathCreate, 643 $bAtomic, 644 ) = 645 logDebugParam 646 ( 647 __PACKAGE__ . '->openWrite', \@_, 648 {name => 'strFile', trace => true}, 649 {name => 'strMode', optional => true, trace => true}, 650 {name => 'strUser', optional => true, trace => true}, 651 {name => 'strGroup', optional => true, trace => true}, 652 {name => 'lTimestamp', optional => true, trace => true}, 653 {name => 'bPathCreate', optional => true, trace => true}, 654 {name => 'bAtomic', optional => true, trace => true}, 655 ); 656 657 my $oFileIO = new pgBackRestTest::Common::StoragePosixWrite( 658 $self, $strFile, 659 {strMode => $strMode, strUser => $strUser, strGroup => $strGroup, lTimestamp => $lTimestamp, bPathCreate => $bPathCreate, 660 bAtomic => $bAtomic, bSync => $self->{bFileSync}}); 661 662 # Return from function and log return values if any 663 return logDebugReturn 664 ( 665 $strOperation, 666 {name => 'oFileIO', value => $oFileIO, trace => true}, 667 ); 668} 669 670#################################################################################################################################### 671# owner - change ownership of path/file 672#################################################################################################################################### 673sub owner 674{ 675 my $self = shift; 676 677 # Assign function parameters, defaults, and log debug info 678 my 679 ( 680 $strOperation, 681 $strFilePath, 682 $strUser, 683 $strGroup, 684 ) = 685 logDebugParam 686 ( 687 __PACKAGE__ . '->owner', \@_, 688 {name => 'strFilePath', trace => true}, 689 {name => 'strUser', optional => true, trace => true}, 690 {name => 'strGroup', optional => true, trace => true}, 691 ); 692 693 # Only proceed if user or group was specified 694 if (defined($strUser) || defined($strGroup)) 695 { 696 my $strMessage = "unable to set ownership for '${strFilePath}'"; 697 my $iUserId; 698 my $iGroupId; 699 700 # If the user or group is not defined then get it by stat'ing the file. This is because the chown function requires that 701 # both user and group be set. 702 my $oStat = $self->info($strFilePath); 703 704 if (!defined($strUser)) 705 { 706 $iUserId = $oStat->uid; 707 } 708 709 if (!defined($strGroup)) 710 { 711 $iGroupId = $oStat->gid; 712 } 713 714 # Lookup user if specified 715 if (defined($strUser)) 716 { 717 $iUserId = getpwnam($strUser); 718 719 if (!defined($iUserId)) 720 { 721 logErrorResult(ERROR_FILE_OWNER, "${strMessage} because user '${strUser}' does not exist"); 722 } 723 } 724 725 # Lookup group if specified 726 if (defined($strGroup)) 727 { 728 $iGroupId = getgrnam($strGroup); 729 730 if (!defined($iGroupId)) 731 { 732 logErrorResult(ERROR_FILE_OWNER, "${strMessage} because group '${strGroup}' does not exist"); 733 } 734 } 735 736 # Set ownership on the file if the user or group would be changed 737 if ($iUserId != $oStat->uid || $iGroupId != $oStat->gid) 738 { 739 if (!chown($iUserId, $iGroupId, $strFilePath)) 740 { 741 logErrorResult(ERROR_FILE_OWNER, "${strMessage}", $OS_ERROR); 742 } 743 } 744 } 745 746 # Return from function and log return values if any 747 return logDebugReturn($strOperation); 748} 749 750#################################################################################################################################### 751# pathCreate - create path 752#################################################################################################################################### 753sub pathCreate 754{ 755 my $self = shift; 756 757 # Assign function parameters, defaults, and log debug info 758 my 759 ( 760 $strOperation, 761 $strPath, 762 $strMode, 763 $bIgnoreExists, 764 $bCreateParent, 765 ) = 766 logDebugParam 767 ( 768 __PACKAGE__ . '->pathCreate', \@_, 769 {name => 'strPath', trace => true}, 770 {name => 'strMode', optional => true, default => '0750', trace => true}, 771 {name => 'bIgnoreExists', optional => true, default => false, trace => true}, 772 {name => 'bCreateParent', optional => true, default => false, trace => true}, 773 ); 774 775 # Attempt to create the directory 776 if (!mkdir($strPath, oct($strMode))) 777 { 778 my $strMessage = "unable to create path '${strPath}'"; 779 780 # If parent path is missing 781 if ($OS_ERROR{ENOENT}) 782 { 783 if (!$bCreateParent) 784 { 785 confess &log(ERROR, "${strMessage} because parent does not exist", ERROR_PATH_MISSING); 786 } 787 788 # Create parent path 789 $self->pathCreate(dirname($strPath), {strMode => $strMode, bIgnoreExists => true, bCreateParent => $bCreateParent}); 790 791 # Create path 792 $self->pathCreate($strPath, {strMode => $strMode, bIgnoreExists => true}); 793 } 794 # Else if path already exists 795 elsif ($OS_ERROR{EEXIST}) 796 { 797 if (!$bIgnoreExists) 798 { 799 confess &log(ERROR, "${strMessage} because it already exists", ERROR_PATH_EXISTS); 800 } 801 } 802 else 803 { 804 logErrorResult(ERROR_PATH_CREATE, ${strMessage}, $OS_ERROR); 805 } 806 } 807 808 # Return from function and log return values if any 809 return logDebugReturn($strOperation); 810} 811 812#################################################################################################################################### 813# pathExists - check if path exists 814#################################################################################################################################### 815sub pathExists 816{ 817 my $self = shift; 818 819 # Assign function parameters, defaults, and log debug info 820 my 821 ( 822 $strOperation, 823 $strPath, 824 ) = 825 logDebugParam 826 ( 827 __PACKAGE__ . '->pathExists', \@_, 828 {name => 'strPath', trace => true}, 829 ); 830 831 # Does the path/file exist? 832 my $bExists = true; 833 my $oStat = lstat($strPath); 834 835 # Use stat to test if path exists 836 if (defined($oStat)) 837 { 838 # Check that it is actually a path 839 $bExists = S_ISDIR($oStat->mode) ? true : false; 840 } 841 else 842 { 843 # If the error is not entry missing, then throw error 844 if (!$OS_ERROR{ENOENT}) 845 { 846 logErrorResult(ERROR_FILE_EXISTS, "unable to test if path '${strPath}' exists", $OS_ERROR); 847 } 848 849 $bExists = false; 850 } 851 852 # Return from function and log return values if any 853 return logDebugReturn 854 ( 855 $strOperation, 856 {name => 'bExists', value => $bExists, trace => true} 857 ); 858} 859 860#################################################################################################################################### 861# pathSync - perform fsync on path 862#################################################################################################################################### 863sub pathSync 864{ 865 my $self = shift; 866 867 # Assign function parameters, defaults, and log debug info 868 my 869 ( 870 $strOperation, 871 $strPath, 872 ) = 873 logDebugParam 874 ( 875 __PACKAGE__ . '->pathSync', \@_, 876 {name => 'strPath', trace => true}, 877 ); 878 879 open(my $hPath, "<", $strPath) 880 or confess &log(ERROR, "unable to open '${strPath}' for sync", ERROR_PATH_OPEN); 881 open(my $hPathDup, ">&", $hPath) 882 or confess &log(ERROR, "unable to duplicate '${strPath}' handle for sync", ERROR_PATH_OPEN); 883 884 $hPathDup->sync() 885 or confess &log(ERROR, "unable to sync path '${strPath}'", ERROR_PATH_SYNC); 886 887 close($hPathDup); 888 close($hPath); 889 890 # Return from function and log return values if any 891 return logDebugReturn($strOperation); 892} 893 894#################################################################################################################################### 895# remove - remove path/file 896#################################################################################################################################### 897sub remove 898{ 899 my $self = shift; 900 901 # Assign function parameters, defaults, and log debug info 902 my 903 ( 904 $strOperation, 905 $xstryPathFile, 906 $bIgnoreMissing, 907 $bRecurse, 908 ) = 909 logDebugParam 910 ( 911 __PACKAGE__ . '->remove', \@_, 912 {name => 'xstryPathFile', trace => true}, 913 {name => 'bIgnoreMissing', optional => true, default => false, trace => true}, 914 {name => 'bRecurse', optional => true, default => false, trace => true}, 915 ); 916 917 # Working variables 918 my $bRemoved = true; 919 920 # Remove a tree 921 if ($bRecurse) 922 { 923 my $oManifest = $self->manifest($xstryPathFile, {bIgnoreMissing => true}); 924 925 # Iterate all files in the manifest 926 foreach my $strFile (sort({$b cmp $a} keys(%{$oManifest}))) 927 { 928 # remove directory 929 if ($oManifest->{$strFile}{type} eq 'd') 930 { 931 my $xstryPathFileRemove = $strFile eq '.' ? $xstryPathFile : "${xstryPathFile}/${strFile}"; 932 933 if (!rmdir($xstryPathFileRemove)) 934 { 935 # Throw error if this is not an ignored missing path 936 if (!($OS_ERROR{ENOENT} && $bIgnoreMissing)) 937 { 938 logErrorResult(ERROR_PATH_REMOVE, "unable to remove path '${strFile}'", $OS_ERROR); 939 } 940 } 941 } 942 # Remove file 943 else 944 { 945 $self->remove("${xstryPathFile}/${strFile}", {bIgnoreMissing => true}); 946 } 947 } 948 } 949 # Only remove the specified file 950 else 951 { 952 foreach my $strFile (ref($xstryPathFile) ? @{$xstryPathFile} : ($xstryPathFile)) 953 { 954 if (unlink($strFile) != 1) 955 { 956 $bRemoved = false; 957 958 # Throw error if this is not an ignored missing file 959 if (!($OS_ERROR{ENOENT} && $bIgnoreMissing)) 960 { 961 logErrorResult( 962 $OS_ERROR{ENOENT} ? ERROR_FILE_MISSING : ERROR_FILE_OPEN, "unable to remove file '${strFile}'", $OS_ERROR); 963 } 964 } 965 } 966 } 967 968 # Return from function and log return values if any 969 return logDebugReturn 970 ( 971 $strOperation, 972 {name => 'bRemoved', value => $bRemoved, trace => true} 973 ); 974} 975 976#################################################################################################################################### 977# Getters/Setters 978#################################################################################################################################### 979sub className {STORAGE_POSIX_DRIVER} 980sub tempExtension {shift->{strTempExtension}} 981sub tempExtensionSet {my $self = shift; $self->{strTempExtension} = shift} 982sub type {STORAGE_POSIX} 983 9841; 985