1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18** DAV extension module for Apache 2.0.* 19** - Database support using DBM-style databases, 20** part of the filesystem repository implementation 21*/ 22 23/* 24** This implementation uses a SDBM database per file and directory to 25** record the properties. These databases are kept in a subdirectory (of 26** the directory in question or the directory that holds the file in 27** question) named by the macro DAV_FS_STATE_DIR (.DAV). The filename of the 28** database is equivalent to the target filename, and is 29** DAV_FS_STATE_FILE_FOR_DIR (.state_for_dir) for the directory itself. 30*/ 31 32#include "apr_strings.h" 33#include "apr_file_io.h" 34 35#include "apr_dbm.h" 36 37#define APR_WANT_BYTEFUNC 38#include "apr_want.h" /* for ntohs and htons */ 39 40#include "mod_dav.h" 41#include "repos.h" 42#include "http_log.h" 43#include "http_main.h" /* for ap_server_conf */ 44 45APLOG_USE_MODULE(dav_fs); 46 47struct dav_db { 48 apr_pool_t *pool; 49 apr_dbm_t *file; 50 51 /* when used as a property database: */ 52 53 int version; /* *minor* version of this db */ 54 55 dav_buffer ns_table; /* table of namespace URIs */ 56 short ns_count; /* number of entries in table */ 57 int ns_table_dirty; /* ns_table was modified */ 58 apr_hash_t *uri_index; /* map URIs to (1-based) table indices */ 59 60 dav_buffer wb_key; /* work buffer for dav_gdbm_key */ 61 62 apr_datum_t iter; /* iteration key */ 63}; 64 65/* ------------------------------------------------------------------------- 66 * 67 * GENERIC DBM ACCESS 68 * 69 * For the most part, this just uses the APR DBM functions. They are wrapped 70 * a bit with some error handling (using the mod_dav error functions). 71 */ 72 73void dav_dbm_get_statefiles(apr_pool_t *p, const char *fname, 74 const char **state1, const char **state2) 75{ 76 if (fname == NULL) 77 fname = DAV_FS_STATE_FILE_FOR_DIR; 78 79 apr_dbm_get_usednames(p, fname, state1, state2); 80} 81 82static dav_error * dav_fs_dbm_error(dav_db *db, apr_pool_t *p, 83 apr_status_t status) 84{ 85 int errcode; 86 const char *errstr; 87 dav_error *err; 88 char errbuf[200]; 89 90 if (status == APR_SUCCESS) 91 return NULL; 92 93 p = db ? db->pool : p; 94 95 /* There might not be a <db> if we had problems creating it. */ 96 if (db == NULL) { 97 errcode = 1; 98 errstr = "Could not open property database."; 99 if (APR_STATUS_IS_EDSOOPEN(status)) 100 ap_log_error(APLOG_MARK, APLOG_CRIT, status, ap_server_conf, APLOGNO(00576) 101 "The DBM driver could not be loaded"); 102 } 103 else { 104 (void) apr_dbm_geterror(db->file, &errcode, errbuf, sizeof(errbuf)); 105 errstr = apr_pstrdup(p, errbuf); 106 } 107 108 err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, errcode, status, errstr); 109 return err; 110} 111 112/* ensure that our state subdirectory is present */ 113/* ### does this belong here or in dav_fs_repos.c ?? */ 114void dav_fs_ensure_state_dir(apr_pool_t * p, const char *dirname) 115{ 116 const char *pathname = apr_pstrcat(p, dirname, "/" DAV_FS_STATE_DIR, NULL); 117 118 /* ### do we need to deal with the umask? */ 119 120 /* just try to make it, ignoring any resulting errors */ 121 (void) apr_dir_make(pathname, APR_OS_DEFAULT, p); 122} 123 124/* dav_dbm_open_direct: Opens a *dbm database specified by path. 125 * ro = boolean read-only flag. 126 */ 127dav_error * dav_dbm_open_direct(apr_pool_t *p, const char *pathname, int ro, 128 dav_db **pdb) 129{ 130 apr_status_t status; 131 apr_dbm_t *file = NULL; 132 133 *pdb = NULL; 134 135 if ((status = apr_dbm_open(&file, pathname, 136 ro ? APR_DBM_READONLY : APR_DBM_RWCREATE, 137 APR_OS_DEFAULT, p)) 138 != APR_SUCCESS 139 && !ro) { 140 /* ### do something with 'status' */ 141 142 /* we can't continue if we couldn't open the file 143 and we need to write */ 144 return dav_fs_dbm_error(NULL, p, status); 145 } 146 147 /* may be NULL if we tried to open a non-existent db as read-only */ 148 if (file != NULL) { 149 /* we have an open database... return it */ 150 *pdb = apr_pcalloc(p, sizeof(**pdb)); 151 (*pdb)->pool = p; 152 (*pdb)->file = file; 153 } 154 155 return NULL; 156} 157 158static dav_error * dav_dbm_open(apr_pool_t * p, const dav_resource *resource, 159 int ro, dav_db **pdb) 160{ 161 const char *dirpath; 162 const char *fname; 163 const char *pathname; 164 165 /* Get directory and filename for resource */ 166 /* ### should test this result value... */ 167 (void) dav_fs_dir_file_name(resource, &dirpath, &fname); 168 169 /* If not opening read-only, ensure the state dir exists */ 170 if (!ro) { 171 /* ### what are the perf implications of always checking this? */ 172 dav_fs_ensure_state_dir(p, dirpath); 173 } 174 175 pathname = apr_pstrcat(p, dirpath, "/" DAV_FS_STATE_DIR "/", 176 fname ? fname : DAV_FS_STATE_FILE_FOR_DIR, 177 NULL); 178 179 /* ### readers cannot open while a writer has this open; we should 180 ### perform a few retries with random pauses. */ 181 182 /* ### do we need to deal with the umask? */ 183 184 return dav_dbm_open_direct(p, pathname, ro, pdb); 185} 186 187void dav_dbm_close(dav_db *db) 188{ 189 apr_dbm_close(db->file); 190} 191 192dav_error * dav_dbm_fetch(dav_db *db, apr_datum_t key, apr_datum_t *pvalue) 193{ 194 apr_status_t status; 195 196 if (!key.dptr) { 197 /* no key could be created (namespace not known) => no value */ 198 memset(pvalue, 0, sizeof(*pvalue)); 199 status = APR_SUCCESS; 200 } else { 201 status = apr_dbm_fetch(db->file, key, pvalue); 202 } 203 204 return dav_fs_dbm_error(db, NULL, status); 205} 206 207dav_error * dav_dbm_store(dav_db *db, apr_datum_t key, apr_datum_t value) 208{ 209 apr_status_t status = apr_dbm_store(db->file, key, value); 210 211 return dav_fs_dbm_error(db, NULL, status); 212} 213 214dav_error * dav_dbm_delete(dav_db *db, apr_datum_t key) 215{ 216 apr_status_t status = apr_dbm_delete(db->file, key); 217 218 return dav_fs_dbm_error(db, NULL, status); 219} 220 221int dav_dbm_exists(dav_db *db, apr_datum_t key) 222{ 223 return apr_dbm_exists(db->file, key); 224} 225 226static dav_error * dav_dbm_firstkey(dav_db *db, apr_datum_t *pkey) 227{ 228 apr_status_t status = apr_dbm_firstkey(db->file, pkey); 229 230 return dav_fs_dbm_error(db, NULL, status); 231} 232 233static dav_error * dav_dbm_nextkey(dav_db *db, apr_datum_t *pkey) 234{ 235 apr_status_t status = apr_dbm_nextkey(db->file, pkey); 236 237 return dav_fs_dbm_error(db, NULL, status); 238} 239 240void dav_dbm_freedatum(dav_db *db, apr_datum_t data) 241{ 242 apr_dbm_freedatum(db->file, data); 243} 244 245/* ------------------------------------------------------------------------- 246 * 247 * PROPERTY DATABASE FUNCTIONS 248 */ 249 250 251#define DAV_GDBM_NS_KEY "METADATA" 252#define DAV_GDBM_NS_KEY_LEN 8 253 254typedef struct { 255 unsigned char major; 256#define DAV_DBVSN_MAJOR 4 257 /* 258 ** V4 -- 0.9.9 .. 259 ** Prior versions could have keys or values with invalid 260 ** namespace prefixes as a result of the xmlns="" form not 261 ** resetting the default namespace to be "no namespace". The 262 ** namespace would be set to "" which is invalid; it should 263 ** be set to "no namespace". 264 ** 265 ** V3 -- 0.9.8 266 ** Prior versions could have values with invalid namespace 267 ** prefixes due to an incorrect mapping of input to propdb 268 ** namespace indices. Version bumped to obsolete the old 269 ** values. 270 ** 271 ** V2 -- 0.9.7 272 ** This introduced the xml:lang value into the property value's 273 ** record in the propdb. 274 ** 275 ** V1 -- .. 0.9.6 276 ** Initial version. 277 */ 278 279 280 unsigned char minor; 281#define DAV_DBVSN_MINOR 0 282 283 short ns_count; 284 285} dav_propdb_metadata; 286 287struct dav_deadprop_rollback { 288 apr_datum_t key; 289 apr_datum_t value; 290}; 291 292struct dav_namespace_map { 293 int *ns_map; 294}; 295 296/* 297** Internal function to build a key 298** 299** WARNING: returns a pointer to a "static" buffer holding the key. The 300** value must be copied or no longer used if this function is 301** called again. 302*/ 303static apr_datum_t dav_build_key(dav_db *db, const dav_prop_name *name) 304{ 305 char nsbuf[20]; 306 apr_size_t l_ns, l_name = strlen(name->name); 307 apr_datum_t key = { 0 }; 308 309 /* 310 * Convert namespace ID to a string. "no namespace" is an empty string, 311 * so the keys will have the form ":name". Otherwise, the keys will 312 * have the form "#:name". 313 */ 314 if (*name->ns == '\0') { 315 nsbuf[0] = '\0'; 316 l_ns = 0; 317 } 318 else { 319 long ns_id = (long)apr_hash_get(db->uri_index, name->ns, 320 APR_HASH_KEY_STRING); 321 322 323 if (ns_id == 0) { 324 /* the namespace was not found(!) */ 325 return key; /* zeroed */ 326 } 327 328 l_ns = apr_snprintf(nsbuf, sizeof(nsbuf), "%ld", ns_id - 1); 329 } 330 331 /* assemble: #:name */ 332 dav_set_bufsize(db->pool, &db->wb_key, l_ns + 1 + l_name + 1); 333 memcpy(db->wb_key.buf, nsbuf, l_ns); 334 db->wb_key.buf[l_ns] = ':'; 335 memcpy(&db->wb_key.buf[l_ns + 1], name->name, l_name + 1); 336 337 /* build the database key */ 338 key.dsize = l_ns + 1 + l_name + 1; 339 key.dptr = db->wb_key.buf; 340 341 return key; 342} 343 344static void dav_append_prop(apr_pool_t *pool, 345 const char *name, const char *value, 346 apr_text_header *phdr) 347{ 348 const char *s; 349 const char *lang = value; 350 351 /* skip past the xml:lang value */ 352 value += strlen(lang) + 1; 353 354 if (*value == '\0') { 355 /* the property is an empty value */ 356 if (*name == ':') { 357 /* "no namespace" case */ 358 s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name+1); 359 } 360 else { 361 s = apr_psprintf(pool, "<ns%s/>" DEBUG_CR, name); 362 } 363 } 364 else if (*lang != '\0') { 365 if (*name == ':') { 366 /* "no namespace" case */ 367 s = apr_psprintf(pool, "<%s xml:lang=\"%s\">%s</%s>" DEBUG_CR, 368 name+1, lang, value, name+1); 369 } 370 else { 371 s = apr_psprintf(pool, "<ns%s xml:lang=\"%s\">%s</ns%s>" DEBUG_CR, 372 name, lang, value, name); 373 } 374 } 375 else if (*name == ':') { 376 /* "no namespace" case */ 377 s = apr_psprintf(pool, "<%s>%s</%s>" DEBUG_CR, name+1, value, name+1); 378 } 379 else { 380 s = apr_psprintf(pool, "<ns%s>%s</ns%s>" DEBUG_CR, name, value, name); 381 } 382 383 apr_text_append(pool, phdr, s); 384} 385 386static dav_error * dav_propdb_open(apr_pool_t *pool, 387 const dav_resource *resource, int ro, 388 dav_db **pdb) 389{ 390 dav_db *db; 391 dav_error *err; 392 apr_datum_t key; 393 apr_datum_t value = { 0 }; 394 395 *pdb = NULL; 396 397 /* 398 ** Return if an error occurred, or there is no database. 399 ** 400 ** NOTE: db could be NULL if we attempted to open a readonly 401 ** database that doesn't exist. If we require read/write 402 ** access, then a database was created and opened. 403 */ 404 if ((err = dav_dbm_open(pool, resource, ro, &db)) != NULL 405 || db == NULL) 406 return err; 407 408 db->uri_index = apr_hash_make(pool); 409 410 key.dptr = DAV_GDBM_NS_KEY; 411 key.dsize = DAV_GDBM_NS_KEY_LEN; 412 if ((err = dav_dbm_fetch(db, key, &value)) != NULL) { 413 /* ### push a higher-level description? */ 414 return err; 415 } 416 417 if (value.dptr == NULL) { 418 dav_propdb_metadata m = { 419 DAV_DBVSN_MAJOR, DAV_DBVSN_MINOR, 0 420 }; 421 422 /* 423 ** If there is no METADATA key, then the database may be 424 ** from versions 0.9.0 .. 0.9.4 (which would be incompatible). 425 ** These can be identified by the presence of an NS_TABLE entry. 426 */ 427 key.dptr = "NS_TABLE"; 428 key.dsize = 8; 429 if (dav_dbm_exists(db, key)) { 430 dav_dbm_close(db); 431 432 /* call it a major version error */ 433 return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 434 DAV_ERR_PROP_BAD_MAJOR, 0, 435 "Prop database has the wrong major " 436 "version number and cannot be used."); 437 } 438 439 /* initialize a new metadata structure */ 440 dav_set_bufsize(pool, &db->ns_table, sizeof(m)); 441 memcpy(db->ns_table.buf, &m, sizeof(m)); 442 } 443 else { 444 dav_propdb_metadata m; 445 long ns; 446 const char *uri; 447 448 dav_set_bufsize(pool, &db->ns_table, value.dsize); 449 memcpy(db->ns_table.buf, value.dptr, value.dsize); 450 451 memcpy(&m, value.dptr, sizeof(m)); 452 if (m.major != DAV_DBVSN_MAJOR) { 453 dav_dbm_close(db); 454 455 return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 456 DAV_ERR_PROP_BAD_MAJOR, 0, 457 "Prop database has the wrong major " 458 "version number and cannot be used."); 459 } 460 db->version = m.minor; 461 db->ns_count = ntohs(m.ns_count); 462 463 dav_dbm_freedatum(db, value); 464 465 /* create db->uri_index */ 466 for (ns = 0, uri = db->ns_table.buf + sizeof(dav_propdb_metadata); 467 ns++ < db->ns_count; 468 uri += strlen(uri) + 1) { 469 470 /* we must copy the key, in case ns_table.buf moves */ 471 apr_hash_set(db->uri_index, 472 apr_pstrdup(pool, uri), APR_HASH_KEY_STRING, 473 (void *)ns); 474 } 475 } 476 477 *pdb = db; 478 return NULL; 479} 480 481static void dav_propdb_close(dav_db *db) 482{ 483 484 if (db->ns_table_dirty) { 485 dav_propdb_metadata m; 486 apr_datum_t key; 487 apr_datum_t value; 488 dav_error *err; 489 490 key.dptr = DAV_GDBM_NS_KEY; 491 key.dsize = DAV_GDBM_NS_KEY_LEN; 492 493 value.dptr = db->ns_table.buf; 494 value.dsize = db->ns_table.cur_len; 495 496 /* fill in the metadata that we store into the prop db. */ 497 m.major = DAV_DBVSN_MAJOR; 498 m.minor = db->version; /* ### keep current minor version? */ 499 m.ns_count = htons(db->ns_count); 500 501 memcpy(db->ns_table.buf, &m, sizeof(m)); 502 503 err = dav_dbm_store(db, key, value); 504 if (err != NULL) 505 ap_log_error(APLOG_MARK, APLOG_WARNING, err->aprerr, ap_server_conf, 506 APLOGNO(00577) "Error writing propdb: %s", err->desc); 507 } 508 509 dav_dbm_close(db); 510} 511 512static dav_error * dav_propdb_define_namespaces(dav_db *db, dav_xmlns_info *xi) 513{ 514 int ns; 515 const char *uri = db->ns_table.buf + sizeof(dav_propdb_metadata); 516 517 /* within the prop values, we use "ns%d" for prefixes... register them */ 518 for (ns = 0; ns < db->ns_count; ++ns, uri += strlen(uri) + 1) { 519 520 /* Empty URIs signify the empty namespace. These do not get a 521 namespace prefix. when we generate the value, we will simply 522 leave off the prefix, which is defined by mod_dav to be the 523 empty namespace. */ 524 if (*uri == '\0') 525 continue; 526 527 /* ns_table.buf can move, so copy its value (we want the values to 528 last as long as the provided dav_xmlns_info). */ 529 dav_xmlns_add(xi, 530 apr_psprintf(xi->pool, "ns%d", ns), 531 apr_pstrdup(xi->pool, uri)); 532 } 533 534 return NULL; 535} 536 537static dav_error * dav_propdb_output_value(dav_db *db, 538 const dav_prop_name *name, 539 dav_xmlns_info *xi, 540 apr_text_header *phdr, 541 int *found) 542{ 543 apr_datum_t key = dav_build_key(db, name); 544 apr_datum_t value; 545 dav_error *err; 546 547 if ((err = dav_dbm_fetch(db, key, &value)) != NULL) 548 return err; 549 if (value.dptr == NULL) { 550 *found = 0; 551 return NULL; 552 } 553 *found = 1; 554 555 dav_append_prop(db->pool, key.dptr, value.dptr, phdr); 556 557 dav_dbm_freedatum(db, value); 558 559 return NULL; 560} 561 562static dav_error * dav_propdb_map_namespaces( 563 dav_db *db, 564 const apr_array_header_t *namespaces, 565 dav_namespace_map **mapping) 566{ 567 dav_namespace_map *m = apr_palloc(db->pool, sizeof(*m)); 568 int i; 569 int *pmap; 570 const char **puri; 571 572 /* 573 ** Iterate over the provided namespaces. If a namespace already appears 574 ** in our internal map of URI -> ns_id, then store that in the map. If 575 ** we don't know the namespace yet, then add it to the map and to our 576 ** table of known namespaces. 577 */ 578 m->ns_map = pmap = apr_palloc(db->pool, namespaces->nelts * sizeof(*pmap)); 579 for (i = namespaces->nelts, puri = (const char **)namespaces->elts; 580 i-- > 0; 581 ++puri, ++pmap) { 582 583 const char *uri = *puri; 584 apr_size_t uri_len = strlen(uri); 585 long ns_id = (long)apr_hash_get(db->uri_index, uri, uri_len); 586 587 if (ns_id == 0) { 588 dav_check_bufsize(db->pool, &db->ns_table, uri_len + 1); 589 memcpy(db->ns_table.buf + db->ns_table.cur_len, uri, uri_len + 1); 590 db->ns_table.cur_len += uri_len + 1; 591 592 /* copy the uri in case the passed-in namespaces changes in 593 some way. */ 594 apr_hash_set(db->uri_index, apr_pstrdup(db->pool, uri), uri_len, 595 (void *)((long)(db->ns_count + 1))); 596 597 db->ns_table_dirty = 1; 598 599 *pmap = db->ns_count++; 600 } 601 else { 602 *pmap = ns_id - 1; 603 } 604 } 605 606 *mapping = m; 607 return NULL; 608} 609 610static dav_error * dav_propdb_store(dav_db *db, const dav_prop_name *name, 611 const apr_xml_elem *elem, 612 dav_namespace_map *mapping) 613{ 614 apr_datum_t key = dav_build_key(db, name); 615 apr_datum_t value; 616 617 /* Note: mapping->ns_map was set up in dav_propdb_map_namespaces() */ 618 619 /* ### use a db- subpool for these values? clear on exit? */ 620 621 /* quote all the values in the element */ 622 /* ### be nice to do this without affecting the element itself */ 623 /* ### of course, the cast indicates Badness is occurring here */ 624 apr_xml_quote_elem(db->pool, (apr_xml_elem *)elem); 625 626 /* generate a text blob for the xml:lang plus the contents */ 627 apr_xml_to_text(db->pool, elem, APR_XML_X2T_LANG_INNER, NULL, 628 mapping->ns_map, 629 (const char **)&value.dptr, &value.dsize); 630 631 return dav_dbm_store(db, key, value); 632} 633 634static dav_error * dav_propdb_remove(dav_db *db, const dav_prop_name *name) 635{ 636 apr_datum_t key = dav_build_key(db, name); 637 return dav_dbm_delete(db, key); 638} 639 640static int dav_propdb_exists(dav_db *db, const dav_prop_name *name) 641{ 642 apr_datum_t key = dav_build_key(db, name); 643 return dav_dbm_exists(db, key); 644} 645 646static const char *dav_get_ns_table_uri(dav_db *db, int ns_id) 647{ 648 const char *p = db->ns_table.buf + sizeof(dav_propdb_metadata); 649 650 while (ns_id--) 651 p += strlen(p) + 1; 652 653 return p; 654} 655 656static void dav_set_name(dav_db *db, dav_prop_name *pname) 657{ 658 const char *s = db->iter.dptr; 659 660 if (s == NULL) { 661 pname->ns = pname->name = NULL; 662 } 663 else if (*s == ':') { 664 pname->ns = ""; 665 pname->name = s + 1; 666 } 667 else { 668 int id = atoi(s); 669 670 pname->ns = dav_get_ns_table_uri(db, id); 671 if (s[1] == ':') { 672 pname->name = s + 2; 673 } 674 else { 675 pname->name = ap_strchr_c(s + 2, ':') + 1; 676 } 677 } 678} 679 680static dav_error * dav_propdb_next_name(dav_db *db, dav_prop_name *pname) 681{ 682 dav_error *err; 683 684 /* free the previous key. note: if the loop is aborted, then the DBM 685 will toss the key (via pool cleanup) */ 686 if (db->iter.dptr != NULL) 687 dav_dbm_freedatum(db, db->iter); 688 689 if ((err = dav_dbm_nextkey(db, &db->iter)) != NULL) 690 return err; 691 692 /* skip past the METADATA key */ 693 if (db->iter.dptr != NULL && *db->iter.dptr == 'M') 694 return dav_propdb_next_name(db, pname); 695 696 dav_set_name(db, pname); 697 return NULL; 698} 699 700static dav_error * dav_propdb_first_name(dav_db *db, dav_prop_name *pname) 701{ 702 dav_error *err; 703 704 if ((err = dav_dbm_firstkey(db, &db->iter)) != NULL) 705 return err; 706 707 /* skip past the METADATA key */ 708 if (db->iter.dptr != NULL && *db->iter.dptr == 'M') 709 return dav_propdb_next_name(db, pname); 710 711 dav_set_name(db, pname); 712 return NULL; 713} 714 715static dav_error * dav_propdb_get_rollback(dav_db *db, 716 const dav_prop_name *name, 717 dav_deadprop_rollback **prollback) 718{ 719 dav_deadprop_rollback *rb = apr_pcalloc(db->pool, sizeof(*rb)); 720 apr_datum_t key; 721 apr_datum_t value; 722 dav_error *err; 723 724 key = dav_build_key(db, name); 725 rb->key.dptr = apr_pstrdup(db->pool, key.dptr); 726 rb->key.dsize = key.dsize; 727 728 if ((err = dav_dbm_fetch(db, key, &value)) != NULL) 729 return err; 730 if (value.dptr != NULL) { 731 rb->value.dptr = apr_pmemdup(db->pool, value.dptr, value.dsize); 732 rb->value.dsize = value.dsize; 733 } 734 735 *prollback = rb; 736 return NULL; 737} 738 739static dav_error * dav_propdb_apply_rollback(dav_db *db, 740 dav_deadprop_rollback *rollback) 741{ 742 if (!rollback) { 743 return NULL; /* no rollback, nothing to do */ 744 } 745 746 if (rollback->value.dptr == NULL) { 747 /* don't fail if the thing isn't really there. */ 748 (void) dav_dbm_delete(db, rollback->key); 749 return NULL; 750 } 751 752 return dav_dbm_store(db, rollback->key, rollback->value); 753} 754 755const dav_hooks_db dav_hooks_db_dbm = 756{ 757 dav_propdb_open, 758 dav_propdb_close, 759 dav_propdb_define_namespaces, 760 dav_propdb_output_value, 761 dav_propdb_map_namespaces, 762 dav_propdb_store, 763 dav_propdb_remove, 764 dav_propdb_exists, 765 dav_propdb_first_name, 766 dav_propdb_next_name, 767 dav_propdb_get_rollback, 768 dav_propdb_apply_rollback, 769 770 NULL /* ctx */ 771}; 772