pastebin - collaborative debugging tool
kpaste.net RSS


md5db script (threaded, ram-only version) with flac and large file support
Posted by Anonymous on Sun 23rd Sep 2012 14:42
raw | new post
modification of post by Anonymous (view diff)

  1. #!/usr/bin/perl
  2.  
  3. use 5.12.3;
  4. use strict;
  5. use warnings;
  6. use Cwd qw(abs_path cwd);
  7. use IO::Handle qw(autoflush);
  8. use File::Basename qw(basename dirname);
  9. use File::Path qw(make_path remove_tree);
  10. #use File::Slurp qw(read_file);
  11. use diagnostics;
  12.  
  13. use threads qw(yield);
  14. use threads::shared;
  15. use Thread::Queue;
  16. use Thread::Semaphore;
  17. #use Fcntl qw(:flock);
  18. use POSIX qw(SIGINT);
  19. use POSIX qw(ceil);
  20.  
  21. chomp(my $cores = `grep -c ^processor /proc/cpuinfo`);
  22.  
  23. my (@lib, $mode);
  24.  
  25. # Path to and name of log file to be used for logging.
  26. my $logf = "$ENV{HOME}/md5db.log";
  27.  
  28. # Delimiter used for database
  29. my $delim = "\t\*\t";
  30.  
  31. # Array for storing the actual arguments used by the script internally.
  32. # Might be useful for debugging.
  33. my @cmd = (basename($0));
  34.  
  35. # Name of database file.
  36. my $db = 'md5.db';
  37.  
  38. # Clear screen command.
  39. my $clear = `clear && echo`;
  40.  
  41. # Creating a hash that will store the names of files that are
  42. # too big to fit into RAM. We'll process them last.
  43. my %large :shared;
  44.  
  45. # Creating a few shared variables.
  46. # %err will be used for errors
  47. # $n will be used to count the number of files processed
  48. # %md5h is the database hash
  49. my %err :shared;
  50. my $n :shared = 0;
  51. my %md5h :shared;
  52. my %file_contents :shared;
  53. my $stopping :shared = 0;
  54. my $file_stack :shared = 0;
  55. my $busy :shared = 0;
  56.  
  57. my $tempfs = '/dev/shm/md5sum' . '.' . int(rand(10000));
  58. my $disk_size = 500000000;
  59.  
  60. # This will be used to control access to the logger subroutine.
  61. my $semaphore = Thread::Semaphore->new();
  62.  
  63. POSIX::sigaction(SIGINT, POSIX::SigAction->new(\&handler))
  64. || die "Error setting SIGINT handler: $!\n";
  65.  
  66. # Creating a custom POSIX signal handler.
  67. # First we create a shared variable that will work as a SIGINT switch.
  68. # Then we define the handler subroutine.
  69. # If ^C is pressed for the first time,
  70. # trip the switch and let the current threads finish.
  71. # If ^C is pressed a second time, quit directly.
  72. # Each subroutine to be used for starting threads will have to
  73. # take notice of the state of the $saw_sigint variable.
  74. my $saw_sigint :shared = 0;
  75. sub handler {
  76.         $saw_sigint = 1;
  77. }
  78.  
  79. # Open file handle for the log file
  80. open(my $LOG, '>>', $logf) or die "Can't open '$logf': $!";
  81.  
  82. # Make the $LOG file handle unbuffered for instant logging.
  83. $LOG->autoflush(1);
  84.  
  85. # Duplicate STDERR as a regular file handle
  86. open(my $SE, ">&STDERR") or die "Can't duplicate STDERR: $!";
  87.  
  88. ### Subroutine for printing usage instructions
  89. sub usage {
  90.  
  91.         my $s = basename($0);
  92.  
  93.         say <<"HELP"
  94. Usage: $s [options] [directory 1] .. [directory N]
  95.  
  96.         -help Print this help message.
  97.  
  98.         -double Check database for files that have identical
  99.         hashes.
  100.  
  101.         -import Import MD5 sums to the database from already existing
  102.         \*.MD5 files in each directory.
  103.  
  104.         -index Index new files in each directory.
  105.  
  106.         -test Test the MD5 sums of the files in the database to see if
  107.         they've changed.
  108.  
  109. HELP
  110. }
  111.  
  112. # This loop goes through the argument list as passed to the script
  113. # by the user when ran.
  114. foreach my $arg (@ARGV) {
  115.  
  116.         # If argument starts with a dash '-', interprete it as an option
  117.         if ($arg =~ /^-/) {
  118.  
  119.                 given ($arg) {
  120.                         # When '-double', set script mode to 'double', and call
  121.                         # the md5double subroutine later.
  122.                         when (/^-double$/) {
  123.                                 if (!$mode) { push(@cmd, $arg); $mode = 'double'; }
  124.                         }
  125.  
  126.                         # When '-import', set script mode to 'import', and call
  127.                         # the md5import subroutine later.
  128.                         when (/^-import$/) {
  129.                                 if (!$mode) { push(@cmd, $arg); $mode = 'import'; }
  130.                         }
  131.  
  132.                         # When '-help', set script mode to 'help', and print
  133.                         # usage instructions later.
  134.                         when (/^-help$/) {
  135.                                 if (!$mode) { push(@cmd, $arg); $mode = 'help'; }
  136.                         }
  137.  
  138.                         # When '-index', set script mode to 'index', and call
  139.                         # the md5index subroutine later.
  140.                         when (/^-index$/) {
  141.                                 if (!$mode) { push(@cmd, $arg); $mode = 'index'; }
  142.                         }
  143.  
  144.                         # When '-test', set the script mode to 'test', and call
  145.                         # the md5test subroutine later.
  146.                         when (/^-test$/) {
  147.                                 if (!$mode) { push(@cmd, $arg); $mode = 'test'; }
  148.                         }
  149.                 }
  150.         # If argument is a directory, include it in the @lib array
  151.         } elsif (-d $arg) { push(@lib, $arg); push(@cmd, $arg); }
  152. }
  153.  
  154. # If no switches were used, print usage instructions
  155. if (!@lib || !$mode || $mode eq 'help')
  156.         { usage; exit; }
  157.  
  158. #say "@cmd\n";
  159.  
  160. # Subroutine for controlling the log file
  161. # Applying a semaphore so multiple threads won't try to
  162. # access it at once, just in case ;-)
  163. sub logger {
  164.  
  165.         $semaphore->down();
  166.  
  167.         my($arg, $sw, @fn, $n);
  168.  
  169.         # Creating a variable to hold the current time.
  170.         my $now = localtime(time);
  171.  
  172.         # Array of accepted switches to this subroutine
  173.         my @larg = qw{start int gone corr diff end};
  174.  
  175.         # Loop through all the arguments passed to this subroutine
  176.         # Perform checks that decide which variable the arguments are to
  177.         # be assigned to.
  178.         CHECK: while (@_) {
  179.                         $arg = shift(@_);
  180.  
  181.                         # If $arg is a switch, set the $sw variable and start
  182.                         # the next iteration of the CHECK loop.
  183.                         foreach (@larg) {
  184.                                 if ($_ eq $arg) { $sw = $arg; next CHECK; }
  185.                         }
  186.  
  187.                         # If $arg is a number assign it to $n, if it's a file
  188.                         # add it to @fn.
  189.                         if ($arg =~ /^[0-9]*$/) { $n = $arg; }
  190.                         else { push(@fn, $arg); }
  191.         }
  192.         given ($sw) {
  193.                 # Starts writing the log.
  194.                 when ('start') {
  195.                         say $LOG "\n**** Logging started on $now ****\n";
  196.                         say $LOG "Running script in '$mode' mode.\n";
  197.                 }
  198.                 # When the script is interrupted by user pressing ^C,
  199.                 # say so in STDOUT, close the log.
  200.                 when ('int') {
  201.                         say "\nInterrupted by user!\n";
  202.                         say $LOG $n . " file(s) were tested.";
  203.                         say $LOG "\n**** Logging ended on $now ****\n";
  204.                         close $LOG or die "Can't close '$LOG': $!";
  205.                 }
  206.                 # Called when file has been deleted or moved.
  207.                 when ('gone') {
  208.                         say $LOG $fn[0] . "\n\t" . "has been (re)moved.\n";
  209.                         $err{$fn[0]} = "has been (re)moved.\n";
  210.                 }
  211.                 # Called when file has been corrupted.
  212.                 when ('corr') {
  213.                         say $LOG $fn[0] . "\n\t" .
  214.                         "has been corrupted.\n";
  215.                         $err{$fn[0]} = "has been corrupted.\n";
  216.                 }
  217.                 when ('diff') {
  218.                         say $LOG $fn[0] . "\n\t" .
  219.                                 "doesn't match the hash in database.\n";
  220.                         $err{$fn[0]} = "doesn't match the hash in database.\n";
  221.                 }
  222.                 # Called when done, and to close the log.
  223.                 # If no errors occurred write "Everything is OK!" to the log.
  224.                 # If errors occurred print the %err hash.
  225.                 # Either way, print number of files processed.
  226.                 when ('end') {
  227.                         if (!%err) {
  228.                                 say $LOG "\nEverything is OK!\n";
  229.                         } else {
  230.                                 say "\n**** Errors Occurred ****\n";
  231.                                 foreach my $fn (sort keys %err) {
  232.                                         say $SE $fn . "\n\t" . $err{$fn};
  233.                                 }
  234.                         }
  235.  
  236.                         say $LOG $n . " file(s) were tested.\n" if ($n);
  237.                         say $LOG "\n**** Logging ended on $now ****\n";
  238.                         close $LOG or die "Can't close '$LOG': $!";
  239.                 }
  240.         }
  241.         $semaphore->up();
  242. }
  243.  
  244.  
  245. # Subroutine for reading a database file into the database hash.
  246. # This is the first subroutine that will be executed and all others
  247. # depend upon it, cause without it we don't have a
  248. # database hash to work with.
  249. sub file2hash {
  250.  
  251.         # The format string which is used for parsing the database file
  252.         my $format = qr/^.*\t\*\t[[:alnum:]]{32}$/;
  253.         my (@dbfile, @gone);
  254.  
  255.         # Open the database file and read it into the @dbfile variable
  256.         open(MD5DB, '<', $db) or die "Can't open '$db': $!";
  257.         chomp (@dbfile = (<MD5DB>));
  258.         close(MD5DB) or die "Can't close '$db': $!";
  259.  
  260.         # Loop through all the lines in the database file and split
  261.         # them before storing in the database hash.
  262.         # Also, print each line to STDOUT for debug purposes
  263.         foreach my $line (@dbfile) {
  264.  
  265.                 # If current line matches the proper database file format,
  266.                 # continue.
  267.                 if ($line =~ /$format/) {
  268.  
  269.                         my ($fn, $hash) = (split(/\Q$delim/, $line));
  270.  
  271.                         # If $fn is a real file and not already in the hash,
  272.                         # continue.
  273.                         if (-f $fn && ! $md5h{$fn}) {
  274.                                         $md5h{$fn} = $hash;
  275.                                         say "$fn". "$delim" . "$hash";
  276.  
  277.                         # Saves the names of deleted or moved files in '@gone'
  278.                         # for printing at the end of this subroutine.
  279.                         } else { push(@gone, $fn); }
  280.                 }
  281.         }
  282.  
  283.         # Clears the screen, thereby scrolling past the database file print
  284.         print $clear;
  285.  
  286.         # Loops through the @gone array and logs every file name
  287.         # that's been deleted or moved.
  288.         foreach my $fn (@gone) { logger('gone', $fn); }
  289. }
  290.  
  291. # Subroutine for printing the database hash to the database file
  292. sub hash2file {
  293.  
  294.         open(MD5DB, '>', "$db") or die "Can't open '$db': $!";
  295.         # Loops through all the keys in the database hash and prints
  296.         # the entries (divided by the $delim variable) to the database file.
  297.         foreach my $k (sort keys %md5h) {
  298.                 say MD5DB $k . $delim . $md5h{$k};
  299.         }
  300.         close(MD5DB) or die "Can't close '$db': $!";
  301. }
  302.  
  303. # Subroutine for finding files
  304. # Finds all the files inside the directory name passed to it,
  305. # and sorts the output before storing it in the @files array.
  306. sub getfiles {
  307.  
  308.         my $dn = shift;
  309.         my @files;
  310.  
  311.         open(FIND, '-|', qq(find "$dn" -type f -name "*" -nowarn))
  312.         or die "Can\'t run 'find': $!";
  313.         while (my $fn = (<FIND>)) {
  314.                         chomp($fn);
  315.                         $fn =~ s($dn/)();
  316.                         push(@files, $fn) if (-f $fn && $fn ne basename($db));
  317.         }
  318.         close(FIND) or die "Can't close 'find': $!";
  319.         return @files;
  320. }
  321.  
  322. sub md5double {
  323.  
  324.         if (!keys(%md5h)) {
  325.                 say "No database file. Run the script in 'index' mode first\n" .
  326.                 "to index the files.";
  327.                 exit;
  328.         }
  329.  
  330.         # Loop through the %md5h hash and save the checksums as keys in a
  331.         # new hash called %exists. Each of those keys will hold an
  332.         # anonymous array with the matching file names.
  333.         my %exists;
  334.         foreach my $fn (keys(%md5h)) {
  335.                 my $hash = $md5h{$fn};
  336.                 if (!$exists{"$hash"}) {
  337.                         $exists{"$hash"}->[0] = $fn;
  338.                 } else {
  339.                         push(@{$exists{"$hash"}}, $fn);
  340.                 }
  341.         }
  342.  
  343.         # Loop through the %exists hash and print files that are identical,
  344.         # if any.
  345.         foreach my $hash (keys(%exists)) {
  346.                 if (scalar(@{$exists{"$hash"}}) > 1) {
  347.                         say "These files have the same hash (${hash}):";
  348.                         foreach my $fn (@{$exists{"$hash"}}) {
  349.                                 say $fn;
  350.                         }
  351.                         say "";
  352.                 }
  353.         }
  354. }
  355.  
  356. # Subroutine for finding and parsing *.MD5 files, adding the hashes
  357. # to the database hash and thereby also to the file.
  358. # It takes 1 argument:
  359. # (1) the thread queue
  360. sub md5import {
  361.  
  362.         my $md5fn = shift;
  363.  
  364.         my ($fn, $hash, @fields, @lines);
  365.  
  366.         # The format string which is used for parsing the *.MD5 files.
  367.         my $format = qr/^[[:alnum:]]{32}\s\*.*/;
  368.  
  369.         # If the file extension is *.MD5 in either upper- or
  370.         # lowercase, continue.
  371.         if ($md5fn =~ /.md5$/i) {
  372.  
  373.                 # Open the *.MD5 file and read its contents to the
  374.                 # @lines array.
  375.                 open(MD5, '<', $md5fn) or die "Can't open '$md5fn': $!";
  376.                 chomp(@lines = (<MD5>));
  377.                 close(MD5) or die "Can't close '$md5fn': $!";
  378.  
  379.                 # Loop to check that the format of the *.MD5 file really
  380.                 # is correct before proceeding.
  381.                 foreach my $line (@lines) {
  382.  
  383.                         # If format string matches the line(s) in the *.MD5
  384.                         # file, continue.
  385.                         if ($line =~ /$format/) {
  386.  
  387.                                 # Split the line so that the hash and file name go
  388.                                 # into @fields array.
  389.                                 # After that strip the path (if any) of the file
  390.                                 # name, and prepend the path of the *.MD5 file to
  391.                                 # it instead.
  392.                                 # Store hash and file name in the $hash and $fn
  393.                                 # variables for readability.
  394.                                 @fields = split(/\s\Q*/, $line, 2);
  395.                                 my $path = dirname($md5fn);
  396.                                 $hash = $fields[0];
  397.  
  398.                                 if ($path eq '.') { $fn = basename($fields[1]); }
  399.                                 else { $fn = dirname($md5fn)
  400.                                 . '/' . basename($fields[1]); }
  401.  
  402.                                 # Convert CR+LF newlines to proper LF to avoid
  403.                                 # identical file names from being interpreted as
  404.                                 # different.
  405.                                 $fn =~ s/\r//;
  406.  
  407.                                 # Unless file name already is in the database hash,
  408.                                 # print a message, add it to the hash.
  409.                                 if (! $md5h{$fn} && -f $fn) {
  410.  
  411.                                         say "$fn" . "\n\t" .
  412.                                         "Imported MD5 sum from '" .
  413.                                         basename($md5fn) .
  414.                                         "'.\n";
  415.  
  416.                                         $md5h{$fn} = $hash;
  417.  
  418.                                         # If file name is not a real file, write to
  419.                                         # the log.
  420.                                         # If file name is in database hash but the
  421.                                         # MD5 sum from the MD5 file doesn't match,
  422.                                         # print to the log.
  423.                                 } elsif (! -f $fn) { logger('gone', $fn); }
  424.                                 elsif ($md5h{$fn} ne $hash)
  425.                                 { logger('diff', $md5fn); }
  426.                         }
  427.                 }
  428.         }
  429. }
  430.  
  431. sub iquit {
  432.         my $tid = threads->tid();
  433.         say "Closing thread: " . $tid;
  434.         hash2file();
  435.         if (-d $tempfs) {
  436.                 remove_tree($tempfs) or die "Can't remove_tree '$tempfs': $!";
  437.         }
  438.         exit;
  439. }
  440.  
  441. sub md5sum {
  442.         my $fn = shift;
  443.  
  444.         while ($busy) { yield(); }
  445.  
  446.         if ($large{$fn}) {
  447.                 lock($busy);
  448.                 $busy = 1;
  449.                 chomp(my $hash = (split(/\Q */, qx/md5sum -b "$fn"/, 2))[0]);
  450.                 $busy = 0;
  451.                 return $hash;
  452.         } else {
  453.  
  454.                 my $tempfn = $tempfs . '/' . quotemeta(basename($fn)) . '.' .
  455.                 int(rand(10000));
  456.                 #say "tempfs: $tempfs";
  457.                 #say "tempfn: $tempfn";
  458.                 if (! -d $tempfs) {
  459.                         make_path($tempfs);
  460.                         if (! -d $tempfs) {
  461.                                 die "Couldnt make_path $tempfs: $!"; }
  462.                 }
  463.  
  464.                 my($hash, $md5sum_in);
  465.  
  466.                 my $pid = open($md5sum_in, '|-:raw',
  467.                 qq{md5sum -b >${tempfn}})
  468.                 or die "Can't open md5sum: $!";
  469.                 print $md5sum_in $file_contents{$fn};
  470.                 close($md5sum_in);
  471.                 waitpid( $pid, 0 );
  472.                 if ($? == 2) { iquit(); }
  473.  
  474.                 chomp($hash = (split(/\Q */, qx/cat $tempfn/, 2))[0]);
  475.                 say "$fn : $hash";
  476.                 #system(qq{rm -rf $tempfn});
  477.                 #if ($? != 0) { die "Couldn't rm '$tempfn': $!"; }
  478.  
  479.                 lock($file_stack);
  480.                 $file_stack -= length($file_contents{$fn});
  481.                 lock(%file_contents);
  482.                 delete($file_contents{$fn});
  483.                 return $hash;
  484.         }
  485. }
  486.  
  487. # Subroutine to index the files
  488. # i.e calculate and store the MD5 sums in the database hash/file.
  489. # It takes 1 argument:
  490. # (1) the thread queue
  491. sub md5index {
  492.         my $q = shift;
  493.         my $tid = threads->tid();
  494.  
  495.         # Loop through the thread que.
  496.         LOOP2: while ((my $fn = $q->dequeue_nb()) || !$stopping) {
  497.                         if ($fn) {
  498.                                 $md5h{$fn} = md5sum($fn);
  499.                                 #say "$tid $fn: done indexing (${file_stack})";
  500.                                 lock($n);
  501.                                 $n++;
  502.                         } else {
  503.                                 yield();
  504.                         }
  505.  
  506.                         # If the $saw_sigint variable has been tripped.
  507.                         # Quit this 'while' loop, thereby closing the thread.
  508.                         if ($saw_sigint == 1) {
  509.                                 iquit();
  510.                         }
  511.         }
  512.  
  513.         while (!$q->pending() && !$stopping) {
  514.                 yield();
  515.                 goto(LOOP2);
  516.         }
  517. }
  518.  
  519. # Subroutine for testing to see if the MD5 sums
  520. # in the database file are correct (i.e. have changed or not).
  521. # It takes 1 argument:
  522. # (1) the thread queue
  523. sub md5test {
  524.         my $q = shift;
  525.         my $tid = threads->tid();
  526.         my ($oldmd5, $newmd5);
  527.  
  528.         # Loop through the thread queue.
  529.         LOOP: while ((my $fn = $q->dequeue_nb()) || !$stopping) {
  530.                                 if ($fn) {
  531.                                         $newmd5 = md5sum($fn);
  532.                                         say "$tid $fn: done testing (${file_stack})";
  533.                                         $oldmd5 = $md5h{$fn};
  534.  
  535.                                         # If the new MD5 sum doesn't match the one in the hash,
  536.                                         # and file doesn't already exist in the %err hash,
  537.                                         # log it and replace the old MD5 sum in the hash with
  538.                                         # the new one.
  539.                                         if ($newmd5 ne $oldmd5 && ! $err{$fn}) {
  540.                                                 logger('diff', $fn);
  541.                                                 $md5h{$fn} = $newmd5;
  542.                                         }
  543.                                         lock($n);
  544.                                         $n++;
  545.                                 } else {
  546.                                         yield();
  547.                                 }
  548.                                 # If the $saw_sigint variable has been tripped.
  549.                                 # Quit this 'while' loop, thereby closing the thread.
  550.                                 if ($saw_sigint == 1) {
  551.                                         iquit();
  552.                                 }
  553.         }
  554.  
  555.         while (!$q->pending() && !$stopping) {
  556.                 yield();
  557.                 goto(LOOP);
  558.         }
  559. }
  560.  
  561. sub md5flac {
  562.         my $fn = shift;
  563.         my (@req, $hash);
  564.  
  565.         if ($fn =~ /.flac$/i) {
  566.  
  567.                 if (! @req) {
  568.                         chomp(@req = ( `which flac metaflac 2>&-` ));
  569.  
  570.                         if (! $req[0] || ! $req[1]) {
  571.                                 say "You need both 'flac' and 'metaflac' to test FLAC files!\n" .
  572.                                 "Using normal test method...\n";
  573.                                 @req = '0';
  574.                                 return;
  575.                         }
  576.                 }
  577.  
  578.                 unless ($req[0] = '0') {
  579.                         chomp($hash = `metaflac --show-md5sum "$fn" 2>&-`);
  580.                         if ($? != 0 && $? != 2) { logger('corr', $fn); return; }
  581.  
  582.                         system('flac', '--totally-silent', '--test', "$fn");
  583.                         if ($? != 0 && $? != 2) { logger('corr', $fn); return; }
  584.  
  585.                         return $hash;
  586.                 }
  587.         }
  588. }
  589.  
  590. # Create the thread queue.
  591. my $q = Thread::Queue->new();
  592.  
  593. # Depending on which script mode is active,
  594. # set the @run array to the correct arguments.
  595. # This will be used to start the threads later.
  596. my @run;
  597. given ($mode) {
  598.         when ('index') {
  599.                 @run = (\&md5index, $q);
  600.         }
  601.         when ('test') {
  602.                 @run = (\&md5test, $q);
  603.         }
  604. }
  605.  
  606. # If script mode is either 'import' or 'double' we'll start only
  607. # one thread, else we'll start as many as the available number of CPUs.
  608. my @threads;
  609. if ($mode ne 'import' && $mode ne 'double') {
  610.         foreach (1 .. $cores) {
  611.                 push(@threads, threads->create(@run));
  612.         }
  613. }
  614.  
  615. # This loop is where the actual action takes place
  616. # (i.e. where all the subroutines get called from)
  617. foreach my $dn (@lib) {
  618.         if (-d $dn) {
  619.                 # Changing $dn to the absolute path.
  620.                 $dn = abs_path($dn);
  621.  
  622.                 # Adding the current PATH to the $db variable.
  623.                 $db = "$dn/$db";
  624.  
  625.                 # Change directory to $dn.
  626.                 chdir($dn)
  627.                         or die "Can't change directory to '$dn': $!";
  628.  
  629.                 # Start logging.
  630.                 logger('start');
  631.  
  632.                 # If the database file is a real file,
  633.                 # store it in the database hash.
  634.                 file2hash() if (-f $db);
  635.  
  636.                 given ($mode) {
  637.                         when ('double') {
  638.                                 # Find identical files in database.
  639.                                 md5double();
  640.                         }
  641.                         when ('import') {
  642.                                 # For all the files in $dn, run md5import.
  643.                                 foreach my $fn (getfiles($dn)) { md5import($fn); }
  644.                         }
  645.                         when ('index') {
  646.                                 #say("Enqueueing files");
  647.                                 # Get all the file names and put them in the queue.
  648.                                 foreach my $fn (getfiles($dn)) {
  649.                                         while ($file_stack >= $disk_size) {
  650.                                                 my $active = threads->running();
  651.                                                 say("${active}: $file_stack > $disk_size");
  652.                                                 yield();
  653.                                         }
  654.                                         # Unless file name exists in the database hash,
  655.                                         # continue.
  656.                                         unless ($md5h{$fn}) {
  657.                                                 my $size = (stat($fn))[7];
  658.                                                 if ($size < $disk_size) {
  659.  
  660.                                                         #say("${fn}: reading");
  661.                                                         open(FILE, '<:raw', $fn) or die "Can't open '$fn': $!";
  662.                                                         sysread(FILE, $file_contents{$fn}, $size);
  663.                                                         close(FILE) or die "Can't close '$fn': $!";
  664.  
  665.                                                         #say("${fn}: queueing");
  666.                                                         lock($file_stack);
  667.                                                         $file_stack += length($file_contents{$fn});
  668.                                                         $q->enqueue($fn);
  669.                                                 } else {
  670.                                                         $large{$fn} = 1;
  671.                                                 }
  672.                                         }
  673.                                 }
  674.                         }
  675.                         when ('test') {
  676.                                 #say("Enqueueing files");
  677.                                 # Fetch all the keys for the database hash and put
  678.                                 # them in the queue.
  679.                                 foreach my $fn (sort(keys(%md5h))) {
  680.                                         while ($file_stack >= $disk_size) {
  681.                                                 say("$file_stack > $disk_size");
  682.                                                 yield();
  683.                                         }
  684.  
  685.                                         my $size = (stat($fn))[7];
  686.                                         if ($size < $disk_size) {
  687.  
  688.                                                 #say("${fn}: reading");
  689.                                                 open(FILE, '<:raw', $fn) or die "Can't open '$fn': $!";
  690.                                                 sysread(FILE, $file_contents{$fn}, $size);
  691.                                                 close(FILE) or die "Can't close '$fn': $!";
  692.  
  693.                                                 #say("${fn}: queueing");
  694.                                                 lock($file_stack);
  695.                                                 $file_stack += length($file_contents{$fn});
  696.                                                 $q->enqueue($fn);
  697.                                         } else {
  698.                                                 $large{$fn} = 1;
  699.                                         }
  700.                                 }
  701.                         }
  702.                 }
  703.  
  704.                 if (%large) {
  705.                         while ($file_stack > 0) {
  706.                                 say("$file_stack > 0");
  707.                                 yield();
  708.                         }
  709.                         foreach my $fn (sort(keys(%large))) {
  710.                                 #say("${fn}: queueing");
  711.                                 $q->enqueue($fn);
  712.                         }
  713.                 }
  714.  
  715. #     use Digest::MD5;
  716. #    $md5 = Digest::MD5->new;
  717. #    $md5->add('foo', 'bar');
  718. #    $md5->add('baz');
  719. #    $digest = $md5->hexdigest;
  720. #    print "Digest is $digest\n";
  721.  
  722.  
  723.  
  724.         }
  725. }
  726.  
  727. $stopping = 1;
  728. foreach my $t (threads->list()) { $t->join(); }
  729. #say("All threads joined");
  730.  
  731. # Print the hash to the database file and close the log
  732. hash2file();
  733. logger('end', $n);
  734.  
  735. if (-d $tempfs) {
  736.         remove_tree($tempfs) or die "Can't remove_tree '$tempfs': $!";
  737. }

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at