Microsoft-style Update Sequence Numbers

This commit is contained in:
Howard Chu 2007-09-27 02:09:38 +00:00
parent ebd5d088cf
commit e6d027ae8f
2 changed files with 367 additions and 0 deletions

View file

@ -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
<http://www.OpenLDAP.org/license.html>.
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 <path to>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.

View file

@ -0,0 +1,329 @@
/* usn.c - Maintain Microsoft-style Update Sequence Numbers */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* 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
* <http://www.OpenLDAP.org/license.html>.
*/
/* ACKNOWLEDGEMENTS:
* This work was initially developed by Howard Chu for inclusion in
* OpenLDAP Software.
*/
#include "portable.h"
#ifdef SLAPD_OVER_USN
#include <stdio.h>
#include <ac/string.h>
#include <ac/socket.h>
#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) */