#! /usr/bin/perl -W
#
# Update a set of files in the given chroot dir.
# Decision to update is based on file size and date, -
# if any of these don't match, whole file is copied
# to a temp file and renamed into place.
# In the destination, files are copied with full path.
# Only regular files (or symlinks to regular files)
# are copied.

use strict;
use Fcntl qw(O_RDONLY O_WRONLY O_EXCL O_CREAT S_IMODE S_ISREG);
use Errno qw(EEXIST ENOENT);

my $dest = shift @ARGV;

-d "$dest" or die "not a directory: $dest\n";

foreach my $snm (@ARGV) {

  sysopen SRC, $snm, O_RDONLY
    and my @sst = stat SRC
      or die "unable to open $snm: $!\n";

  my $dnm = "$dest/$snm";
  if (my @dst = stat $dnm) {
    next if $sst[7] == $dst[7] && $sst[9] == $dst[9];
  }

  die "$snm: not a regular file\n"
    unless S_ISREG($sst[2]);

  print "updating $snm => $dnm\n";

  my $dtnm = "$dnm.tmp";
  unlink "$dtnm";

  while (!sysopen DST, $dtnm, O_WRONLY|O_EXCL|O_CREAT, 0600) {
    $! == ENOENT or die "unable to create $dtnm: $!\n";
    my @c = split /\/+/, $snm;
    pop @c; # all but last component
    my $dd = $dest;
    foreach my $c (@c) {
      $dd .= '/' . $c;
      unless (mkdir $dd, 0755) {
        die "unable to mkdir $dd: $!" unless $! == EEXIST;
      }
    }
  }

  my $r;
  while($r = sysread(SRC,$_,64*1024)) {
    syswrite(DST,$_) or die "error writing to $dtnm: $!\n";
  }
  $r == 0 or die "error reading from $snm: $!\n";
  close SRC;

  utime $sst[9], $sst[9], \*DST;
  chmod S_IMODE($sst[2]), \*DST;
  chown $sst[4], $sst[5], \*DST;

  close DST and rename $dtnm, $dnm
    or die "error renaming $dtnm to $dnm: $!\n";

}

exit 0;
