[Date Prev][Date Next] [Chronological] [Thread] [Top]

back-ldbm/modrdn.c corrupts dn2id.gdbm (ITS#3059)



Full_Name: Wout van Albada
Version: 2.2.6
OS: Solaris 9
URL: ftp://ftp.openldap.org/incoming/
Submission from: (NULL) (146.50.3.34)


The back-ldbm backend has a bug in the modify DN operation
which results in entries remaining in the tree but disappearing
from ancestral subtrees.

Example:

My suffix is:
    dc=science,dc=uva,dc=nl

My tree has several branches, one being:
    ou=ethers,dc=science,dc=uva,dc=nl

I have the following entry:
    cn=pc-bor19.science.uva.nl,dc=science,dc=uva,dc=nl

When I do a modrdn of the cn attribute, the entry's id
disappears from the ou=ethers,... subtree and one id lists
in dn2id.gdbm. This is because of the following code in
back-ldbm/modrdn.c:

    /* add new one */
    if ( dn2id_add( op->o_bd, &e->e_nname, e->e_id ) != 0 ) {
            send_ldap_error( op, rs, LDAP_OTHER,
                    "DN index add failed" );
            goto return_results;
    }
    /* delete old one */
    if ( dn2id_delete( op->o_bd, &old_ndn, e->e_id ) != 0 ) {
            /* undo add of new one */
            dn2id_delete( op->o_bd, &e->e_nname, e->e_id );
            send_ldap_error( op, rs, LDAP_OTHER,
                    "DN index delete fail" );
            goto return_results;
    }

The modified entry is first added to dn2id.gdbm using the old
id of the entry: e->e_id. This means that the id is added to
the one and subtree id lists for ou=ethers,dc=science,dc=uva,dc=nl.

Then the old entry is removed from dn2id.gdbm using the same
old id. This means that the id is removed from the one and
subtree id lists for ou=ethers,dc=science,dc=uva,dc=nl.

Result is the id is no longer in the one and subtree id
lists for ou=ethers,dc=science,dc=uva,dc=nl.

If the entry was deeper in the tree, more subtrees would be
corrupted. The subtree for dc=science,dc=uva,dc=nl (suffix)
does not exist in dn2id.gdbm because it is treated as a
special case.

The following patch swaps the dn2id_add() and dn2id_delete()
calls and removes the problem:

--- modrdn.c.orig       Thu Jan  1 19:16:37 2004
+++ modrdn.c    Tue Apr  6 12:43:21 2004
@@ -613,21 +613,22 @@
        case SLAPD_ABANDON:
                goto return_results;
        }
-
-       /* add new one */
-       if ( dn2id_add( op->o_bd, &e->e_nname, e->e_id ) != 0 ) {
-               send_ldap_error( op, rs, LDAP_OTHER,
-                       "DN index add failed" );
-               goto return_results;
-       }
+
        /* delete old one */
        if ( dn2id_delete( op->o_bd, &old_ndn, e->e_id ) != 0 ) {
-               /* undo add of new one */
-               dn2id_delete( op->o_bd, &e->e_nname, e->e_id );
                send_ldap_error( op, rs, LDAP_OTHER,
                        "DN index delete fail" );
                goto return_results;
        }
+
+       /* add new one */
+       if ( dn2id_add( op->o_bd, &e->e_nname, e->e_id ) != 0 ) {
+               /* add failed, try to repair old entry - probably hopeless */
+               dn2id_add( op->o_bd, &old_ndn, e->e_id);
+               send_ldap_error( op, rs, LDAP_OTHER,
+                       "DN index add failed" );
+               goto return_results;
+       }

        /* id2entry index */
        if ( id2entry_add( op->o_bd, e ) != 0 ) {