Bläddra i källkod

Bug 725542 - Instance upgrade fails when upgrading 389-ds-base package

https://bugzilla.redhat.com/show_bug.cgi?id=725542
Resolves: bug 725542
Bug Description: Instance upgrade fails when upgrading 389-ds-base package
Reviewed by: nhosoi (Thanks!)
Branch: master
Fix Description: On 32-bit platforms, the pack 'Q' format specifier is not
available unless perl is explicitly compiled with 64-bit long long support.
Unfortunately, on RHEL/Fedora platforms, it is not.  Same with native support
for 64-bit values - fortunately, the bigint package is available, so we use
it.
Additionally, the format flags '>' and '<' for little/big endianness are not
available on perl 5.8 and earlier, so they are not very portable.  Finally,
the way we write the 32-bit unique id generator state is simply not usable.
We already skip it if going from 32-bit to 64-bit, so just ignore it if
going from 32-bit to 32-bit.  The fix is to unpack the 64-bit integer
values using two 32-bit values, using 'V' first, then 'N' if we think the
value might be big-endian (e.g. sparc).  For short values, 'v' or 'n'.
The 64-bit values are stored with the low part first, followed by the high
part.  We convert these two 32-bit values to a native 64-bit value to
perform computations with it, then convert it back to two 32-bit values.
Then pack the values using the format for the current platform -
unfortunately we do not use a platform independent way to store the
nsState values.
Platforms tested: RHEL6 x86_64 and RHEL5 i386
Flag Day: no
Doc impact: no
Rich Megginson 14 år sedan
förälder
incheckning
077544cad8
1 ändrade filer med 80 tillägg och 22 borttagningar
  1. 80 22
      ldap/admin/src/scripts/50fixNsState.pl

+ 80 - 22
ldap/admin/src/scripts/50fixNsState.pl

@@ -4,15 +4,26 @@ use Mozilla::LDAP::Utils qw(normalizeDN);
 use Mozilla::LDAP::API qw(:constant ldap_url_parse ldap_explode_dn);
 use DSUtil qw(debug);
 use Config;
+use bigint;
 
 # # Determine the endianness of your system
-my $packfmt32 = "QA6SCx3"; # must be 20 bytes
-my $packfmt64 = "QA6SCx7"; # must be 24 bytes
+my $packfmt32 = "VVA6vCx3"; # must be 20 bytes
+my $packfmt64 = "VVA6vCx7"; # must be 24 bytes
 
 my $is_big_endian = unpack('xc', pack('s', 1));
 # see if we are on an LP64 system
 my $is64 = ($Config{longsize} == 8);
 
