]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/overlays/lastmod.c
Sync with HEAD
[openldap] / servers / slapd / overlays / lastmod.c
index 5ea1a182f2cf504e04dd6eee807fd07f518d0c43..454239c0bcf63d8f7f01fed73e4247aad03dd1cd 100644 (file)
@@ -1,7 +1,7 @@
 /* lastmod.c - returns last modification info */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2004 The OpenLDAP Foundation.
+ * Copyright 2004-2005 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -33,12 +33,14 @@ typedef struct lastmod_info_t {
        struct berval           lmi_rdnvalue;
        Entry                   *lmi_e;
        ldap_pvt_thread_mutex_t lmi_entry_mutex;
+       int                     lmi_enabled;
 } lastmod_info_t;
 
 struct lastmod_schema_t {
        ObjectClass             *lms_oc_lastmod;
        AttributeDescription    *lms_ad_lastmodDN;
        AttributeDescription    *lms_ad_lastmodType;
+       AttributeDescription    *lms_ad_lastmodEnabled;
 } lastmod_schema;
 
 enum lastmodType_e {
@@ -61,15 +63,16 @@ struct berval lastmodType[] = {
 };
 
 static struct m_s {
-       char    *name;
-       char    *schema;
-       slap_mask_t flags;
-       int     offset;
+       char                    *name;
+       char                    *schema;
+       slap_mask_t             flags;
+       int                     offset;
 } moc[] = {
        { "lastmod", "( 1.3.6.1.4.1.4203.666.3.13"
                "NAME 'lastmod' "
                "DESC 'OpenLDAP per-database last modification monitoring' "
-               "SUP top STRUCTURAL "
+               "STRUCTURAL "
+               "SUP top "
                "MUST cn "
                "MAY ( "
                        "lastmodDN "
@@ -77,7 +80,7 @@ static struct m_s {
                        "$ description "
                        "$ seeAlso "
                ") )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
-               offsetof(struct lastmod_schema_t, lms_oc_lastmod) },
+               offsetof( struct lastmod_schema_t, lms_oc_lastmod ) },
        { NULL }
 }, mat[] = {
        { "lastmodDN", "( 1.3.6.1.4.1.4203.666.1.28"
@@ -87,7 +90,7 @@ static struct m_s {
                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
                "NO-USER-MODIFICATION "
                "USAGE directoryOperation )", SLAP_AT_HIDE,
-               offsetof(struct lastmod_schema_t, lms_ad_lastmodDN) },
+               offsetof( struct lastmod_schema_t, lms_ad_lastmodDN ) },
        { "lastmodType", "( 1.3.6.1.4.1.4203.666.1.29"
                "NAME 'lastmodType' "
                "DESC 'Type of last modification' "
@@ -96,34 +99,42 @@ static struct m_s {
                "SINGLE-VALUE "
                "NO-USER-MODIFICATION "
                "USAGE directoryOperation )", SLAP_AT_HIDE,
-               offsetof(struct lastmod_schema_t, lms_ad_lastmodType) },
+               offsetof( struct lastmod_schema_t, lms_ad_lastmodType ) },
+       { "lastmodEnabled", "( 1.3.6.1.4.1.4203.666.1.30"
+               "NAME 'lastmodEnabled' "
+               "DESC 'Lastmod overlay state' "
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+               "EQUALITY booleanMatch "
+               "SINGLE-VALUE )", 0,
+               offsetof( struct lastmod_schema_t, lms_ad_lastmodEnabled ) },
        { NULL }
-};
 
-static const struct berval *write_exop[] = {
-       &slap_EXOP_MODIFY_PASSWD,
-       NULL
+       /* FIXME: what about UUID of last modified entry? */
 };
 
 static int
 lastmod_search( Operation *op, SlapReply *rs )
 {
-       slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
+       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
        lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+       int                     rc;
+
+       /* if we get here, it must be a success */
+       rs->sr_err = LDAP_SUCCESS;
 
-#if 0
-       /* FIXME: can't restore the correct bd_info otherwise 
-        * backend_operational() screws things up */
-       op->o_bd->bd_info = (BackendInfo *)on->on_info;
-#endif
        ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
-       rs->sr_attrs = op->ors_attrs;
-       rs->sr_flags = 0;
-       rs->sr_entry = lmi->lmi_e;
-       rs->sr_err = send_search_entry( op, rs );
-       rs->sr_entry = NULL;
-       rs->sr_flags = 0;
-       rs->sr_attrs = NULL;
+
+       rc = test_filter( op, lmi->lmi_e, op->oq_search.rs_filter );
+       if ( rc == LDAP_COMPARE_TRUE ) {
+               rs->sr_attrs = op->ors_attrs;
+               rs->sr_flags = 0;
+               rs->sr_entry = lmi->lmi_e;
+               rs->sr_err = send_search_entry( op, rs );
+               rs->sr_entry = NULL;
+               rs->sr_flags = 0;
+               rs->sr_attrs = NULL;
+       }
+
        ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
 
        send_ldap_result( op, rs );
@@ -134,13 +145,14 @@ lastmod_search( Operation *op, SlapReply *rs )
 static int
 lastmod_compare( Operation *op, SlapReply *rs )
 {
-       slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
+       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
        lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
        Attribute               *a;
 
        ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+
        if ( get_assert( op ) &&
-               ( test_filter( op, lmi->lmi_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+               ( test_filter( op, lmi->lmi_e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
        {
                rs->sr_err = LDAP_ASSERTION_FAILED;
                goto return_results;
@@ -157,7 +169,7 @@ lastmod_compare( Operation *op, SlapReply *rs )
 
        for ( a = attr_find( lmi->lmi_e->e_attrs, op->oq_compare.rs_ava->aa_desc );
                a != NULL;
-               a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ))
+               a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
        {
                rs->sr_err = LDAP_COMPARE_FALSE;
 
@@ -172,7 +184,9 @@ lastmod_compare( Operation *op, SlapReply *rs )
        }
 
 return_results:;
+
        ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
        send_ldap_result( op, rs );
 
        if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
@@ -185,7 +199,7 @@ return_results:;
 static int
 lastmod_exop( Operation *op, SlapReply *rs )
 {
-       slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
+       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
 
        /* Temporary */
 
@@ -198,12 +212,79 @@ lastmod_exop( Operation *op, SlapReply *rs )
        return -1;
 }
 
+static int
+lastmod_modify( Operation *op, SlapReply *rs )
+{
+       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
+       lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+       Modifications           *ml;
+
+       ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+
+       if ( !acl_check_modlist( op, lmi->lmi_e, op->orm_modlist ) ) {
+               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+               goto cleanup;
+       }
+
+       for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+               Attribute       *a;
+
+               if ( ml->sml_desc != lastmod_schema.lms_ad_lastmodEnabled ) {
+                       continue;
+               }
+
+               if ( ml->sml_op != LDAP_MOD_REPLACE ) {
+                       rs->sr_text = "unsupported mod type";
+                       rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+                       goto cleanup;
+               }
+               
+               a = attr_find( lmi->lmi_e->e_attrs, ml->sml_desc );
+
+               if ( a == NULL ) {
+                       rs->sr_text = "lastmod overlay internal error";
+                       rs->sr_err = LDAP_OTHER;
+                       goto cleanup;
+               }
+
+               ch_free( a->a_vals[ 0 ].bv_val );
+               ber_dupbv( &a->a_vals[ 0 ], &ml->sml_values[ 0 ] );
+               if ( a->a_nvals ) {
+                       ch_free( a->a_nvals[ 0 ].bv_val );
+                       if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
+                               ber_dupbv( &a->a_nvals[ 0 ], &ml->sml_nvalues[ 0 ] );
+                       } else {
+                               ber_dupbv( &a->a_nvals[ 0 ], &ml->sml_values[ 0 ] );
+                       }
+               }
+
+               if ( strcmp( ml->sml_values[ 0 ].bv_val, "TRUE" ) == 0 ) {
+                       lmi->lmi_enabled = 1;
+               } else if ( strcmp( ml->sml_values[ 0 ].bv_val, "FALSE" ) == 0 ) {
+                       lmi->lmi_enabled = 0;
+               } else {
+                       assert( 0 );
+               }
+       }
+
+       rs->sr_err = LDAP_SUCCESS;
+
+cleanup:;
+       ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+       send_ldap_result( op, rs );
+       rs->sr_text = NULL;
+
+       return rs->sr_err;
+}
+
 static int
 lastmod_op_func( Operation *op, SlapReply *rs )
 {
-       slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
+       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
        lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
        unsigned                i;
+       Modifications           *ml;
 
        if ( dn_match( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) {
                switch ( op->o_tag ) {
@@ -219,14 +300,27 @@ lastmod_op_func( Operation *op, SlapReply *rs )
 
                case LDAP_REQ_EXTENDED:
                        /* if write, reject; otherwise process */
-                       for ( i = 0; write_exop[ i ] != NULL; i++ ) {
-                               if ( ber_bvcmp( write_exop[ i ], &op->oq_extended.rs_reqoid ) == 0 ) {
+                       if ( exop_is_write( op )) {
+                               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+                               rs->sr_text = "not allowed within namingContext";
+                               goto return_error;
+                       }
+                       return lastmod_exop( op, rs );
+
+               case LDAP_REQ_MODIFY:
+                       /* allow only changes to overlay status */
+                       for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+                               if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_modifiersName ) != 0
+                                               && ad_cmp( ml->sml_desc, slap_schema.si_ad_modifyTimestamp ) != 0
+                                               && ad_cmp( ml->sml_desc, slap_schema.si_ad_entryCSN ) != 0
+                                               && ad_cmp( ml->sml_desc, lastmod_schema.lms_ad_lastmodEnabled ) != 0 )
+                               {
                                        rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
                                        rs->sr_text = "not allowed within namingContext";
                                        goto return_error;
                                }
                        }
-                       return lastmod_exop( op, rs );
+                       return lastmod_modify( op, rs );
 
                default:
                        rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
@@ -246,12 +340,15 @@ return_referral:;
        rs->sr_ref = referral_rewrite( default_referral,
                        NULL, &op->o_req_dn, op->ors_scope );
 
-       if (!rs->sr_ref) rs->sr_ref = default_referral;
+       if ( !rs->sr_ref ) {
+               rs->sr_ref = default_referral;
+       }
        rs->sr_err = LDAP_REFERRAL;
        send_ldap_result( op, rs );
 
-       if (rs->sr_ref != default_referral)
-       ber_bvarray_free( rs->sr_ref );
+       if ( rs->sr_ref != default_referral ) {
+               ber_bvarray_free( rs->sr_ref );
+       }
        rs->sr_ref = NULL;
 
        return -1;
@@ -264,14 +361,71 @@ return_error:;
        return -1;
 }
 
+static int
+best_guess( Operation *op,
+               struct berval *bv_entryCSN, struct berval *bv_nentryCSN,
+               struct berval *bv_modifyTimestamp, struct berval *bv_nmodifyTimestamp,
+               struct berval *bv_modifiersName, struct berval *bv_nmodifiersName )
+{
+       if ( bv_entryCSN ) {
+               char            csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
+               struct berval   entryCSN;
+       
+               slap_get_csn( NULL, csnbuf, sizeof(csnbuf), &entryCSN, 0 );
+
+               ber_dupbv( bv_entryCSN, &entryCSN );
+               ber_dupbv( bv_nentryCSN, &entryCSN );
+       }
+
+       if ( bv_modifyTimestamp ) {
+               struct tm       *tm;
+#ifdef HAVE_GMTIME_R
+               struct tm       tm_buf;
+#endif
+               char            tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+               time_t          currtime;
+
+               /* best guess */
+#if 0
+               currtime = slap_get_time();
+#endif
+               /* maybe we better use the time the operation was initiated */
+               currtime = op->o_time;
+
+#ifndef HAVE_GMTIME_R
+               ldap_pvt_thread_mutex_lock( &gmtime_mutex );
+               tm = gmtime( &currtime );
+#else /* HAVE_GMTIME_R */
+               tm = gmtime_r( &currtime, &tm_buf );
+#endif /* HAVE_GMTIME_R */
+               lutil_gentime( tmbuf, sizeof( tmbuf ), tm );
+#ifndef HAVE_GMTIME_R
+               ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
+#endif
+
+               ber_str2bv( tmbuf, 0, 1, bv_modifyTimestamp );
+               ber_dupbv( bv_nmodifyTimestamp, bv_modifyTimestamp );
+       }
+
+       if ( bv_modifiersName ) {
+               /* best guess */
+               ber_dupbv( bv_modifiersName, &op->o_dn );
+               ber_dupbv( bv_nmodifiersName, &op->o_ndn );
+       }
+
+       return 0;
+}
+
 static int
 lastmod_update( Operation *op, SlapReply *rs )
 {
-       slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
+       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
        lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
        Attribute               *a;
        Modifications           *ml = NULL;
-       struct berval           bv_modifyTimestamp = BER_BVNULL,
+       struct berval           bv_entryCSN = BER_BVNULL,
+                               bv_nentryCSN = BER_BVNULL,
+                               bv_modifyTimestamp = BER_BVNULL,
                                bv_nmodifyTimestamp = BER_BVNULL,
                                bv_modifiersName = BER_BVNULL,
                                bv_nmodifiersName = BER_BVNULL,
@@ -286,10 +440,14 @@ lastmod_update( Operation *op, SlapReply *rs )
        case LDAP_REQ_ADD:
                lmt = LASTMOD_ADD;
                e = op->ora_e;
-               a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
+               a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
                if ( a != NULL ) {
-                       ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
-                       ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
+                       ber_dupbv( &bv_entryCSN, &a->a_vals[0] );
+                       if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
+                               ber_dupbv( &bv_nentryCSN, &a->a_nvals[0] );
+                       } else {
+                               ber_dupbv( &bv_nentryCSN, &a->a_vals[0] );
+                       }
                }
                a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
                if ( a != NULL ) {
@@ -300,56 +458,63 @@ lastmod_update( Operation *op, SlapReply *rs )
                                ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] );
                        }
                }
+               a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
+               if ( a != NULL ) {
+                       ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
+                       ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
+               }
                ber_dupbv( &bv_name, &e->e_name );
                ber_dupbv( &bv_nname, &e->e_nname );
                break;
 
        case LDAP_REQ_DELETE:
-       {
-               struct tm       *tm;
-#ifdef HAVE_GMTIME_R
-               struct tm       tm_buf;
-#endif
-               char            tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
-               time_t          currtime;
-
                lmt = LASTMOD_DELETE;
 
-               /* best guess */
-               currtime = slap_get_time();
+               best_guess( op, &bv_entryCSN, &bv_nentryCSN,
+                               &bv_modifyTimestamp, &bv_nmodifyTimestamp,
+                               &bv_modifiersName, &bv_nmodifiersName );
 
-#ifndef HAVE_GMTIME_R
-               ldap_pvt_thread_mutex_lock( &gmtime_mutex );
-               tm = gmtime( &currtime );
-#else /* HAVE_GMTIME_R */
-               tm = gmtime_r( &currtime, &tm_buf );
-#endif /* HAVE_GMTIME_R */
-               lutil_gentime( tmbuf, sizeof( tmbuf ), tm );
-#ifndef HAVE_GMTIME_R
-               ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
-#endif
+               ber_dupbv( &bv_name, &op->o_req_dn );
+               ber_dupbv( &bv_nname, &op->o_req_ndn );
+               break;
 
-               ber_str2bv( tmbuf, 0, 1, &bv_modifyTimestamp );
-               ber_dupbv( &bv_nmodifyTimestamp, &bv_modifyTimestamp );
+       case LDAP_REQ_EXTENDED:
+               lmt = LASTMOD_EXOP;
 
-               /* best guess */
-               ber_dupbv( &bv_modifiersName, &op->o_dn );
-               ber_dupbv( &bv_nmodifiersName, &op->o_ndn );
+               /* actually, password change is wrapped around a backend 
+                * call to modify, so it never shows up as an exop... */
+               best_guess( op, &bv_entryCSN, &bv_nentryCSN,
+                               &bv_modifyTimestamp, &bv_nmodifyTimestamp,
+                               &bv_modifiersName, &bv_nmodifiersName );
 
                ber_dupbv( &bv_name, &op->o_req_dn );
                ber_dupbv( &bv_nname, &op->o_req_ndn );
                break;
-       }
 
        case LDAP_REQ_MODIFY:
                lmt = LASTMOD_MODIFY;
+               rc = 3;
 
                for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
                        if ( ad_cmp( ml->sml_desc , slap_schema.si_ad_modifiersName ) == 0 ) {
                                ber_dupbv( &bv_modifiersName, &ml->sml_values[0] );
                                ber_dupbv( &bv_nmodifiersName, &ml->sml_nvalues[0] );
 
-                               if ( !BER_BVISNULL( &bv_modifyTimestamp ) ) {
+                               rc--;
+                               if ( !rc ) {
+                                       break;
+                               }
+
+                       } else if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_entryCSN ) == 0 ) {
+                               ber_dupbv( &bv_entryCSN, &ml->sml_values[0] );
+                               if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[0] ) ) {
+                                       ber_dupbv( &bv_nentryCSN, &ml->sml_nvalues[0] );
+                               } else {
+                                       ber_dupbv( &bv_nentryCSN, &ml->sml_values[0] );
+                               }
+
+                               rc --;
+                               if ( !rc ) {
                                        break;
                                }
 
@@ -361,12 +526,26 @@ lastmod_update( Operation *op, SlapReply *rs )
                                        ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_values[0] );
                                }
 
-                               if ( !BER_BVISNULL( &bv_modifiersName ) ) {
+                               rc --;
+                               if ( !rc ) {
                                        break;
                                }
                        }
                }
 
