modify.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. #ifdef HAVE_CONFIG_H
  39. # include <config.h>
  40. #endif
  41. /*
  42. * File: modify.c
  43. *
  44. * Functions:
  45. *
  46. * ldif_back_modify() - ldif backend modify function
  47. * update_db() - updates memory and disk db to reflect changes
  48. * db2disk() - writes out ldif database to disk
  49. * ldifentry_free() - frees an ldif_Entry
  50. * ldifentry_dup() - copies an ldif_Entry
  51. * ldif_find_entry() - searches an ldif DB for a particular dn
  52. * apply_mods() - applies the modifications to an Entry
  53. *
  54. */
  55. #include "back-ldif.h"
  56. /*Prototypes*/
  57. void ldifentry_free(ldif_Entry *);
  58. ldif_Entry * ldifentry_dup(ldif_Entry *);
  59. int apply_mods( Slapi_Entry *, LDAPMod ** );
  60. ldif_Entry * ldif_find_entry(Slapi_PBlock *, LDIF *, char *, ldif_Entry **);
  61. int db2disk(Slapi_PBlock *, LDIF *);
  62. int update_db(Slapi_PBlock *, LDIF *, ldif_Entry *, ldif_Entry *, int );
  63. /*
  64. * Function: ldif_back_modify
  65. *
  66. * Returns: returns 0 if good, -1 else.
  67. *
  68. * Description: For changetype: modify, this makes the changes
  69. */
  70. int
  71. ldif_back_modify( Slapi_PBlock *pb )
  72. {
  73. LDIF *db; /*The ldif file is stored here*/
  74. ldif_Entry *entry, *entry2,*prev; /*For db manipulation*/
  75. int err; /*House keeping stuff*/
  76. LDAPMod **mods; /*Used to apply the modifications*/
  77. char *dn; /*Storage for the dn*/
  78. char *errbuf = NULL; /* To get error back */
  79. LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_modify\n", 0, 0, 0 );
  80. prev = NULL;
  81. /*Get the database, the dn and the mods*/
  82. if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db ) < 0 ||
  83. slapi_pblock_get( pb, SLAPI_MODIFY_TARGET, &dn ) < 0 ||
  84. slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ) < 0){
  85. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  86. return(-1);
  87. }
  88. /*Lock the database*/
  89. PR_Lock( db->ldif_lock );
  90. /*
  91. * Find the entry we are about to modify.
  92. * prev will point to the previous element in the list,
  93. * NULL if there is no previous element.
  94. */
  95. if ( (entry = (ldif_Entry *)ldif_find_entry( pb, db, dn, &prev)) == NULL ) {
  96. slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
  97. PR_Unlock( db->ldif_lock );
  98. return( -1 );
  99. }
  100. /*Check acl, note that entry is not an Entry, but a ldif_Entry*/
  101. if ( (err = slapi_acl_check_mods( pb, entry->lde_e, mods, &errbuf )) != LDAP_SUCCESS ) {
  102. slapi_send_ldap_result( pb, err, NULL, errbuf, 0, NULL );
  103. if (errbuf) free (errbuf);
  104. PR_Unlock( db->ldif_lock );
  105. goto error_return;
  106. }
  107. /* Create a copy of the entry and apply the changes to it */
  108. if ( (entry2 = (ldif_Entry *) ldifentry_dup( entry )) == NULL ) {
  109. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  110. PR_Unlock( db->ldif_lock );
  111. goto error_return;
  112. }
  113. /*Actually apply the modifications*/
  114. if ( (err = apply_mods( entry2->lde_e, mods )) != 0 ) {
  115. slapi_send_ldap_result( pb, err, NULL, NULL, 0, NULL );
  116. PR_Unlock( db->ldif_lock );
  117. goto error_return;
  118. }
  119. /* Check for abandon */
  120. if ( slapi_op_abandoned( pb ) ) {
  121. PR_Unlock( db->ldif_lock );
  122. goto error_return;
  123. }
  124. /* Check that the entry still obeys the schema */
  125. if ( slapi_entry_schema_check( pb, entry2->lde_e ) != 0 ) {
  126. slapi_send_ldap_result( pb, LDAP_OBJECT_CLASS_VIOLATION, NULL, NULL, 0, NULL );
  127. PR_Unlock( db->ldif_lock );
  128. goto error_return;
  129. }
  130. /* Check if the attribute values in the mods obey the syntaxes */
  131. if ( slapi_mods_syntax_check( pb, mods, 0 ) != 0 ) {
  132. slapi_send_ldap_result( pb, LDAP_INVALID_SYNTAX, NULL, NULL, 0, NULL );
  133. PR_Unlock( db->ldif_lock );
  134. goto error_return;
  135. }
  136. /* Check for abandon again */
  137. if ( slapi_op_abandoned( pb ) ) {
  138. PR_Unlock( db->ldif_lock );
  139. goto error_return;
  140. }
  141. /* Change the entry itself both on disk and in the cache */
  142. if ( update_db(pb, db, entry2, prev, LDIF_DB_REPLACE) != 0) {
  143. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  144. PR_Unlock( db->ldif_lock );
  145. goto error_return;
  146. }
  147. slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
  148. PR_Unlock( db->ldif_lock );
  149. LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_modify\n", 0, 0, 0 );
  150. return( 0 );
  151. error_return:;
  152. if ( entry2 != NULL ) {
  153. ldifentry_free( entry2 );
  154. }
  155. return( -1 );
  156. }
  157. /*
  158. * Function: update_db
  159. *
  160. * Returns: returns 0 if good, -1 else.
  161. *
  162. * Description: Will update the database in memory, and on disk
  163. * if prev == NULL, then the element to be deleted/replaced
  164. * is the first in the list.
  165. * mode = LDIF_DB_ADD | LDIF_DB_REPLACE | LDIF_DB_DELETE
  166. * The database should be locked when this function is called.
  167. * Note that on replaces and deletes, the old ldif_Entry's
  168. * are freed.
  169. */
  170. int
  171. update_db(Slapi_PBlock *pb, LDIF *db, ldif_Entry *new, ldif_Entry *prev, int mode)
  172. {
  173. ldif_Entry *tmp; /*Used to free the removed/replaced entries*/
  174. char *buf; /*Used to convert entries to strings for output to file*/
  175. FILE *fp; /*File ptr to the ldif file*/
  176. int len; /*Used by slapi_entry2str*/
  177. int db_updated=0; /*Flag to designate if db in memory has been updated*/
  178. /*Make sure that the database is not null. Everything else can be, though*/
  179. if (db == NULL){
  180. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  181. return(-1);
  182. }
  183. /*
  184. * If we are adding an entry, then prev should be pointing
  185. * to the last element in the list, or null if the list is empty,
  186. * and new should not be null.
  187. */
  188. if (mode == LDIF_DB_ADD) {
  189. /*Make sure there is something to add*/
  190. if ( new == NULL ) {
  191. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  192. return(-1);
  193. }
  194. /*If prev is null, then there had better be no entries in the list*/
  195. if (prev == NULL){
  196. if( db->ldif_entries != NULL) {
  197. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  198. return(-1);
  199. }
  200. /*There are no elements, so let's add the new one*/
  201. db->ldif_entries = new;
  202. db->ldif_n++;
  203. /*Set a flag*/
  204. db_updated = 1;
  205. }
  206. /*
  207. * Last error case to test for is if prev is not null, and prev->next
  208. * points to something. This means that we are not at the end of the list
  209. */
  210. if (prev != NULL) {
  211. if (prev->next != NULL) {
  212. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  213. return(-1);
  214. }
  215. /*We're at the end of the list, so tack the new entry onto the end*/
  216. prev->next = new;
  217. db->ldif_n++;
  218. db_updated = 1;
  219. }
  220. /*If the database has been updated in memory, update the disk*/
  221. if (db_updated && db->ldif_file!=NULL) {
  222. /*Update the disk by appending to the ldif file*/
  223. fp = fopen(db->ldif_file, "a");
  224. if (fp == NULL) {
  225. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  226. /*This is s pretty serious problem, so we exit*/
  227. exit(-1);
  228. }
  229. /*Convert the entry to ldif format*/
  230. buf = slapi_entry2str(new->lde_e, &len);
  231. fprintf(fp, "%s\n", buf);
  232. free ( (void *) buf);
  233. fclose(fp);
  234. return(0);
  235. } else {
  236. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  237. fclose(fp);
  238. return(-1);
  239. }
  240. } else if (mode == LDIF_DB_DELETE){
  241. /*We're not deleting the first entry in the list*/
  242. if (prev != NULL){
  243. /*Make sure there is something to delete*/
  244. if (prev->next == NULL) {
  245. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  246. return(-1);
  247. }
  248. tmp = prev->next;
  249. prev->next = tmp->next;
  250. db->ldif_n--;
  251. ldifentry_free(tmp);
  252. db_updated = 1;
  253. } else { /*We are deleting the first entry in the list*/
  254. /*Make sure there is something to delete*/
  255. if (db->ldif_entries == NULL) {
  256. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  257. return(-1);
  258. }
  259. tmp = db->ldif_entries;
  260. db->ldif_entries = tmp->next;
  261. db->ldif_n--;
  262. /*Free the entry, and set the flag*/
  263. ldifentry_free(tmp);
  264. db_updated = 1;
  265. }
  266. /*
  267. * Update the disk by rewriting entire ldif file
  268. * I know, I know, but simplicity is the key here.
  269. */
  270. if (db_updated) {
  271. return(db2disk(pb, db));
  272. } else {
  273. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  274. return(-1);
  275. }
  276. } else if (mode == LDIF_DB_REPLACE) {
  277. /*Make sure there is something to replace with*/
  278. if ( new == NULL ) {
  279. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  280. return(-1);
  281. }
  282. /*We're not replacing the first element in the list*/
  283. if (prev != NULL){
  284. /*Make sure there is something to replace*/
  285. if (prev->next == NULL) {
  286. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  287. return(-1);
  288. }
  289. /*Splice out the old entry, and put in the new*/
  290. tmp = prev->next;
  291. prev->next = new;
  292. new->next = tmp->next;
  293. /*Free it*/
  294. ldifentry_free(tmp);
  295. db_updated = 1;
  296. } else { /*We are replacing the first entry in the list*/
  297. /*Make sure there is something to replace*/
  298. if (db->ldif_entries == NULL) {
  299. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  300. return(-1);
  301. }
  302. /*Splice out the old entry, and put in the new*/
  303. tmp = db->ldif_entries;
  304. db->ldif_entries = new;
  305. new->next = tmp->next;
  306. /*Free it*/
  307. ldifentry_free(tmp);
  308. db_updated = 1;
  309. }
  310. /*Update the disk by rewriting entire ldif file*/
  311. if (db_updated) {
  312. return(db2disk(pb, db));
  313. } else {
  314. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  315. return(-1);
  316. }
  317. }
  318. }
  319. /*
  320. * Function: db2disk
  321. *
  322. * Returns: returns 0 if good, exits else
  323. *
  324. * Description: Takes an ldif database, db, and writes it out to disk
  325. * if it can't open the file, there's trouble, so we exit
  326. * because this function is usually called after the db
  327. * in memory has been updated.
  328. *
  329. */
  330. int
  331. db2disk(Slapi_PBlock *pb, LDIF *db)
  332. {
  333. ldif_Entry *cur; /*Used for walking down the list*/
  334. char *buf; /*temp storage for Entry->ldif converter*/
  335. FILE *fp; /*File pointer to ldif target file*/
  336. int len; /*length returned by slapi_entry2str*/
  337. /*Open the file*/
  338. fp = fopen(db->ldif_file, "w");
  339. if (fp == NULL) {
  340. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  341. /*This is s pretty serious problem, so we exit*/
  342. exit(-1);
  343. }
  344. /*
  345. * Walk down the list, converting each entry to a string,
  346. * writing the string out to fp
  347. */
  348. for (cur = db->ldif_entries; cur != NULL; cur = cur->next){
  349. buf = slapi_entry2str(cur->lde_e, &len);
  350. fprintf(fp, "%s\n",buf);
  351. free ( (void *) buf);
  352. }
  353. fclose(fp);
  354. return(0);
  355. }
  356. /*
  357. * Function: ldifentry_free
  358. *
  359. * Returns: void
  360. *
  361. * Description: Frees an ldif_Entry
  362. */
  363. void
  364. ldifentry_free(ldif_Entry *e)
  365. {
  366. /*Make sure that there is actually something to free*/
  367. if (e == NULL){
  368. return;
  369. }
  370. /*Free the entry*/
  371. slapi_entry_free(e->lde_e);
  372. /*Free the entire thing*/
  373. free ((void *) e);
  374. }
  375. /*
  376. * Function: ldifentry_dup
  377. *
  378. * Returns: a pointer to the new ldif_entry, or NULL
  379. *
  380. * Description: Copies and returns a pointer to a new
  381. * ldif_Entry whose contents are a copy of e's contents
  382. * Note: uses malloc
  383. */
  384. ldif_Entry *
  385. ldifentry_dup(ldif_Entry *e)
  386. {
  387. ldif_Entry *new;
  388. /*Let's make sure that e is not null*/
  389. if (e == NULL){
  390. return(NULL);
  391. }
  392. /*Allocate a new ldif_entry, and return it if it is null*/
  393. new = (ldif_Entry *) malloc( (sizeof(ldif_Entry)));
  394. if (new == NULL) {
  395. return(new);
  396. }
  397. /*Copy the Entry in e*/
  398. new->lde_e = slapi_entry_dup(e->lde_e);
  399. new->next = NULL;
  400. return(new);
  401. }
  402. /*
  403. * Function: ldif_find_entry
  404. *
  405. * Returns: A pointer to the matched ldif_Entry, or Null
  406. *
  407. * Description: Goes down the list of entries in db to find the entry
  408. * matching dn. Returns a pointer to the entry,
  409. * and sets prev to point to the entry before the match.
  410. * If there is no match, prev points to the last
  411. * entry in the list, and null is returned.
  412. * If the first element matches, prev points to NULL
  413. */
  414. ldif_Entry *
  415. ldif_find_entry(Slapi_PBlock *pb, LDIF *db, char *dn, ldif_Entry **prev)
  416. {
  417. ldif_Entry *cur; /*Used for walking down the list*/
  418. char *finddn, *targetdn; /*Copies of dns for searching */
  419. int found_it = 0; /*A flag to denote a successful search*/
  420. /*Set cur to the start of the list*/
  421. cur =db->ldif_entries;
  422. /*Increase the number of accesses*/
  423. db->ldif_tries++;
  424. /*Make a copy of the target dn, and normalize it*/
  425. targetdn = strdup(dn);
  426. (void) slapi_dn_normalize(targetdn);
  427. /*Go down the list until we find the entry*/
  428. while(cur != NULL) {
  429. finddn = strdup(slapi_entry_get_dn(cur->lde_e));
  430. (void) slapi_dn_normalize(finddn);
  431. /*Test to see if we got the entry matching the dn*/
  432. if (strcasecmp(targetdn, finddn) == 0)
  433. {
  434. found_it = 1;
  435. free ((void *)finddn);
  436. db->ldif_hits++;
  437. break;
  438. }
  439. /*Udpate the pointers*/
  440. *prev = cur;
  441. cur = cur->next;
  442. free ((void *)finddn);
  443. }
  444. free ((void *)targetdn);
  445. /*
  446. * If we didn't find a matching entry, we should
  447. * return, and let the caller handle this (possible) error
  448. */
  449. if (!found_it){
  450. return(NULL);
  451. }
  452. /*
  453. * If the first entry matches, we have to set prev to null,
  454. * so the caller knows.
  455. */
  456. if (*prev == cur){
  457. *prev = NULL;
  458. }
  459. return( cur );
  460. }
  461. /*
  462. * Function: apply_mods
  463. *
  464. * Returns: LDAP_SUCCESS if success
  465. *
  466. * Description: Applies the modifications specified in mods to e.
  467. */
  468. int
  469. apply_mods( Slapi_Entry *e, LDAPMod **mods )
  470. {
  471. int err, i, j;
  472. LDAPDebug( LDAP_DEBUG_TRACE, "=> apply_mods\n", 0, 0, 0 );
  473. err = LDAP_SUCCESS;
  474. for ( j = 0; mods[j] != NULL; j++ ) {
  475. switch ( mods[j]->mod_op & ~LDAP_MOD_BVALUES ) {
  476. case LDAP_MOD_ADD:
  477. LDAPDebug( LDAP_DEBUG_ARGS, " add: %s\n",
  478. mods[j]->mod_type, 0, 0 );
  479. err = slapi_entry_add_values( e, mods[j]->mod_type,
  480. mods[j]->mod_bvalues );
  481. break;
  482. case LDAP_MOD_DELETE:
  483. LDAPDebug( LDAP_DEBUG_ARGS, " delete: %s\n",
  484. mods[j]->mod_type, 0, 0 );
  485. err = slapi_entry_delete_values( e, mods[j]->mod_type,
  486. mods[j]->mod_bvalues );
  487. break;
  488. case LDAP_MOD_REPLACE:
  489. LDAPDebug( LDAP_DEBUG_ARGS, " replace: %s\n",
  490. mods[j]->mod_type, 0, 0 );
  491. err = entry_replace_values( e, mods[j]->mod_type,
  492. mods[j]->mod_bvalues );
  493. break;
  494. }
  495. for ( i = 0; mods[j]->mod_bvalues != NULL &&
  496. mods[j]->mod_bvalues[i] != NULL; i++ ) {
  497. LDAPDebug( LDAP_DEBUG_ARGS, " %s: %s\n",
  498. mods[j]->mod_type, mods[j]->mod_bvalues[i]->bv_val,
  499. 0 );
  500. }
  501. LDAPDebug( LDAP_DEBUG_ARGS, " -\n", 0, 0, 0 );
  502. if ( err != LDAP_SUCCESS ) {
  503. break;
  504. }
  505. }
  506. LDAPDebug( LDAP_DEBUG_TRACE, "<= apply_mods %d\n", err, 0, 0 );
  507. return( err );
  508. }