File-DirSync

 view release on metacpan or  search on metacpan

lib/File/DirSync.pm  view on Meta::CPAN

        print "$dst: Removing tree\n" if $self->{verbose};
        $self->rmtree($dst) or warn "$dst: Failed to rmtree!\n";
      } elsif (-e $dst) {
        # Regular file (or something else) needs to go
        print "$dst: Removing\n" if $self->{verbose};
        unlink $dst or warn "$dst: Failed to purge: $!\n";
      }
      if (-l $dst || -e $dst) {
        warn "$dst: Still exists after wipe?!!!\n";
      }
      $point = $1 if $point =~ /^(.+)$/; # Taint
      # Point to the same place that $src points to
      print "$dst -> $point\n" if $self->{verbose};
      symlink $point, $dst or warn "$dst: Failed to create symlink: $!\n";
      $self->_op(5) if $self->{_gentle_percent};
      return;
    }
  }

  if ($self->{nocache} && -d _) {
    $size_dst = -1;
  }
  # Short circuit and kick out the common case:
  # Nothing to do if the timestamp and size match
  if ( defined ( $when_src && $when_dst && $size_src && $size_dst) &&
       $when_src == $when_dst && $size_src == $size_dst ) {
    push @{ $self->{_tracking}->{skipped} }, $dst if $self->{_tracking};
    return;
  }

  # Regular File Check
  if (-f _) {
    # Source is a plain file
    if (-l $dst) {
      # Dest is a symlink
      print "$dst: Removing symlink\n" if $self->{verbose};
      unlink $dst or warn "$dst: Failed to remove symlink: $!\n";
      $self->_op if $self->{_gentle_percent};
    } elsif (-d _) {
      # Wipe directory
      print "$dst: Removing tree\n" if $self->{verbose};
      $self->rmtree($dst) or warn "$dst: Failed to rmtree: $!\n";
    }
    $self->_op if $self->{_gentle_percent};
    $0 = "$self->{proctitle} - copying: $src => $dst" if $self->{proctitle};
    if ($self->copy($src, $dst)) {
      print "$dst: Updated\n" if $self->{verbose};
      push @{ $self->{_tracking}->{updated} }, $dst if $self->{_tracking};
    } else {
      warn "$dst: Failed to copy: $!\n";
    }
    if (!-e $dst) {
      warn "$dst: Never created?!!!\n";
      push @{ $self->{_tracking}->{failed} }, $dst if $self->{_tracking};
      $self->_op if $self->{_gentle_percent};
      return;
    }
    # Force permissions to match the source
    chmod( $stat_src[2] & 0777, $dst) or warn "$dst: Failed to chmod: $!\n";
    # Force user and group ownership to match the source
    chown( $stat_src[4], $stat_src[5], $dst) or warn "$dst: Failed to chown: $!\n";
    # Force timestamp to match the source.
    utime $when_src, $when_src, $dst or warn "$dst: Failed to utime: $!\n";
    $self->_op(4) if $self->{_gentle_percent};
    return;
  }

  # Missing Check
  if (!-e _) {
    # The source does not exist
    # The destination must also not exist
    print "$dst: Removing\n" if $self->{verbose};
    $0 = "$self->{proctitle} - removing: $dst" if $self->{proctitle};
    if ( $self->rmtree($dst) ) {
      push @{ $self->{_tracking}->{removed} }, $dst if $self->{_tracking};
    } else {
      push @{ $self->{_tracking}->{failed} }, $dst if $self->{_tracking};
      warn "$dst: Failed to rmtree!\n";
    }
    return;
  }

  # Finally, the recursive Directory Check
  if (-d _) {
    # Source is a directory
    if (-l $dst) {
      # Dest is a symlink
      print "$dst: Removing symlink\n" if $self->{verbose};
      unlink $dst or warn "$dst: Failed to remove symlink: $!\n";
      $self->_op if $self->{_gentle_percent};
    }
    if (-f $dst) {
      # Dest is a plain file
      # It must be wiped
      print "$dst: Removing file\n" if $self->{verbose};
      if ( unlink($dst) ) {
        push @{ $self->{_tracking}->{removed} }, $dst if $self->{_tracking};
      } else {
        push @{ $self->{_tracking}->{failed} }, $dst if $self->{_tracking};
        warn "$dst: Failed to unlink file: $!\n";
      }
      $self->_op if $self->{_gentle_percent};
    }
    if (!-d $dst) {
      if ( mkdir $dst, 0755 ) {
        push @{ $self->{_tracking}->{updated} }, $dst if $self->{_tracking};
      } else {
        push @{ $self->{_tracking}->{failed} }, $dst if $self->{_tracking};
        warn "$dst: Failed to mkdir: $!\n";
      }
      $self->_op if $self->{_gentle_percent};
    }
    -d $dst or warn "$dst: Destination directory cannot exist?\n";
    $self->_op(4) if $self->{_gentle_percent};

    # If nocache() was not specified, then it is okay
    # skip this directory if the timestamps match.
    if (!$self->{nocache}) {
      # (The directory sizes do not really matter.)
      # If the timestamps are the same, nothing to do
      # because rebuild() will ensure that the directory
      # timestamp is the most recent within its
      # entire descent.
      if ( defined ( $when_src && $when_dst) &&
           $when_src == $when_dst ) {
        push @{ $self->{_tracking}->{skipped} }, $dst if $self->{_tracking};
        return;
      }
    }

    print "$dst: Scanning...\n" if $self->{verbose};

    # I know the source is a directory.
    # I know the destination is also a directory
    # which has a different timestamp than the
    # source.  All nodes within both directories
    # must be scanned and updated accordingly.

    my ($handle, $node, %nodes);

    $handle = do { local *FH; };
    $0 = "$self->{proctitle} - src: $src" if $self->{proctitle};
    return unless opendir($handle, $src);
    while (defined ($node = readdir($handle))) {
      next if $node =~ /^\.\.?$/;
      next if $self->{ignore}->{$node};
      next if ($self->{localmode} &&
               !-l "$src/$node" &&
               -d _);
      $nodes{$node} = 1;
      $self->_op if $self->{_gentle_percent};
    }
    closedir($handle);

    $handle = do { local *FH; };
    $0 = "$self->{proctitle} - dst: $dst" if $self->{proctitle};
    return unless opendir($handle, $dst);
    while (defined ($node = readdir($handle))) {
      next if $node =~ /^\.\.?$/;
      next if $self->{ignore}->{$node};
      next if ($self->{localmode} &&
               !-l "$src/$node" &&
               -d _);
      $nodes{$node} = 1;
      $self->_op if $self->{_gentle_percent};
    }
    closedir($handle);

    $0 = "$self->{proctitle} - syncing: $src => $dst" if $self->{proctitle};
    # %nodes is now a union set of all nodes
    # in both the source and destination.
    # Recursively call myself for each node.
    foreach $node (keys %nodes) {
      $self->_dirsync("$src/$node", "$dst/$node");
    }
    # Force permissions to match the source
    chmod( $stat_src[2] & 0777, $dst) or warn "$dst: Failed to chmod: $!\n";
    # Force user and group ownership to match the source
    chown( $stat_src[4], $stat_src[5], $dst) or warn "$dst: Failed to chown: $!\n";
    # Force timestamp to match the source.
    utime $when_src, $when_src, $dst or warn "$dst: Failed to utime: $!\n";
    $self->_op(5) if $self->{_gentle_percent};
    return;
  }

  print "$src: Unimplemented weird type of file! Skipping...\n" if $self->{verbose};
}

sub only {
  my $self = shift;
  push (@{ $self->{only} }, @_);
}

sub maxskew {
  my $self = shift;
  $self->{maxskew} = shift || 0;
}

sub dst {
  my $self = shift;
  $self->{dst} = shift;
}

sub src {
  my $self = shift;
  $self->{src} = shift;
}

sub ignore {
  my $self = shift;
  $self->{ignore} ||= {};
  # Load ignore into a hash
  foreach my $node (@_) {
    $self->{ignore}->{$node} = 1;
  }
}

sub lockfile {
  my $self = shift;
  my $lockfile = shift or return;
  open (LOCK, ">$lockfile") or return;
  if (!flock(LOCK, 6)) { # (LOCK_EX | LOCK_NB)
    print "Skipping due to concurrent process already running.\n" if $self->{verbose};
    exit;
  }
}

sub verbose {
  my $self = shift;
  if (@_) {
    $self->{verbose} = shift;
  }
  return $self->{verbose};
}

sub localmode {
  my $self = shift;
  if (@_) {
    $self->{localmode} = shift;



( run in 1.280 second using v1.01-cache-2.11-cpan-71847e10f99 )