+               /* if rooted at global overlay, opattrs are not yet in place */
+               if ( BER_BVISNULL( &bv_modifiersName ) ) {
+                       best_guess( op, NULL, NULL, NULL, NULL, &bv_modifiersName, &bv_nmodifiersName );
+               }
+
+               if ( BER_BVISNULL( &bv_entryCSN ) ) {
+                       best_guess( op, &bv_entryCSN, &bv_nentryCSN, NULL, NULL, NULL, NULL );
+               }
+
+               if ( BER_BVISNULL( &bv_modifyTimestamp ) ) {
+                       best_guess( op, NULL, NULL, &bv_modifyTimestamp, &bv_nmodifyTimestamp, NULL, NULL );
+               }
+
                ber_dupbv( &bv_name, &op->o_req_dn );
                ber_dupbv( &bv_nname, &op->o_req_ndn );
                break;
@@ -374,27 +553,42 @@ lastmod_update( Operation *op, SlapReply *rs )
        case LDAP_REQ_MODRDN:
                lmt = LASTMOD_MODRDN;
                e = NULL;
-               if ( on->on_info->oi_orig->bi_entry_get_rw ) {
-                       BackendInfo     *bi = op->o_bd->bd_info;
-                       struct berval   dn = BER_BVNULL;
 
-                       if ( op->orr_nnewSup && !BER_BVISNULL( op->orr_nnewSup ) ) {
-                               build_new_dn( &dn, op->orr_nnewSup, &op->orr_nnewrdn, NULL );
+               if ( op->orr_newSup && !BER_BVISNULL( op->orr_newSup ) ) {
+                       build_new_dn( &bv_name, op->orr_newSup, &op->orr_newrdn, NULL );
+                       build_new_dn( &bv_nname, op->orr_nnewSup, &op->orr_nnewrdn, NULL );
 
-                       } else {
-                               struct berval   pdn;
+               } else {
+                       struct berval   pdn;
 
-                               dnParent( &op->o_req_ndn, &pdn );
-                               build_new_dn( &dn, &pdn, &op->orr_nnewrdn, NULL );
-                       }
+                       dnParent( &op->o_req_dn, &pdn );
+                       build_new_dn( &bv_name, &pdn, &op->orr_newrdn, NULL );
+
+                       dnParent( &op->o_req_ndn, &pdn );
+                       build_new_dn( &bv_nname, &pdn, &op->orr_nnewrdn, NULL );
+               }
+
+               if ( on->on_info->oi_orig->bi_entry_get_rw ) {
+                       BackendInfo     *bi = op->o_bd->bd_info;
+                       int             rc;
 
                        op->o_bd->bd_info = (BackendInfo *)on->on_info->oi_orig;
-                       if ( (*op->o_bd->bd_info->bi_entry_get_rw)( op, &dn, NULL, NULL, 0, &e ) == LDAP_SUCCESS ) {
+                       rc = (*op->o_bd->bd_info->bi_entry_get_rw)( op, &bv_name, NULL, NULL, 0, &e );
+                       if ( rc == LDAP_SUCCESS ) {
                                a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
                                if ( a != NULL ) {
                                        ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
                                        ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
                                }
+                               a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
+                               if ( a != NULL ) {
+                                       ber_dupbv( &bv_entryCSN, &a->a_vals[0] );
+                                       if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
+                                               ber_dupbv( &bv_nentryCSN, &a->a_nvals[0] );
+                                       } else {
+                                               ber_dupbv( &bv_nentryCSN, &a->a_vals[0] );
+                                       }
+                               }
                                a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
                                if ( a != NULL ) {
                                        ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] );
@@ -405,15 +599,23 @@ lastmod_update( Operation *op, SlapReply *rs )
                                        }
                                }
 