+sub convert_to_32bit {
+    my $val64 = shift;
+    return ($val64 >> 32, $val64 & 0xffffffff);
+}
+
+sub convert_from_32bit {
+    my ($hi, $lo) = @_;
+    return ($hi << 32) + $lo;
+}
+
 sub convert_uniqueid {
     my $ent = shift;
     my $val = shift;
@@ -24,20 +35,28 @@ sub convert_uniqueid {
     my $hex = unpack('H*', $val);
     #print "hex=$hex\n";
 
-    my $fmt32 = "QA6SC";
-    my $fmt64 = "QA6SC";
+    my $fmt32 = "VVA6vC";
+    my $bigfmt32 = "NNA6nC";
+    my $fmt64 = "VVA6vC";
+    my $bigfmt64 = "NNA6nC";
     my $fmt = $fmt32;
+    my $bigfmt = $bigfmt32;
     if (length($val) > 20) {
         $fmt = $fmt64;
+        $bigfmt = $bigfmt64;
     } elsif ($is64) {
         # cannot convert 32-bit to 64-bit - just delete the entry and continue
         debug(1, "Cannot convert 32-bit nsState value $hex to 64-bit - deleting entry " .
               $ent->getDN() . " and continuing\n");
         return (-1, 0);
+    } else { # 32-bit to 32-bit - just leave it alone
+        debug(1, "Skipping 32-bit nsState value $hex in entry " .
+              $ent->getDN() . " and continuing\n");
+        return (0, 0);
     }
     if ($is_big_endian) {
-        $packfmt32 = "(QA6SCx3)>";
-        $packfmt64 = "(QA6SCx7)>";
+        $packfmt32 = "NNA6nCx3";
+        $packfmt64 = "NNA6nCx7";
     }
 
     my $packfmt = $packfmt32;
@@ -45,16 +64,16 @@ sub convert_uniqueid {
         $packfmt = $packfmt64;
     }
     
-    my ($ts, $node, $clockseq, $last_update) = unpack($fmt, $val);
-    # if we think it is from bigendian, do 
-    # $bigfmt = "(" . $fmt . ")>";
+    my ($tslow, $tshigh, $node, $clockseq, $last_update) = unpack($fmt, $val);
+    my $ts = convert_from_32bit($tshigh, $tslow);
     my $tssecs = ($ts - 0x01B21DD213814000) / 10000000;
     my $curts = time;
     my $tsdiff = abs($curts - $tssecs);
     my $maxdiff = 86400*365*10; # 10 years
     if (($tsdiff > $maxdiff) || (($last_update != 0) && ($last_update != 1))) {
         # try big endian
-        ($ts, $node, $clockseq, $last_update) = unpack("($fmt)>", $val);
+        ($tslow, $tshigh, $node, $clockseq, $last_update) = unpack($bigfmt, $val);
+        $ts = convert_from_32bit($tshigh, $tslow);
         $tssecs = ($ts - 0x01B21DD213814000) / 10000000;
         $tsdiff = abs($curts - $tssecs);
         if (($tsdiff > $maxdiff) || (($last_update != 0) && ($last_update != 1))) {
@@ -64,9 +83,10 @@ sub convert_uniqueid {
     }
 
     # format for the target system
-    my $newval = pack($packfmt, $ts, $node, $clockseq, $last_update);
+    ($tshigh, $tslow) = convert_to_32bit($ts);
+    my $newval = pack($packfmt, $tslow, $tshigh, $node, $clockseq, $last_update);
     my $rc = 0;
-    if ($val != $newval) { # changed
+    if ($val ne $newval) { # changed
         my $hex2 = unpack('H*', $newval);
         debug(1, "Converted old nsState val in ", $ent->getDN(), " from $hex to $hex2\n");
         $rc = 1; # changed
@@ -85,22 +105,40 @@ sub convert_replica {
     my $len = length($val);
     my $pad;
     my $timefmt;
+    my ($rid, $sampled_time, $local_offset, $remote_offset, $seq_num);
+    my ($st_high, $st_low, $lo_high, $lo_low, $ro_high, $ro_low);
+    my $fmtstr;
+    my $bigfmtstr;
     if ($len <= 20) {
         $pad = 2; # padding for short H values
-        $timefmt = 'I'; # timevals are unsigned 32-bit int
+        $timefmt = 'V'; # timevals are unsigned 32-bit int - try little-endian 'V' first
+        $fmtstr = "vx" . $pad . $timefmt . "3vx" . $pad;
+        $bigfmtstr = 'nx' . $pad . 'N' . '3nx' . $pad;
+        ($rid, $sampled_time, $local_offset, $remote_offset, $seq_num) = unpack($fmtstr, $val);
     } else {
         $pad = 6; # padding for short H values
-        $timefmt = 'Q'; # timevals are unsigned 64-bit int
+        $timefmt = 'V'; # timevals are unsigned 64-bit int
+        $fmtstr = "vx" . $pad . $timefmt . "6vx" . $pad;
+        $bigfmtstr = 'nx' . $pad . 'N' . '6nx' . $pad;
+        ($rid, $st_low, $st_high, $lo_low, $lo_high, $rt_low, $rt_high, $seq_num) = unpack($fmtstr, $val);
+        $sampled_time = convert_from_32bit($st_high, $st_low);
+        $local_offset = convert_from_32bit($lo_high, $lo_low);
+        $remote_offset = convert_from_32bit($ro_high, $ro_low);
     }
     # short - padbytes - 3 timevals - short - padbytes
-    my $fmtstr = "Sx" . $pad . $timefmt . "3Sx" . $pad;
-    my ($rid, $sampled_time, $local_offset, $remote_offset, $seq_num) = unpack($fmtstr, $val);
     my $hex = unpack('H*', $val);
     my $now = time;
     my $tdiff = abs($now - $sampled_time);
     my $maxdiff = 86400*365*10; # 10 years
     if ($tdiff > $maxdiff) { # try big endian
-        ($rid, $sampled_time, $local_offset, $remote_offset, $seq_num) = unpack("($fmtstr)>", $val);
+        if ($len <= 20) {
+            ($rid, $sampled_time, $local_offset, $remote_offset, $seq_num) = unpack($bigfmtstr, $val);
+        } else {
+            ($rid, $st_low, $st_high, $lo_low, $lo_high, $rt_low, $rt_high, $seq_num) = unpack($bigfmtstr, $val);
+            $sampled_time = convert_from_32bit($st_high, $st_low);
+            $local_offset = convert_from_32bit($lo_high, $lo_low);
+            $remote_offset = convert_from_32bit($ro_high, $ro_low);
+        }
         my $tdiff = abs($now - $sampled_time);
         if ($tdiff > $maxdiff) { # error
             debug(0, "Error: could not parse nsstate $hex - tdiff is $tdiff seconds or", ($tdiff/86400), " days\n");
@@ -108,12 +146,28 @@ sub convert_replica {
         }
     }
     # format for the target system
-    if ($is_big_endian) {
-        $fmtstr = "($fmtstr)>";
+    my $packfmt;
+    my @packargs;
+    if ($is64) {
+        my $packfmt = "vx" . $pad . "V6vx" . $pad;
+        if ($is_big_endian) {
+            $packfmt = "nx" . $pad . "N6nx" . $pad;
+        }
+        $st_high = $st >> 32;
+        ($st_high, $st_low) = convert_to_32bit($sampled_time);
+        ($lo_high, $lo_low) = convert_to_32bit($local_offset);
+        ($ro_high, $ro_low) = convert_to_32bit($remote_offset);
+        @packargs = ($rid, $st_low, $st_high, $lo_low, $lo_high, $ro_low, $ro_high, $seq_num);
+    } else {
+        my $packfmt = "vx" . $pad . "V3vx" . $pad;
+        if ($is_big_endian) {
+            $packfmt = "nx" . $pad . "N3nx" . $pad;
+        }
+        @packargs = ($rid, $sampled_time, $local_offset, $remote_offset, $seq_num);
     }
-    my $newval = pack($fmtstr, $rid, $sampled_time, $local_offset, $remote_offset, $seq_num);
+    my $newval = pack($fmtstr, @packargs);
     my $rc = 0;
-    if ($val != $newval) { # changed
+    if ($val ne $newval) { # changed
         my $hex2 = unpack('H*', $newval);
         debug(1, "Converted old nsState val in ", $ent->getDN(), " from $hex to $hex2\n");
         $rc = 1; # changed
@@ -164,7 +218,11 @@ sub runinst {
 sub testit {
 #my $val = 'ACm2BdIdsgH+tw/8AAB+swEAAAA=';
 #my $val = 'AOj+tyuA4AHsNZ7S9NnxZwEAAAAAAAAA';
-my $testval = "00a43cb4d11db2018b7912fd0000a42e01000000";
+#my $val = 'ABI3gdIdsgH3TJWpAACGIgEAAAA=';
+#my $testval = "00a43cb4d11db2018b7912fd0000a42e01000000";
+#my $testval = "0029B605D21DB201FEB70FFC00007EB301000000";
+#my $testval = "00E8FEB72B80E001EC359ED2F4D9F1670100000000000000";
+my $testval = "00123781D21DB201F74C95A90000862201000000";
 my $testdecval = $testval;
 # base16 decode
 $testdecval =~ s/(..)/chr(hex($1))/eg;