From e6d027ae8fe40f09ccb49a6b2cfa2136542442fe Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 27 Sep 2007 02:09:38 +0000 Subject: [PATCH] Microsoft-style Update Sequence Numbers --- contrib/slapd-modules/usn/README | 38 ++++ contrib/slapd-modules/usn/usn.c | 329 +++++++++++++++++++++++++++++++ 2 files changed, 367 insertions(+) create mode 100644 contrib/slapd-modules/usn/README create mode 100644 contrib/slapd-modules/usn/usn.c diff --git a/contrib/slapd-modules/usn/README b/contrib/slapd-modules/usn/README new file mode 100644 index 0000000000..05d1398313 --- /dev/null +++ b/contrib/slapd-modules/usn/README @@ -0,0 +1,38 @@ +Copyright 2007 Howard Chu, Symas Corp. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +. + +This directory contains a slapd overlay, usn, that extends slapd +to maintain the usnCreated and usnChanged operational attributes +normally used by Microsoft ActiveDirectory. + +To use the overlay, add: + + moduleload usn.so + ... + + database bdb + ... + overlay usn + +to your slapd configuration file. The schema definitions for the +two USN attributes are hardcoded in this overlay. + +No Makefile is provided. Just compile with an invocation like + gcc -c -I ../../include/ -I ../../servers/slapd -DSLAPD_OVER_USN=SLAPD_MOD_DYNAMIC usn.c + gcc -shared -o usn.so usn.o + +This overlay is only set up to be built as a dynamically loaded module. +On most platforms, in order for the module to be usable, all of the +library dependencies must also be available as shared libraries. + +If you need to build the overlay statically, you will have to move it into the +slapd/overlays directory and edit the Makefile and overlays.c to reference +it. You will also have to define SLAPD_OVER_USN to SLAPD_MOD_STATIC, +and add the relevant libraries to the main slapd link command. diff --git a/contrib/slapd-modules/usn/usn.c b/contrib/slapd-modules/usn/usn.c new file mode 100644 index 0000000000..c102027880 --- /dev/null +++ b/contrib/slapd-modules/usn/usn.c @@ -0,0 +1,329 @@ +/* usn.c - Maintain Microsoft-style Update Sequence Numbers */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 2007 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#ifdef SLAPD_OVER_USN + +#include + +#include +#include + +#include "slap.h" +#include "config.h" + +/* This overlay intercepts write operations and adds a Microsoft-style + * USN to the target entry. + */ + +typedef struct usn_info { + int ui_current; + ldap_pvt_thread_mutex_t ui_mutex; +} usn_info_t; + +static AttributeDescription *ad_usnCreated, *ad_usnChanged; + +static struct { + char *desc; + AttributeDescription **adp; +} as[] = { + { "( 1.2.840.113556.1.2.19 " + "NAME 'uSNCreated' " + "SYNTAX '1.2.840.113556.1.4.906' " + "SINGLE-VALUE " + "NO-USER-MODIFICATION )", + &ad_usnCreated }, + { "( 1.2.840.113556.1.2.120 " + "NAME 'uSNChanged' " + "SYNTAX '1.2.840.113556.1.4.906' " + "SINGLE-VALUE " + "NO-USER-MODIFICATION )", + &ad_usnChanged }, + { NULL } +}; + +static int +usn_func( Operation *op, SlapReply *rs ) +{ + slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; + usn_info_t *ui = on->on_bi.bi_private; + int my_usn; + char intbuf[64]; + struct berval bv[2]; + + ldap_pvt_thread_mutex_lock( &ui->ui_mutex ); + ui->ui_current++; + my_usn = ui->ui_current; + ldap_pvt_thread_mutex_unlock( &ui->ui_mutex ); + + BER_BVZERO(&bv[1]); + bv[0].bv_val = intbuf; + bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn ); + switch(op->o_tag) { + case LDAP_REQ_ADD: + attr_merge( op->ora_e, ad_usnCreated, bv, NULL ); + attr_merge( op->ora_e, ad_usnChanged, bv, NULL ); + break; + case LDAP_REQ_DELETE: + /* Probably need to update root usnLastObjRem */ + break; + default: { + /* Modify, ModDN */ + Modifications *ml, *mod = ch_calloc( sizeof( Modifications ), 1 ); + for ( ml = op->orm_modlist; ml && ml->sml_next; ml = ml->sml_next ); + ml->sml_next = mod; + mod->sml_desc = ad_usnChanged; + mod->sml_numvals = 1; + value_add_one( &mod->sml_values, &bv[0] ); + mod->sml_nvalues = NULL; + mod->sml_op = LDAP_MOD_REPLACE; + mod->sml_flags = 0; + mod->sml_next = NULL; + break; + } + } + return SLAP_CB_CONTINUE; +} + +static int +usn_operational( + Operation *op, + SlapReply *rs ) +{ + slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; + usn_info_t *ui = (usn_info_t *)on->on_bi.bi_private; + + if ( rs->sr_entry && + dn_match( &rs->sr_entry->e_nname, op->o_bd->be_nsuffix )) { + + if ( SLAP_OPATTRS( rs->sr_attr_flags ) || + ad_inlist( ad_usnChanged, rs->sr_attrs )) { + Attribute *a, **ap = NULL; + char intbuf[64]; + struct berval bv; + int my_usn; + + for ( a=rs->sr_entry->e_attrs; a; a=a->a_next ) { + if ( a->a_desc == ad_usnChanged ) + break; + } + + if ( !a ) { + for ( ap = &rs->sr_operational_attrs; *ap; + ap=&(*ap)->a_next ); + + a = attr_alloc( ad_usnChanged ); + *ap = a; + } + + if ( !ap ) { + if ( !rs->sr_flags & REP_ENTRY_MODIFIABLE ) { + rs->sr_entry = entry_dup( rs->sr_entry ); + rs->sr_flags |= + REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED; + a = attr_find( rs->sr_entry->e_attrs, + ad_usnChanged ); + } + ber_bvarray_free( a->a_vals ); + a->a_vals = NULL; + a->a_numvals = 0; + } + ldap_pvt_thread_mutex_lock( &ui->ui_mutex ); + my_usn = ui->ui_current; + ldap_pvt_thread_mutex_unlock( &ui->ui_mutex ); + bv.bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn ); + bv.bv_val = intbuf; + attr_valadd( a, &bv, NULL, 1 ); + } + } + return SLAP_CB_CONTINUE; +} + +/* Read the old USN from the underlying DB. This code is + * stolen from the syncprov overlay. + */ +static int +usn_db_open( + BackendDB *be, + ConfigReply *cr) +{ + slap_overinst *on = (slap_overinst *) be->bd_info; + usn_info_t *ui = (usn_info_t *)on->on_bi.bi_private; + + Connection conn = { 0 }; + OperationBuffer opbuf; + Operation *op; + Entry *e = NULL; + Attribute *a; + int rc; + void *thrctx = NULL; + + thrctx = ldap_pvt_thread_pool_context(); + connection_fake_init( &conn, &opbuf, thrctx ); + op = &opbuf.ob_op; + op->o_bd = be; + op->o_dn = be->be_rootdn; + op->o_ndn = be->be_rootndn; + + rc = overlay_entry_get_ov( op, be->be_nsuffix, NULL, + slap_schema.si_ad_contextCSN, 0, &e, on ); + + if ( e ) { + a = attr_find( e->e_attrs, ad_usnChanged ); + if ( a ) { + ui->ui_current = atoi( a->a_vals[0].bv_val ); + } + overlay_entry_release_ov( op, e, 0, on ); + } + return 0; +} + +static int +usn_db_init( + BackendDB *be, + ConfigReply *cr +) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + usn_info_t *ui; + + if ( SLAP_ISGLOBALOVERLAY( be ) ) { + Debug( LDAP_DEBUG_ANY, + "usn must be instantiated within a database.\n", + 0, 0, 0 ); + return 1; + } + + ui = ch_calloc(1, sizeof(usn_info_t)); + on->on_bi.bi_private = ui; + return 0; +} + +static int +usn_db_close( + BackendDB *be, + ConfigReply *cr +) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + usn_info_t *ui = on->on_bi.bi_private; + Connection conn = {0}; + OperationBuffer opbuf; + Operation *op; + SlapReply rs = {REP_RESULT}; + void *thrctx; + + Modifications mod; + SlapReply rsm = { 0 }; + slap_callback cb = {0}; + char intbuf[64]; + struct berval bv[2]; + + thrctx = ldap_pvt_thread_pool_context(); + connection_fake_init( &conn, &opbuf, thrctx ); + op = &opbuf.ob_op; + op->o_bd = be; + BER_BVZERO( &bv[1] ); + bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", ui->ui_current ); + bv[0].bv_val = intbuf; + mod.sml_numvals = 1; + mod.sml_values = bv; + mod.sml_nvalues = NULL; + mod.sml_desc = ad_usnChanged; + mod.sml_op = LDAP_MOD_REPLACE; + mod.sml_flags = 0; + mod.sml_next = NULL; + + cb.sc_response = slap_null_cb; + op->o_tag = LDAP_REQ_MODIFY; + op->o_callback = &cb; + op->orm_modlist = &mod; + op->orm_no_opattrs = 1; + op->o_dn = be->be_rootdn; + op->o_ndn = be->be_rootndn; + op->o_req_dn = op->o_bd->be_suffix[0]; + op->o_req_ndn = op->o_bd->be_nsuffix[0]; + op->o_bd->bd_info = on->on_info->oi_orig; + op->o_managedsait = SLAP_CONTROL_NONCRITICAL; + op->o_no_schema_check = 1; + op->o_bd->be_modify( op, &rs ); + if ( mod.sml_next != NULL ) { + slap_mods_free( mod.sml_next, 1 ); + } + return 0; +} + +static int +usn_db_destroy( + BackendDB *be, + ConfigReply *cr +) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + ch_free( on->on_bi.bi_private ); + return 0; +} + +/* This overlay is set up for dynamic loading via moduleload. For static + * configuration, you'll need to arrange for the slap_overinst to be + * initialized and registered by some other function inside slapd. + */ + +static slap_overinst usn; + +int +usn_init( void ) +{ + int i, code; + + memset( &usn, 0, sizeof( slap_overinst ) ); + usn.on_bi.bi_type = "usn"; + usn.on_bi.bi_db_init = usn_db_init; + usn.on_bi.bi_db_destroy = usn_db_destroy; + usn.on_bi.bi_db_open = usn_db_open; + usn.on_bi.bi_db_close = usn_db_close; + + usn.on_bi.bi_op_modify = usn_func; + usn.on_bi.bi_op_modrdn = usn_func; + usn.on_bi.bi_op_add = usn_func; + usn.on_bi.bi_op_delete = usn_func; + usn.on_bi.bi_operational = usn_operational; + + for ( i = 0; as[i].desc; i++ ) { + code = register_at( as[i].desc, as[i].adp, 0 ); + if ( code ) { + Debug( LDAP_DEBUG_ANY, + "usn_init: register_at #%d failed\n", i, 0, 0 ); + return code; + } + } + return overlay_register( &usn ); +} + +#if SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC +int +init_module( int argc, char *argv[] ) +{ + return usn_init(); +} +#endif /* SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC */ + +#endif /* defined(SLAPD_OVER_USN) */ -- 2.39.5