-                               ber_dupbv( &bv_name, &e->e_name );
-                               ber_dupbv( &bv_nname, &e->e_nname );
+                               assert( dn_match( &bv_name, &e->e_name ) );
+                               assert( dn_match( &bv_nname, &e->e_nname ) );
 
                                (*op->o_bd->bd_info->bi_entry_release_rw)( op, e, 0 );
                        }
 
-                       ch_free( dn.bv_val );
                        op->o_bd->bd_info = bi;
+
                }
+
+               /* if !bi_entry_get_rw || bi_entry_get_rw failed for any reason... */
+               if ( e == NULL ) {
+                       best_guess( op, &bv_entryCSN, &bv_nentryCSN,
+                                       &bv_modifyTimestamp, &bv_nmodifyTimestamp,
+                                       &bv_modifiersName, &bv_nmodifiersName );
+               }
+
                break;
 
        default:
@@ -446,7 +648,7 @@ lastmod_update( Operation *op, SlapReply *rs )
        ch_free( a->a_vals[0].bv_val );
        ber_dupbv( &a->a_vals[0], &lastmodType[ lmt ] );
        ch_free( a->a_nvals[0].bv_val );
-       ber_dupbv( &a->a_vals[0], &lastmodType[ lmt ] );
+       ber_dupbv( &a->a_nvals[0], &lastmodType[ lmt ] );
 
 #if 0
        fprintf( stderr, "### modifiersName: %s %s\n", bv_modifiersName.bv_val, bv_nmodifiersName.bv_val );
