mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-12-25 09:09:54 -05:00
Microsoft-style Update Sequence Numbers
This commit is contained in:
parent
ebd5d088cf
commit
e6d027ae8f
2 changed files with 367 additions and 0 deletions
38
contrib/slapd-modules/usn/README
Normal file
38
contrib/slapd-modules/usn/README
Normal 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.
|
||||
329
contrib/slapd-modules/usn/usn.c
Normal file
329
contrib/slapd-modules/usn/usn.c
Normal 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) */
|
||||
Loading…
Reference in a new issue