mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Yannick Lecaillez
12.35.2015 ba29b0f8216baba3c804a2a8336384dab389c286
OPENDJ-2135: verify-index: IndexOutOfBoundsException
4 files modified
127 ■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2ID.java 13 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java 20 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/DN2IDTest.java 56 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java 38 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2ID.java
@@ -190,14 +190,19 @@
      return false;
    }
    // Immediate children should only have one RDN separator past the parent length
    for (int i = child.length(); i >= parent.length(); i--)
    int nbSeparator = 0;
    for (int i = parent.length() ; i < child.length(); i++)
    {
      if (child.byteAt(i) == DN.NORMALIZED_RDN_SEPARATOR && i != parent.length())
      if (child.byteAt(i) == DN.NORMALIZED_RDN_SEPARATOR)
      {
        return false;
        nbSeparator++;
        if (nbSeparator > 1)
        {
          return false;
        }
      }
    }
    return true;
    return (nbSeparator == 1);
  }
  /**
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java
@@ -35,13 +35,13 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
@@ -443,7 +443,7 @@
   */
  private void iterateDN2ID(ReadableTransaction txn) throws StorageRuntimeException
  {
    final Queue<ChildrenCount> childrenCounters = new LinkedList<>();
    final Deque<ChildrenCount> childrenCounters = new LinkedList<>();
    ChildrenCount currentNode = null;
    try(final Cursor<ByteString, ByteString> cursor = txn.openCursor(dn2id.getName()))
@@ -491,27 +491,29 @@
        }
      }
      while ((currentNode = childrenCounters.poll()) != null)
      while ((currentNode = childrenCounters.pollLast()) != null)
      {
        verifyID2ChildrenCount(txn, currentNode);
      }
    }
  }
  private ChildrenCount verifyID2ChildrenCount(ReadableTransaction txn, final Queue<ChildrenCount> childrenCounters,
  private ChildrenCount verifyID2ChildrenCount(ReadableTransaction txn, final Deque<ChildrenCount> childrenCounters,
      final ByteString key, final EntryID entryID)
  {
    while (childrenCounters.peek() != null && !DN2ID.isChild(childrenCounters.peek().baseDN, key))
    ChildrenCount currentParent = childrenCounters.peekLast();
    while (currentParent != null && !DN2ID.isChild(currentParent.baseDN, key))
    {
      // This subtree is fully processed, pop the counter of the parent DN from the stack and verify it's value
      verifyID2ChildrenCount(txn, childrenCounters.remove());
      verifyID2ChildrenCount(txn, childrenCounters.removeLast());
      currentParent = childrenCounters.getLast();
    }
    if (childrenCounters.peek() != null)
    if (currentParent != null)
    {
      childrenCounters.peek().numberOfChildren++;
      currentParent.numberOfChildren++;
    }
    final ChildrenCount node = new ChildrenCount(key, entryID);
    childrenCounters.add(node);
    childrenCounters.addLast(node);
    return node;
  }
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/DN2IDTest.java
@@ -33,6 +33,7 @@
import java.util.concurrent.TimeUnit;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.util.promise.NeverThrowsException;
import org.forgerock.util.promise.PromiseImpl;
import org.opends.server.DirectoryServerTestCase;
@@ -41,6 +42,7 @@
import org.opends.server.admin.std.server.BackendIndexCfg;
import org.opends.server.admin.std.server.PDBBackendCfg;
import org.opends.server.backends.pdb.PDBStorage;
import org.opends.server.backends.pluggable.spi.Cursor;
import org.opends.server.backends.pluggable.spi.ReadOperation;
import org.opends.server.backends.pluggable.spi.ReadableTransaction;
import org.opends.server.backends.pluggable.spi.SequentialCursor;
@@ -54,7 +56,6 @@
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
@@ -68,16 +69,12 @@
  private DN2ID dn2ID;
  private PDBStorage storage;
  // FIXME: This is required since PDBStorage is now using
  // DirectoryServer static method.
  @BeforeClass
  public void startFakeServer() throws Exception
  public void startServer() throws Exception
  {
    TestCaseUtils.startFakeServer();
  }
  @AfterClass
  public void stopFakeServer() throws Exception
  {
    TestCaseUtils.shutdownFakeServer();
    TestCaseUtils.startServer();
  }
  @BeforeMethod
@@ -138,6 +135,47 @@
  }
  @Test
  public void testIsChild() throws DirectoryException, Exception {
    put(dn("dc=example,dc=com"), 1);
    put(dn("ou=People,dc=example,dc=com"), 2);
    put(dn("uid=user.0,ou=People,dc=example,dc=com"), 3);
    put(dn("uid=user.1,ou=People,dc=example,dc=com"), 4);
    put(dn("uid=user.10,ou=People,dc=example,dc=com"), 5);
    storage.read(new ReadOperation<Void>()
    {
      @Override
      public Void run(ReadableTransaction txn) throws Exception
      {
        try (final Cursor<ByteString, ByteString> cursor = txn.openCursor(dn2ID.getName()))
        {
          cursor.next();
          final ByteString rootDN = cursor.getKey();
          cursor.next();
          final ByteString parentDN = cursor.getKey();
          cursor.next();
          assertThat(DN2ID.isChild(rootDN, parentDN)).isTrue();
          final ByteString childDN = cursor.getKey();
          assertThat(DN2ID.isChild(parentDN, childDN)).isTrue();
          cursor.next();
          final ByteString otherChildDN = cursor.getKey();
          assertThat(DN2ID.isChild(parentDN, otherChildDN)).isTrue();
          assertThat(DN2ID.isChild(childDN, otherChildDN)).isFalse();
          final ByteString lastChildDN = cursor.getKey();
          assertThat(DN2ID.isChild(parentDN, lastChildDN)).isTrue();
          assertThat(DN2ID.isChild(otherChildDN, lastChildDN)).isFalse();
          assertThat(DN2ID.isChild(childDN, lastChildDN)).isFalse();
        }
        return null;
      }
    });
  }
  @Test
  public void testGetNonExistingDNReturnNull() throws Exception
  {
    assertThat(get("dc=non,dc=existing")).isNull();
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java
@@ -57,7 +57,10 @@
import org.opends.server.admin.std.server.PluggableBackendCfg;
import org.opends.server.api.Backend.BackendOperation;
import org.opends.server.api.ClientConnection;
import org.opends.server.backends.VerifyConfig;
import org.opends.server.backends.pluggable.spi.ReadOnlyStorageException;
import org.opends.server.backends.pluggable.spi.ReadOperation;
import org.opends.server.backends.pluggable.spi.ReadableTransaction;
import org.opends.server.backends.pluggable.spi.Storage;
import org.opends.server.backends.pluggable.spi.Storage.AccessMode;
import org.opends.server.backends.pluggable.spi.TreeName;
@@ -909,6 +912,41 @@
  }
  @Test(dependsOnMethods = "testImportLDIF")
  public void testVerifyID2ChildrenCount() throws Exception
  {
    final Storage storage = backend.getRootContainer().getStorage();
    final DN2ID dn2ID = backend.getRootContainer().getEntryContainer(testBaseDN).getDN2ID();
    final ID2Count id2ChildrenCount = backend.getRootContainer().getEntryContainer(testBaseDN).getID2ChildrenCount();
    final VerifyConfig config = new VerifyConfig();
    config.setBaseDN(DN.valueOf("dc=test,dc=com"));
    config.addCleanIndex("dn2id");
    assertThat(backend.verifyBackend(config)).isEqualTo(0);
    // Insert an error
    final EntryID peopleID = storage.read(new ReadOperation<EntryID>()
    {
      @Override
      public EntryID run(ReadableTransaction txn) throws Exception
      {
        return dn2ID.get(txn, testBaseDN.child(DN.valueOf("ou=People")));
      }
    });
    storage.write(new WriteOperation()
    {
      @Override
      public void run(WriteableTransaction txn) throws Exception
      {
        id2ChildrenCount.deleteCount(txn, peopleID);
        id2ChildrenCount.addDelta(txn, peopleID, 1);
      }
    });
    assertThat(backend.verifyBackend(config)).isEqualTo(1);
  }
  @Test(dependsOnMethods = "testImportLDIF")
  public void testBackup() throws Exception
  {
    assertEquals(backend.supports(BackendOperation.BACKUP), true, "Skip Backup");