@@ -474,6 +676,19 @@ lastmod_update( Operation *op, SlapReply *rs )
        ch_free( a->a_nvals[0].bv_val );
        a->a_nvals[0] = bv_nmodifyTimestamp;
 
+#if 0
+       fprintf( stderr, "### entryCSN: %s %s\n", bv_nentryCSN.bv_val, bv_entryCSN.bv_val );
+#endif
+
+       a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_entryCSN );
+       if ( a == NULL ) {
+               goto error_return;
+       } 
+       ch_free( a->a_vals[0].bv_val );
+       a->a_vals[0] = bv_entryCSN;
+       ch_free( a->a_nvals[0].bv_val );
+       a->a_nvals[0] = bv_nentryCSN;
+
        rc = 0;
 
 error_return:;
@@ -485,38 +700,48 @@ error_return:;
 static int
 lastmod_response( Operation *op, SlapReply *rs )
 {
-       unsigned int    i;
+       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
+       lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+       unsigned int            i;
+
+       /* don't record failed operations */
+       switch ( rs->sr_err ) {
+       case LDAP_SUCCESS:
+               /* FIXME: other cases? */
+               break;
 
-       if ( rs->sr_err != LDAP_SUCCESS ) {
+       default:
                return SLAP_CB_CONTINUE;
        }
 
+       /* record only write operations */
        switch ( op->o_tag ) {
        case LDAP_REQ_ADD:
        case LDAP_REQ_MODIFY:
        case LDAP_REQ_MODRDN:
        case LDAP_REQ_DELETE:
-               /* FIXME: exop? */
                break;
 
        case LDAP_REQ_EXTENDED:
                /* if write, process */
-               for ( i = 0; write_exop[ i ] != NULL; i++ ) {
-                       if ( ber_bvcmp( write_exop[ i ], &op->oq_extended.rs_reqoid ) == 0 ) {
-                               goto process;
-                       }
-               }
-               /* fall thru */
+               if ( exop_is_write( op ))
+                       break;
 
+               /* fall thru */
        default:
                return SLAP_CB_CONTINUE;
        }
 
-process:;
-       if ( rs->sr_err == LDAP_SUCCESS ) {
-               /* FIXME: ignore errors */
-               (void)lastmod_update( op, rs );
+       /* skip if disabled */
+       ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+       if ( !lmi->lmi_enabled ) {
+               ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+               return SLAP_CB_CONTINUE;
        }
+       ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+process:;
+       (void)lastmod_update( op, rs );
 
        return SLAP_CB_CONTINUE;
 }
@@ -526,12 +751,12 @@ lastmod_db_init(
        BackendDB *be
 )
 {
-       slap_overinst           *on = (slap_overinst *) be->bd_info;
+       slap_overinst           *on = (slap_overinst *)be->bd_info;
        lastmod_info_t          *lmi;
 
        if ( lastmod_schema.lms_oc_lastmod == NULL ) {
-               int                     i;
-               const char              *text;
+               int             i;
+               const char      *text;
 
                /* schema integration */
                for ( i = 0; mat[i].name; i++ ) {
@@ -543,42 +768,24 @@ lastmod_db_init(
                        at = ldap_str2attributetype( mat[i].schema, &code,
                                &err, LDAP_SCHEMA_ALLOW_ALL );
                        if ( !at ) {
-#ifdef NEW_LOGGING
-                               LDAP_LOG( OPERATION, CRIT, "lastmod_init: "
-                                       "in AttributeType '%s' %s before %s\n",
-                                       mat[i].name, ldap_scherr2str(code), err );
-#else
                                Debug( LDAP_DEBUG_ANY, "lastmod_init: "
                                        "in AttributeType '%s' %s before %s\n",
                                        mat[i].name, ldap_scherr2str(code), err );
-#endif
                                return -1;
                        }
        
                        if ( at->at_oid == NULL ) {
-#ifdef NEW_LOGGING
-                               LDAP_LOG( OPERATION, CRIT, "lastmod_init: "
-                                       "null OID for attributeType '%s'\n",
-                                       mat[i].name, 0, 0 );
-#else
                                Debug( LDAP_DEBUG_ANY, "lastmod_init: "
                                        "null OID for attributeType '%s'\n",
                                        mat[i].name, 0, 0 );
-#endif
                                return -1;
                        }
        
                        code = at_add(at, &err);
                        if ( code ) {
-#ifdef NEW_LOGGING
-                               LDAP_LOG( OPERATION, CRIT, "lastmod_init: "
-                                       "%s in attributeType '%s'\n",
-                                       scherr2str(code), mat[i].name, 0 );
-#else
                                Debug( LDAP_DEBUG_ANY, "lastmod_init: "
                                        "%s in attributeType '%s'\n",
                                        scherr2str(code), mat[i].name, 0 );
-#endif
                                return -1;
                        }
                        ldap_memfree(at);
@@ -586,13 +793,8 @@ lastmod_db_init(
                        ad = ((AttributeDescription **)&(((char *)&lastmod_schema)[mat[i].offset]));
                        ad[0] = NULL;
                        if ( slap_str2ad( mat[i].name, ad, &text ) ) {
-#ifdef NEW_LOGGING
-                               LDAP_LOG( OPERATION, CRIT,
-                                       "lastmod_init: %s\n", text, 0, 0 );
-#else
                                Debug( LDAP_DEBUG_ANY,
                                        "lastmod_init: %s\n", text, 0, 0 );
-#endif
                                return -1;
                        }
        
@@ -608,44 +810,25 @@ lastmod_db_init(
                        oc = ldap_str2objectclass(moc[i].schema, &code, &err,
                                        LDAP_SCHEMA_ALLOW_ALL );
                        if ( !oc ) {
-#ifdef NEW_LOGGING
-                               LDAP_LOG( OPERATION, CRIT,
-                                       "unable to parse lastmod objectClass '%s': "
-                                       "%s before %s\n" , moc[i].name,
-                                       ldap_scherr2str(code), err );
-#else
                                Debug( LDAP_DEBUG_ANY,
                                        "unable to parse lastmod objectClass '%s': "
                                        "%s before %s\n" , moc[i].name,
                                        ldap_scherr2str(code), err );
-#endif
                                return -1;
                        }
 
                        if ( oc->oc_oid == NULL ) {
-#ifdef NEW_LOGGING
-                               LDAP_LOG( OPERATION, CRIT,
-                                       "objectClass '%s' has no OID\n" ,
-                                       moc[i].name, 0, 0 );
-#else
                                Debug( LDAP_DEBUG_ANY,
                                        "objectClass '%s' has no OID\n" ,
                                        moc[i].name, 0, 0 );
-#endif
                                return -1;
                        }
 
                        code = oc_add(oc, 0, &err);
                        if ( code ) {
-#ifdef NEW_LOGGING
-                               LDAP_LOG( OPERATION, CRIT,
-                                       "objectClass '%s': %s \"%s\"\n" ,
-                                       moc[i].name, scherr2str(code), err );
-#else
                                Debug( LDAP_DEBUG_ANY,
                                        "objectClass '%s': %s \"%s\"\n" ,
                                        moc[i].name, scherr2str(code), err );
-#endif
                                return -1;
                        }
        
@@ -653,15 +836,9 @@ lastmod_db_init(
        
                        Oc = oc_find( moc[i].name );
                        if ( Oc == NULL ) {
-#ifdef NEW_LOGGING
-                               LDAP_LOG( OPERATION, CRIT, "lastmod_init: "
-                                               "unable to find objectClass %s "
-                                               "(just added)\n", moc[i].name, 0, 0 );
-#else
                                Debug( LDAP_DEBUG_ANY, "lastmod_init: "
                                                "unable to find objectClass %s "
                                                "(just added)\n", moc[i].name, 0, 0 );
-#endif
                                return -1;
                        }
 
@@ -671,8 +848,11 @@ lastmod_db_init(
                }
        }
 
-       lmi = (lastmod_info_t *)ch_malloc(sizeof(lastmod_info_t));
-       memset(lmi, 0, sizeof(lastmod_info_t));
+       lmi = (lastmod_info_t *)ch_malloc( sizeof( lastmod_info_t ) );
+
+       memset( lmi, 0, sizeof( lastmod_info_t ) );
+       lmi->lmi_enabled = 1;
+       
        on->on_bi.bi_private = lmi;
 
        return 0;
@@ -680,14 +860,14 @@ lastmod_db_init(
 
 static int
 lastmod_db_config(
-    BackendDB  *be,
-    const char *fname,
-    int                lineno,
-    int                argc,
-    char       **argv
+       BackendDB       *be,
+       const char      *fname,
+       int             lineno,
+       int             argc,
+       char    **argv
 )
 {
-       slap_overinst           *on = (slap_overinst *) be->bd_info;
+       slap_overinst           *on = (slap_overinst *)be->bd_info;
        lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
 
        if ( strcasecmp( argv[ 0 ], "lastmod-rdnvalue" ) == 0 ) {
@@ -698,6 +878,17 @@ lastmod_db_config(
 
                ber_str2bv( argv[ 1 ], 0, 1, &lmi->lmi_rdnvalue );
 
+       } else if ( strcasecmp( argv[ 0 ], "lastmod-enabled" ) == 0 ) {
+               if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+                       lmi->lmi_enabled = 1;
+
+               } else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+                       lmi->lmi_enabled = 0;
+
+               } else {
+                       return -1;
+               }
+
        } else {
                return SLAP_CONF_UNKNOWN;
        }
@@ -719,6 +910,9 @@ lastmod_db_open(
 #endif
        static char             tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
 
+       char                    csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
+       struct berval           entryCSN;
+
        if ( !SLAP_LASTMOD( be ) ) {
                fprintf( stderr, "set \"lastmod on\" to make this overlay effective\n" );
                return -1;
@@ -738,37 +932,43 @@ lastmod_db_open(
        ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
 #endif
 
+       slap_get_csn( NULL, csnbuf, sizeof(csnbuf), &entryCSN, 0 );
+
        if ( BER_BVISNULL( &lmi->lmi_rdnvalue ) ) {
                ber_str2bv( "Lastmod", 0, 1, &lmi->lmi_rdnvalue );
        }
 
        snprintf( buf, sizeof( buf ),
-                       "dn: cn=%s,%s\n"
+                       "dn: cn=%s%s%s\n"
                        "objectClass: %s\n"
                        "structuralObjectClass: %s\n"
                        "cn: %s\n"
                        "description: This object contains the last modification to this database\n"
-                       "%s: cn=%s,%s\n"
+                       "%s: cn=%s%s%s\n"
+                       "%s: %s\n"
                        "%s: %s\n"
                        "createTimestamp: %s\n"
                        "creatorsName: %s\n"
+                       "entryCSN: %s\n"
                        "modifyTimestamp: %s\n"
                        "modifiersName: %s\n"
                        "hasSubordinates: FALSE\n",
-                       lmi->lmi_rdnvalue.bv_val, be->be_suffix[0].bv_val,
+                       lmi->lmi_rdnvalue.bv_val, BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ? "" : ",", be->be_suffix[ 0 ].bv_val,
                        lastmod_schema.lms_oc_lastmod->soc_cname.bv_val,
                        lastmod_schema.lms_oc_lastmod->soc_cname.bv_val,
                        lmi->lmi_rdnvalue.bv_val,
-                       lastmod_schema.lms_ad_lastmodDN->ad_cname.bv_val, lmi->lmi_rdnvalue.bv_val, be->be_suffix[0].bv_val,
+                       lastmod_schema.lms_ad_lastmodDN->ad_cname.bv_val,
+                               lmi->lmi_rdnvalue.bv_val, BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ? "" : ",", be->be_suffix[ 0 ].bv_val,
                        lastmod_schema.lms_ad_lastmodType->ad_cname.bv_val, lastmodType[ LASTMOD_ADD ].bv_val,
+                       lastmod_schema.lms_ad_lastmodEnabled->ad_cname.bv_val, lmi->lmi_enabled ? "TRUE" : "FALSE",
                        tmbuf,
-                       BER_BVISNULL( &be->be_rootdn ) ? "" : be->be_rootdn.bv_val,
+                       BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val,
+                       entryCSN.bv_val,
                        tmbuf,
-                       BER_BVISNULL( &be->be_rootdn ) ? "" : be->be_rootdn.bv_val );
+                       BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val );
 
 #if 0
        fprintf( stderr, "# entry:\n%s\n", buf );
-       fflush( stderr );
 #endif
 
        lmi->lmi_e = str2entry( buf );
@@ -786,7 +986,7 @@ lastmod_db_destroy(
        BackendDB *be
 )
 {
-       slap_overinst   *on = (slap_overinst *) be->bd_info;
+       slap_overinst   *on = (slap_overinst *)be->bd_info;
        lastmod_info_t  *lmi = (lastmod_info_t *)on->on_bi.bi_private;
 
        if ( lmi ) {
@@ -816,7 +1016,6 @@ static slap_overinst               lastmod;
 int
 lastmod_init()
 {
-       memset( &lastmod, 0, sizeof( slap_overinst ) );
        lastmod.on_bi.bi_type = "lastmod";
        lastmod.on_bi.bi_db_init = lastmod_db_init;
        lastmod.on_bi.bi_db_config = lastmod_db_config;
@@ -838,7 +1037,7 @@ lastmod_init()
 
 #if SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC
 int
-init_module(int argc, char *argv[])
+init_module( int argc, char *argv[] )
 {
        return lastmod_init();
 }