summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/back-ldif/modify.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/slapd/back-ldif/modify.c')
-rw-r--r--ldap/servers/slapd/back-ldif/modify.c557
1 files changed, 557 insertions, 0 deletions
diff --git a/ldap/servers/slapd/back-ldif/modify.c b/ldap/servers/slapd/back-ldif/modify.c
new file mode 100644
index 00000000..53f0bb2e
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/modify.c
@@ -0,0 +1,557 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: modify.c
+ *
+ * Functions:
+ *
+ * ldif_back_modify() - ldif backend modify function
+ * update_db() - updates memory and disk db to reflect changes
+ * db2disk() - writes out ldif database to disk
+ * ldifentry_free() - frees an ldif_Entry
+ * ldifentry_dup() - copies an ldif_Entry
+ * ldif_find_entry() - searches an ldif DB for a particular dn
+ * apply_mods() - applies the modifications to an Entry
+ *
+ */
+
+#include "back-ldif.h"
+
+/*Prototypes*/
+void ldifentry_free(ldif_Entry *);
+ldif_Entry * ldifentry_dup(ldif_Entry *);
+int apply_mods( Slapi_Entry *, LDAPMod ** );
+ldif_Entry * ldif_find_entry(Slapi_PBlock *, LDIF *, char *, ldif_Entry **);
+int db2disk(Slapi_PBlock *, LDIF *);
+int update_db(Slapi_PBlock *, LDIF *, ldif_Entry *, ldif_Entry *, int );
+
+/*
+ * Function: ldif_back_modify
+ *
+ * Returns: returns 0 if good, -1 else.
+ *
+ * Description: For changetype: modify, this makes the changes
+ */
+int
+ldif_back_modify( Slapi_PBlock *pb )
+{
+ LDIF *db; /*The ldif file is stored here*/
+ ldif_Entry *entry, *entry2,*prev; /*For db manipulation*/
+ int err; /*House keeping stuff*/
+ LDAPMod **mods; /*Used to apply the modifications*/
+ char *dn; /*Storage for the dn*/
+ char *errbuf = NULL; /* To get error back */
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_modify\n", 0, 0, 0 );
+ prev = NULL;
+
+ /*Get the database, the dn and the mods*/
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_MODIFY_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ) < 0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+ /*Lock the database*/
+ PR_Lock( db->ldif_lock );
+
+ /*
+ * Find the entry we are about to modify.
+ * prev will point to the previous element in the list,
+ * NULL if there is no previous element.
+ */
+ if ( (entry = (ldif_Entry *)ldif_find_entry( pb, db, dn, &prev)) == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+ }
+
+ /*Check acl, note that entry is not an Entry, but a ldif_Entry*/
+ if ( (err = slapi_acl_check_mods( pb, entry->lde_e, mods, &errbuf )) != LDAP_SUCCESS ) {
+ slapi_send_ldap_result( pb, err, NULL, errbuf, 0, NULL );
+ if (errbuf) free (errbuf);
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /* Create a copy of the entry and apply the changes to it */
+ if ( (entry2 = (ldif_Entry *) ldifentry_dup( entry )) == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /*Actually apply the modifications*/
+ if ( (err = apply_mods( entry2->lde_e, mods )) != 0 ) {
+ slapi_send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /* Check for abandon */
+ if ( slapi_op_abandoned( pb ) ) {
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /* Check that the entry still obeys the schema */
+ if ( slapi_entry_schema_check( pb, entry2->lde_e ) != 0 ) {
+ slapi_send_ldap_result( pb, LDAP_OBJECT_CLASS_VIOLATION, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /* Check for abandon again */
+ if ( slapi_op_abandoned( pb ) ) {
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /* Change the entry itself both on disk and in the cache */
+ if ( update_db(pb, db, entry2, prev, LDIF_DB_REPLACE) != 0) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_modify\n", 0, 0, 0 );
+ return( 0 );
+
+ error_return:;
+ if ( entry2 != NULL ) {
+ ldifentry_free( entry2 );
+ }
+
+ return( -1 );
+}
+
+/*
+ * Function: update_db
+ *
+ * Returns: returns 0 if good, -1 else.
+ *
+ * Description: Will update the database in memory, and on disk
+ * if prev == NULL, then the element to be deleted/replaced
+ * is the first in the list.
+ * mode = LDIF_DB_ADD | LDIF_DB_REPLACE | LDIF_DB_DELETE
+ * The database should be locked when this function is called.
+ * Note that on replaces and deletes, the old ldif_Entry's
+ * are freed.
+ */
+int
+update_db(Slapi_PBlock *pb, LDIF *db, ldif_Entry *new, ldif_Entry *prev, int mode)
+{
+ ldif_Entry *tmp; /*Used to free the removed/replaced entries*/
+ char *buf; /*Used to convert entries to strings for output to file*/
+ FILE *fp; /*File ptr to the ldif file*/
+ int len; /*Used by slapi_entry2str*/
+ int db_updated=0; /*Flag to designate if db in memory has been updated*/
+
+ /*Make sure that the database is not null. Everything else can be, though*/
+ if (db == NULL){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*
+ * If we are adding an entry, then prev should be pointing
+ * to the last element in the list, or null if the list is empty,
+ * and new should not be null.
+ */
+ if (mode == LDIF_DB_ADD) {
+
+ /*Make sure there is something to add*/
+ if ( new == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*If prev is null, then there had better be no entries in the list*/
+ if (prev == NULL){
+ if( db->ldif_entries != NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+ /*There are no elements, so let's add the new one*/
+ db->ldif_entries = new;
+ db->ldif_n++;
+
+ /*Set a flag*/
+ db_updated = 1;
+
+ }
+ /*
+ * Last error case to test for is if prev is not null, and prev->next
+ * points to something. This means that we are not at the end of the list
+ */
+ if (prev != NULL) {
+ if (prev->next != NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*We're at the end of the list, so tack the new entry onto the end*/
+ prev->next = new;
+ db->ldif_n++;
+
+ db_updated = 1;
+
+ }
+
+ /*If the database has been updated in memory, update the disk*/
+ if (db_updated && db->ldif_file!=NULL) {
+
+ /*Update the disk by appending to the ldif file*/
+ fp = fopen(db->ldif_file, "a");
+ if (fp == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ /*This is s pretty serious problem, so we exit*/
+ exit(-1);
+ }
+
+ /*Convert the entry to ldif format*/
+ buf = slapi_entry2str(new->lde_e, &len);
+ fprintf(fp, "%s\n", buf);
+ free ( (void *) buf);
+ fclose(fp);
+ return(0);
+ } else {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ fclose(fp);
+ return(-1);
+
+ }
+
+ } else if (mode == LDIF_DB_DELETE){
+
+ /*We're not deleting the first entry in the list*/
+ if (prev != NULL){
+ /*Make sure there is something to delete*/
+ if (prev->next == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+ tmp = prev->next;
+ prev->next = tmp->next;
+ db->ldif_n--;
+ ldifentry_free(tmp);
+
+ db_updated = 1;
+
+ } else { /*We are deleting the first entry in the list*/
+
+ /*Make sure there is something to delete*/
+ if (db->ldif_entries == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ tmp = db->ldif_entries;
+ db->ldif_entries = tmp->next;
+ db->ldif_n--;
+
+ /*Free the entry, and set the flag*/
+ ldifentry_free(tmp);
+ db_updated = 1;
+ }
+
+ /*
+ * Update the disk by rewriting entire ldif file
+ * I know, I know, but simplicity is the key here.
+ */
+ if (db_updated) {
+ return(db2disk(pb, db));
+ } else {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+
+ }
+
+ } else if (mode == LDIF_DB_REPLACE) {
+
+ /*Make sure there is something to replace with*/
+ if ( new == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+
+ /*We're not replacing the first element in the list*/
+ if (prev != NULL){
+
+ /*Make sure there is something to replace*/
+ if (prev->next == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*Splice out the old entry, and put in the new*/
+ tmp = prev->next;
+ prev->next = new;
+ new->next = tmp->next;
+
+ /*Free it*/
+ ldifentry_free(tmp);
+ db_updated = 1;
+ } else { /*We are replacing the first entry in the list*/
+
+ /*Make sure there is something to replace*/
+ if (db->ldif_entries == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*Splice out the old entry, and put in the new*/
+ tmp = db->ldif_entries;
+ db->ldif_entries = new;
+ new->next = tmp->next;
+
+ /*Free it*/
+ ldifentry_free(tmp);
+ db_updated = 1;
+ }
+
+ /*Update the disk by rewriting entire ldif file*/
+ if (db_updated) {
+ return(db2disk(pb, db));
+ } else {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ }
+}
+
+/*
+ * Function: db2disk
+ *
+ * Returns: returns 0 if good, exits else
+ *
+ * Description: Takes an ldif database, db, and writes it out to disk
+ * if it can't open the file, there's trouble, so we exit
+ * because this function is usually called after the db
+ * in memory has been updated.
+ *
+ */
+int
+db2disk(Slapi_PBlock *pb, LDIF *db)
+{
+ ldif_Entry *cur; /*Used for walking down the list*/
+ char *buf; /*temp storage for Entry->ldif converter*/
+ FILE *fp; /*File pointer to ldif target file*/
+ int len; /*length returned by slapi_entry2str*/
+
+ /*Open the file*/
+ fp = fopen(db->ldif_file, "w");
+ if (fp == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ /*This is s pretty serious problem, so we exit*/
+ exit(-1);
+ }
+
+ /*
+ * Walk down the list, converting each entry to a string,
+ * writing the string out to fp
+ */
+ for (cur = db->ldif_entries; cur != NULL; cur = cur->next){
+ buf = slapi_entry2str(cur->lde_e, &len);
+ fprintf(fp, "%s\n",buf);
+ free ( (void *) buf);
+ }
+
+ fclose(fp);
+ return(0);
+
+}
+
+
+/*
+ * Function: ldifentry_free
+ *
+ * Returns: void
+ *
+ * Description: Frees an ldif_Entry
+ */
+void
+ldifentry_free(ldif_Entry *e)
+{
+
+ /*Make sure that there is actually something to free*/
+ if (e == NULL){
+ return;
+ }
+
+ /*Free the entry*/
+ slapi_entry_free(e->lde_e);
+
+ /*Free the entire thing*/
+ free ((void *) e);
+}
+
+/*
+ * Function: ldifentry_dup
+ *
+ * Returns: a pointer to the new ldif_entry, or NULL
+ *
+ * Description: Copies and returns a pointer to a new
+ * ldif_Entry whose contents are a copy of e's contents
+ * Note: uses malloc
+ */
+ldif_Entry *
+ldifentry_dup(ldif_Entry *e)
+{
+ ldif_Entry *new;
+
+ /*Let's make sure that e is not null*/
+ if (e == NULL){
+ return(NULL);
+ }
+
+ /*Allocate a new ldif_entry, and return it if it is null*/
+ new = (ldif_Entry *) malloc( (sizeof(ldif_Entry)));
+ if (new == NULL) {
+ return(new);
+ }
+
+ /*Copy the Entry in e*/
+ new->lde_e = slapi_entry_dup(e->lde_e);
+ new->next = NULL;
+
+ return(new);
+
+
+}
+
+/*
+ * Function: ldif_find_entry
+ *
+ * Returns: A pointer to the matched ldif_Entry, or Null
+ *
+ * Description: Goes down the list of entries in db to find the entry
+ * matching dn. Returns a pointer to the entry,
+ * and sets prev to point to the entry before the match.
+ * If there is no match, prev points to the last
+ * entry in the list, and null is returned.
+ * If the first element matches, prev points to NULL
+ */
+ldif_Entry *
+ldif_find_entry(Slapi_PBlock *pb, LDIF *db, char *dn, ldif_Entry **prev)
+{
+ ldif_Entry *cur; /*Used for walking down the list*/
+ char *finddn, *targetdn; /*Copies of dns for searching */
+ int found_it = 0; /*A flag to denote a successful search*/
+
+ /*Set cur to the start of the list*/
+ cur =db->ldif_entries;
+
+ /*Increase the number of accesses*/
+ db->ldif_tries++;
+
+ /*Make a copy of the target dn, and normalize it*/
+ targetdn = strdup(dn);
+ (void) slapi_dn_normalize(targetdn);
+
+
+ /*Go down the list until we find the entry*/
+ while(cur != NULL) {
+ finddn = strdup(slapi_entry_get_dn(cur->lde_e));
+ (void) slapi_dn_normalize(finddn);
+
+
+ /*Test to see if we got the entry matching the dn*/
+ if (strcasecmp(targetdn, finddn) == 0)
+ {
+ found_it = 1;
+ free ((void *)finddn);
+ db->ldif_hits++;
+ break;
+ }
+
+ /*Udpate the pointers*/
+ *prev = cur;
+ cur = cur->next;
+ free ((void *)finddn);
+
+ }
+
+ free ((void *)targetdn);
+
+
+ /*
+ * If we didn't find a matching entry, we should
+ * return, and let the caller handle this (possible) error
+ */
+ if (!found_it){
+ return(NULL);
+ }
+
+ /*
+ * If the first entry matches, we have to set prev to null,
+ * so the caller knows.
+ */
+ if (*prev == cur){
+ *prev = NULL;
+ }
+
+ return( cur );
+}
+
+/*
+ * Function: apply_mods
+ *
+ * Returns: LDAP_SUCCESS if success
+ *
+ * Description: Applies the modifications specified in mods to e.
+ */
+int
+apply_mods( Slapi_Entry *e, LDAPMod **mods )
+{
+ int err, i, j;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> apply_mods\n", 0, 0, 0 );
+
+ err = LDAP_SUCCESS;
+ for ( j = 0; mods[j] != NULL; j++ ) {
+ switch ( mods[j]->mod_op & ~LDAP_MOD_BVALUES ) {
+ case LDAP_MOD_ADD:
+ LDAPDebug( LDAP_DEBUG_ARGS, " add: %s\n",
+ mods[j]->mod_type, 0, 0 );
+ err = slapi_entry_add_values( e, mods[j]->mod_type,
+ mods[j]->mod_bvalues );
+ break;
+
+ case LDAP_MOD_DELETE:
+ LDAPDebug( LDAP_DEBUG_ARGS, " delete: %s\n",
+ mods[j]->mod_type, 0, 0 );
+ err = slapi_entry_delete_values( e, mods[j]->mod_type,
+ mods[j]->mod_bvalues );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ LDAPDebug( LDAP_DEBUG_ARGS, " replace: %s\n",
+ mods[j]->mod_type, 0, 0 );
+ err = entry_replace_values( e, mods[j]->mod_type,
+ mods[j]->mod_bvalues );
+ break;
+ }
+ for ( i = 0; mods[j]->mod_bvalues != NULL &&
+ mods[j]->mod_bvalues[i] != NULL; i++ ) {
+ LDAPDebug( LDAP_DEBUG_ARGS, " %s: %s\n",
+ mods[j]->mod_type, mods[j]->mod_bvalues[i]->bv_val,
+ 0 );
+ }
+ LDAPDebug( LDAP_DEBUG_ARGS, " -\n", 0, 0, 0 );
+
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= apply_mods %d\n", err, 0, 0 );
+ return( err );
+}