#!/usr/bin/perl

# To reconcile two sets of passwd/group/shadow files

use strict;
use Data::Dumper;

my $mountpoint=shift;
my $oldpassfile=shift;
my $newpassfile=shift;
my $oldgroupfile=shift;
my $newgroupfile=shift;

#my $oldpassfile="/tmp/apps-p";
#my $newpassfile="/tmp/apps-p1";
#my $oldgroupfile="/tmp/apps-g";
#my $newgroupfile="/tmp/apps-g1";

print "# Mount point is $mountpoint\n";
if ( ! ( ( -d $mountpoint."/lost+found" ) && ( -d $mountpoint ) ) ) {
 die "$mountpoint is not a mount point - no /lost+found\n"; 
}

print "# Old password file is $oldpassfile\n";
-f $oldpassfile || die "$oldpassfile is not a file\n";
print "# New password file is $newpassfile\n";
-f $newpassfile || die "$newpassfile is not a file\n";
print "# Old group file is $oldgroupfile\n";
-f $oldgroupfile || die "$oldgroupfile\n";
print "# New group file is $newgroupfile\n";
-f $newgroupfile || die "$newgroupfile\n";

my @passwordfields=("name","pass","uid","pgid","gecos","home","shell");
my @shadowfields=("name","pass","gid","members");

my %oldpass=readfile($oldpassfile,@passwordfields);
my %newpass=readfile($newpassfile,@passwordfields);
my %oldgroup=readfile($oldgroupfile,@shadowfields);
my %newgroup=readfile($newgroupfile,@shadowfields);

my $tempuid=gettempuid();
my $tempgid=gettempgid();
#print "Temporary UID is $tempuid, GID is $tempgid\n";

# Loop through all passwd entries:
foreach my $name (keys(%oldpass)) {
 # Does this name exist in the new password file?
 if (defined($newpass{$name})) {
  if ($oldpass{$name}{uid} != $newpass{$name}{uid}) {
   print "# user $name was uid ".$oldpass{$name}{uid}." but is now ".$newpass{$name}{uid}."\n";
   handleuidchange("uid",$oldpass{$name}{uid},$newpass{$name}{uid});
  }
 } else {
  print "# user $name not in new password file: will be added\n";
 }
}

# Loop through all group entries:
foreach my $name (keys(%oldgroup)) {
 # Does this group exist in the new group file?
 if (defined($newgroup{$name})) {
  if ($oldgroup{$name}{gid} != $newgroup{$name}{gid}) {
   print "# group $name was gid ".$oldgroup{$name}{gid}." but is now ".$newgroup{$name}{gid}."\n";
   handleuidchange("gid",$oldgroup{$name}{gid},$newgroup{$name}{gid});
  }
 } else {
  print "# group $name not in new group file: will be added\n";
 }
}


exit;

sub gettempuid() {
 my $testuid=1000;
 my $flag=0;
 while ($flag == 0) {
  $testuid=$testuid-1;
  $flag=1;
  foreach my $name (keys(%newpass)) {
   if ($newpass{$name}{uid}==$testuid) {
    $flag=0;
   }
  }
 }
 return $testuid;
}

sub gettempgid() {
 my $testgid=1000;
 my $flag=0;
 while ($flag==0) {
  $flag=1;
  $testgid=$testgid-1;
  foreach my $name (keys(%newgroup)) {
   if ($newgroup{$name}{gid}==$testgid) {
    $flag=0;
   }
  }
 }
 return $testgid;
}

sub handleuidchange($$$) {
 my ($type, $olduid, $newuid)=@_;
 my $op="chown";
 if ($type=="gid") { $op="chgrp" };
 system "find $mountpoint -$type $newuid -exec $op $tempuid '{}' ';'";
 system "find $mountpoint -$type $olduid -exec $op $newuid '{}' ';'";
 system "find $mountpoint -$type $tempuid -exec $op $olduid '{}' ';'";
}

sub readfile($$) {
 my $oldpassfile=shift;
 my @names=@_;
 # Read a file into an array
 my $fh;
 my %hash=();
 open ($fh, $oldpassfile) || die("Cannot open $oldpassfile: $!");
 while (my $line=<$fh>) {
  chomp($line);
  my @entry=split(/:/,$line);
  my $key=@entry[0];
  $hash{$key}={};
  foreach my $fname (@names) {
   my $value=shift(@entry);
   $hash{$key}{$fname}=$value;
  }
 }
 close ($fh);
 return %hash;
}

