Main Page | Modules | Namespace List | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages | Examples

dbm.c

Go to the documentation of this file.
00001 /* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
00002  * applicable.
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 /*
00018 ** DAV extension module for Apache 2.0.*
00019 **  - Database support using DBM-style databases,
00020 **    part of the filesystem repository implementation
00021 */
00022 
00023 /*
00024 ** This implementation uses a SDBM database per file and directory to
00025 ** record the properties. These databases are kept in a subdirectory (of
00026 ** the directory in question or the directory that holds the file in
00027 ** question) named by the macro DAV_FS_STATE_DIR (.DAV). The filename of the
00028 ** database is equivalent to the target filename, and is
00029 ** DAV_FS_STATE_FILE_FOR_DIR (.state_for_dir) for the directory itself.
00030 */
00031 
00032 #include "apr_strings.h"
00033 #include "apr_file_io.h"
00034 
00035 #include "apr_dbm.h"
00036 
00037 #define APR_WANT_BYTEFUNC
00038 #include "apr_want.h"       /* for ntohs and htons */
00039 
00040 #include "mod_dav.h"
00041 #include "repos.h"
00042 
00043 
00044 struct dav_db {
00045     apr_pool_t *pool;
00046     apr_dbm_t *file;
00047 
00048     /* when used as a property database: */
00049 
00050     int version;                /* *minor* version of this db */
00051 
00052     dav_buffer ns_table;        /* table of namespace URIs */
00053     short ns_count;             /* number of entries in table */
00054     int ns_table_dirty;         /* ns_table was modified */
00055     apr_hash_t *uri_index;      /* map URIs to (1-based) table indices */
00056 
00057     dav_buffer wb_key;          /* work buffer for dav_gdbm_key */
00058 
00059     apr_datum_t iter;           /* iteration key */
00060 };
00061 
00062 /* -------------------------------------------------------------------------
00063  *
00064  * GENERIC DBM ACCESS
00065  *
00066  * For the most part, this just uses the APR DBM functions. They are wrapped
00067  * a bit with some error handling (using the mod_dav error functions).
00068  */
00069 
00070 void dav_dbm_get_statefiles(apr_pool_t *p, const char *fname,
00071                             const char **state1, const char **state2)
00072 {
00073     if (fname == NULL)
00074         fname = DAV_FS_STATE_FILE_FOR_DIR;
00075 
00076     apr_dbm_get_usednames(p, fname, state1, state2);
00077 }
00078 
00079 static dav_error * dav_fs_dbm_error(dav_db *db, apr_pool_t *p,
00080                                     apr_status_t status)
00081 {
00082     int save_errno = errno;
00083     int errcode;
00084     const char *errstr;
00085     dav_error *err;
00086     char errbuf[200];
00087 
00088     if (status == APR_SUCCESS)
00089         return NULL;
00090 
00091     p = db ? db->pool : p;
00092 
00093     /* There might not be a <db> if we had problems creating it. */
00094     if (db == NULL) {
00095         errcode = 1;
00096         errstr = "Could not open property database.";
00097     }
00098     else {
00099         (void) apr_dbm_geterror(db->file, &errcode, errbuf, sizeof(errbuf));
00100         errstr = apr_pstrdup(p, errbuf);
00101     }
00102 
00103     err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, errcode, errstr);
00104     err->save_errno = save_errno;
00105     return err;
00106 }
00107 
00108 /* ensure that our state subdirectory is present */
00109 /* ### does this belong here or in dav_fs_repos.c ?? */
00110 void dav_fs_ensure_state_dir(apr_pool_t * p, const char *dirname)
00111 {
00112     const char *pathname = apr_pstrcat(p, dirname, "/" DAV_FS_STATE_DIR, NULL);
00113 
00114     /* ### do we need to deal with the umask? */
00115 
00116     /* just try to make it, ignoring any resulting errors */
00117     (void) apr_dir_make(pathname, APR_OS_DEFAULT, p);
00118 }
00119 
00120 /* dav_dbm_open_direct:  Opens a *dbm database specified by path.
00121  *    ro = boolean read-only flag.
00122  */
00123 dav_error * dav_dbm_open_direct(apr_pool_t *p, const char *pathname, int ro,
00124                                 dav_db **pdb)
00125 {
00126     apr_status_t status;
00127     apr_dbm_t *file;
00128 
00129     *pdb = NULL;
00130 
00131     if ((status = apr_dbm_open(&file, pathname,
00132                                ro ? APR_DBM_READONLY : APR_DBM_RWCREATE, 
00133                                APR_OS_DEFAULT, p))
00134                 != APR_SUCCESS
00135         && !ro) {
00136         /* ### do something with 'status' */
00137 
00138         /* we can't continue if we couldn't open the file 
00139            and we need to write */
00140         return dav_fs_dbm_error(NULL, p, status);
00141     }
00142 
00143     /* may be NULL if we tried to open a non-existent db as read-only */
00144     if (file != NULL) {
00145         /* we have an open database... return it */
00146         *pdb = apr_pcalloc(p, sizeof(**pdb));
00147         (*pdb)->pool = p;
00148         (*pdb)->file = file;
00149     }
00150 
00151     return NULL;
00152 }
00153 
00154 static dav_error * dav_dbm_open(apr_pool_t * p, const dav_resource *resource,
00155                                 int ro, dav_db **pdb)
00156 {
00157     const char *dirpath;
00158     const char *fname;
00159     const char *pathname;
00160 
00161     /* Get directory and filename for resource */
00162     /* ### should test this result value... */
00163     (void) dav_fs_dir_file_name(resource, &dirpath, &fname);
00164 
00165     /* If not opening read-only, ensure the state dir exists */
00166     if (!ro) {
00167         /* ### what are the perf implications of always checking this? */
00168         dav_fs_ensure_state_dir(p, dirpath);
00169     }
00170 
00171     pathname = apr_pstrcat(p, dirpath, "/" DAV_FS_STATE_DIR "/",
00172                               fname ? fname : DAV_FS_STATE_FILE_FOR_DIR,
00173                               NULL);
00174 
00175     /* ### readers cannot open while a writer has this open; we should
00176        ### perform a few retries with random pauses. */
00177 
00178     /* ### do we need to deal with the umask? */
00179 
00180     return dav_dbm_open_direct(p, pathname, ro, pdb);
00181 }
00182 
00183 void dav_dbm_close(dav_db *db)
00184 {
00185     apr_dbm_close(db->file);
00186 }
00187 
00188 dav_error * dav_dbm_fetch(dav_db *db, apr_datum_t key, apr_datum_t *pvalue)
00189 {
00190     apr_status_t status = apr_dbm_fetch(db->file, key, pvalue);
00191 
00192     return dav_fs_dbm_error(db, NULL, status);
00193 }
00194 
00195 dav_error * dav_dbm_store(dav_db *db, apr_datum_t key, apr_datum_t value)
00196 {
00197     apr_status_t status = apr_dbm_store(db->file, key, value);
00198 
00199     return dav_fs_dbm_error(db, NULL, status);
00200 }
00201 
00202 dav_error * dav_dbm_delete(dav_db *db, apr_datum_t key)
00203 {
00204     apr_status_t status = apr_dbm_delete(db->file, key);
00205 
00206     return dav_fs_dbm_error(db, NULL, status);
00207 }
00208 
00209 int dav_dbm_exists(dav_db *db, apr_datum_t key)
00210 {
00211     return apr_dbm_exists(db->file, key);
00212 }
00213 
00214 static dav_error * dav_dbm_firstkey(dav_db *db, apr_datum_t *pkey)
00215 {
00216     apr_status_t status = apr_dbm_firstkey(db->file, pkey);
00217 
00218     return dav_fs_dbm_error(db, NULL, status);
00219 }
00220 
00221 static dav_error * dav_dbm_nextkey(dav_db *db, apr_datum_t *pkey)
00222 {
00223     apr_status_t status = apr_dbm_nextkey(db->file, pkey);
00224 
00225     return dav_fs_dbm_error(db, NULL, status);
00226 }
00227 
00228 void dav_dbm_freedatum(dav_db *db, apr_datum_t data)
00229 {
00230     apr_dbm_freedatum(db->file, data);
00231 }
00232 
00233 /* -------------------------------------------------------------------------
00234  *
00235  * PROPERTY DATABASE FUNCTIONS
00236  */
00237 
00238 
00239 #define DAV_GDBM_NS_KEY         "METADATA"
00240 #define DAV_GDBM_NS_KEY_LEN     8
00241 
00242 typedef struct {
00243     unsigned char major;
00244 #define DAV_DBVSN_MAJOR         4
00245     /*
00246     ** V4 -- 0.9.9 ..
00247     **       Prior versions could have keys or values with invalid
00248     **       namespace prefixes as a result of the xmlns="" form not
00249     **       resetting the default namespace to be "no namespace". The
00250     **       namespace would be set to "" which is invalid; it should
00251     **       be set to "no namespace".
00252     **
00253     ** V3 -- 0.9.8
00254     **       Prior versions could have values with invalid namespace
00255     **       prefixes due to an incorrect mapping of input to propdb
00256     **       namespace indices. Version bumped to obsolete the old
00257     **       values.
00258     **
00259     ** V2 -- 0.9.7
00260     **       This introduced the xml:lang value into the property value's
00261     **       record in the propdb.
00262     **
00263     ** V1 -- .. 0.9.6
00264     **       Initial version.
00265     */
00266 
00267 
00268     unsigned char minor;
00269 #define DAV_DBVSN_MINOR         0
00270 
00271     short ns_count;
00272 
00273 } dav_propdb_metadata;
00274 
00275 struct dav_deadprop_rollback {
00276     apr_datum_t key;
00277     apr_datum_t value;
00278 };
00279 
00280 struct dav_namespace_map {
00281     int *ns_map;
00282 };
00283 
00284 /*
00285 ** Internal function to build a key
00286 **
00287 ** WARNING: returns a pointer to a "static" buffer holding the key. The
00288 **          value must be copied or no longer used if this function is
00289 **          called again.
00290 */
00291 static apr_datum_t dav_build_key(dav_db *db, const dav_prop_name *name)
00292 {
00293     char nsbuf[20];
00294     apr_size_t l_ns, l_name = strlen(name->name);
00295     apr_datum_t key = { 0 };
00296 
00297     /*
00298      * Convert namespace ID to a string. "no namespace" is an empty string,
00299      * so the keys will have the form ":name". Otherwise, the keys will
00300      * have the form "#:name".
00301      */
00302     if (*name->ns == '\0') {
00303         nsbuf[0] = '\0';
00304         l_ns = 0;
00305     }
00306     else {
00307         int ns_id = (int)apr_hash_get(db->uri_index, name->ns,
00308                                       APR_HASH_KEY_STRING);
00309 
00310 
00311         if (ns_id == 0) {
00312             /* the namespace was not found(!) */
00313             return key;         /* zeroed */
00314         }
00315 
00316         l_ns = sprintf(nsbuf, "%d", ns_id - 1);
00317     }
00318 
00319     /* assemble: #:name */
00320     dav_set_bufsize(db->pool, &db->wb_key, l_ns + 1 + l_name + 1);
00321     memcpy(db->wb_key.buf, nsbuf, l_ns);
00322     db->wb_key.buf[l_ns] = ':';
00323     memcpy(&db->wb_key.buf[l_ns + 1], name->name, l_name + 1);
00324 
00325     /* build the database key */
00326     key.dsize = l_ns + 1 + l_name + 1;
00327     key.dptr = db->wb_key.buf;
00328 
00329     return key;
00330 }
00331 
00332 static void dav_append_prop(apr_pool_t *pool,
00333                             const char *name, const char *value,
00334                             apr_text_header *phdr)
00335 {
00336     const char *s;
00337     const char *lang = value;
00338 
00339     /* skip past the xml:lang value */
00340     value += strlen(lang) + 1;
00341 
00342     if (*value == '\0') {
00343         /* the property is an empty value */
00344         if (*name == ':') {
00345             /* "no namespace" case */
00346             s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name+1);
00347         }
00348         else {
00349             s = apr_psprintf(pool, "<ns%s/>" DEBUG_CR, name);
00350         }
00351     }
00352     else if (*lang != '\0') {
00353         if (*name == ':') {
00354             /* "no namespace" case */
00355             s = apr_psprintf(pool, "<%s xml:lang=\"%s\">%s</%s>" DEBUG_CR,
00356                              name+1, lang, value, name+1);
00357         }
00358         else {
00359             s = apr_psprintf(pool, "<ns%s xml:lang=\"%s\">%s</ns%s>" DEBUG_CR,
00360                              name, lang, value, name);
00361         }
00362     }
00363     else if (*name == ':') {
00364         /* "no namespace" case */
00365         s = apr_psprintf(pool, "<%s>%s</%s>" DEBUG_CR, name+1, value, name+1);
00366     }
00367     else {
00368         s = apr_psprintf(pool, "<ns%s>%s</ns%s>" DEBUG_CR, name, value, name);
00369     }
00370 
00371     apr_text_append(pool, phdr, s);
00372 }
00373 
00374 static dav_error * dav_propdb_open(apr_pool_t *pool,
00375                                    const dav_resource *resource, int ro,
00376                                    dav_db **pdb)
00377 {
00378     dav_db *db;
00379     dav_error *err;
00380     apr_datum_t key;
00381     apr_datum_t value = { 0 };
00382 
00383     *pdb = NULL;
00384 
00385     /*
00386     ** Return if an error occurred, or there is no database.
00387     **
00388     ** NOTE: db could be NULL if we attempted to open a readonly
00389     **       database that doesn't exist. If we require read/write
00390     **       access, then a database was created and opened.
00391     */
00392     if ((err = dav_dbm_open(pool, resource, ro, &db)) != NULL
00393         || db == NULL)
00394         return err;
00395 
00396     db->uri_index = apr_hash_make(pool);
00397 
00398     key.dptr = DAV_GDBM_NS_KEY;
00399     key.dsize = DAV_GDBM_NS_KEY_LEN;
00400     if ((err = dav_dbm_fetch(db, key, &value)) != NULL) {
00401         /* ### push a higher-level description? */
00402         return err;
00403     }
00404 
00405     if (value.dptr == NULL) {
00406         dav_propdb_metadata m = {
00407             DAV_DBVSN_MAJOR, DAV_DBVSN_MINOR, 0
00408         };
00409 
00410         /*
00411         ** If there is no METADATA key, then the database may be
00412         ** from versions 0.9.0 .. 0.9.4 (which would be incompatible).
00413         ** These can be identified by the presence of an NS_TABLE entry.
00414         */
00415         key.dptr = "NS_TABLE";
00416         key.dsize = 8;
00417         if (dav_dbm_exists(db, key)) {
00418             dav_dbm_close(db);
00419 
00420             /* call it a major version error */
00421             return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR,
00422                                  DAV_ERR_PROP_BAD_MAJOR,
00423                                  "Prop database has the wrong major "
00424                                  "version number and cannot be used.");
00425         }
00426 
00427         /* initialize a new metadata structure */
00428         dav_set_bufsize(pool, &db->ns_table, sizeof(m));
00429         memcpy(db->ns_table.buf, &m, sizeof(m));
00430     }
00431     else {
00432         dav_propdb_metadata m;
00433         int ns;
00434         const char *uri;
00435 
00436         dav_set_bufsize(pool, &db->ns_table, value.dsize);
00437         memcpy(db->ns_table.buf, value.dptr, value.dsize);
00438 
00439         memcpy(&m, value.dptr, sizeof(m));
00440         if (m.major != DAV_DBVSN_MAJOR) {
00441             dav_dbm_close(db);
00442 
00443             return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR,
00444                                  DAV_ERR_PROP_BAD_MAJOR,
00445                                  "Prop database has the wrong major "
00446                                  "version number and cannot be used.");
00447         }
00448         db->version = m.minor;
00449         db->ns_count = ntohs(m.ns_count);
00450 
00451         dav_dbm_freedatum(db, value);
00452 
00453         /* create db->uri_index */
00454         for (ns = 0, uri = db->ns_table.buf + sizeof(dav_propdb_metadata);
00455              ns++ < db->ns_count;
00456              uri += strlen(uri) + 1) {
00457 
00458             /* we must copy the key, in case ns_table.buf moves */
00459             apr_hash_set(db->uri_index,
00460                          apr_pstrdup(pool, uri), APR_HASH_KEY_STRING,
00461                          (void *)ns);
00462         }
00463     }
00464 
00465     *pdb = db;
00466     return NULL;
00467 }
00468 
00469 static void dav_propdb_close(dav_db *db)
00470 {
00471 
00472     if (db->ns_table_dirty) {
00473         dav_propdb_metadata m;
00474         apr_datum_t key;
00475         apr_datum_t value;
00476         dav_error *err;
00477 
00478         key.dptr = DAV_GDBM_NS_KEY;
00479         key.dsize = DAV_GDBM_NS_KEY_LEN;
00480 
00481         value.dptr = db->ns_table.buf;
00482         value.dsize = db->ns_table.cur_len;
00483 
00484         /* fill in the metadata that we store into the prop db. */
00485         m.major = DAV_DBVSN_MAJOR;
00486         m.minor = db->version;          /* ### keep current minor version? */
00487         m.ns_count = htons(db->ns_count);
00488 
00489         memcpy(db->ns_table.buf, &m, sizeof(m));
00490 
00491         err = dav_dbm_store(db, key, value);
00492         /* ### what to do with the error? */
00493     }
00494 
00495     dav_dbm_close(db);
00496 }
00497 
00498 static dav_error * dav_propdb_define_namespaces(dav_db *db, dav_xmlns_info *xi)
00499 {
00500     int ns;
00501     const char *uri = db->ns_table.buf + sizeof(dav_propdb_metadata);
00502 
00503     /* within the prop values, we use "ns%d" for prefixes... register them */
00504     for (ns = 0; ns < db->ns_count; ++ns, uri += strlen(uri) + 1) {
00505 
00506         /* Empty URIs signify the empty namespace. These do not get a
00507            namespace prefix. when we generate the value, we will simply
00508            leave off the prefix, which is defined by mod_dav to be the
00509            empty namespace. */
00510         if (*uri == '\0')
00511             continue;
00512 
00513         /* ns_table.buf can move, so copy its value (we want the values to
00514            last as long as the provided dav_xmlns_info). */
00515         dav_xmlns_add(xi,
00516                       apr_psprintf(xi->pool, "ns%d", ns),
00517                       apr_pstrdup(xi->pool, uri));
00518     }
00519 
00520     return NULL;
00521 }
00522 
00523 static dav_error * dav_propdb_output_value(dav_db *db,
00524                                            const dav_prop_name *name,
00525                                            dav_xmlns_info *xi,
00526                                            apr_text_header *phdr,
00527                                            int *found)
00528 {
00529     apr_datum_t key = dav_build_key(db, name);
00530     apr_datum_t value;
00531     dav_error *err;
00532 
00533     if ((err = dav_dbm_fetch(db, key, &value)) != NULL)
00534         return err;
00535     if (value.dptr == NULL) {
00536         *found = 0;
00537         return NULL;
00538     }
00539     *found = 1;
00540 
00541     dav_append_prop(db->pool, key.dptr, value.dptr, phdr);
00542 
00543     dav_dbm_freedatum(db, value);
00544 
00545     return NULL;
00546 }
00547 
00548 static dav_error * dav_propdb_map_namespaces(
00549     dav_db *db,
00550     const apr_array_header_t *namespaces,
00551     dav_namespace_map **mapping)
00552 {
00553     dav_namespace_map *m = apr_palloc(db->pool, sizeof(*m));
00554     int i;
00555     int *pmap;
00556     const char **puri;
00557 
00558     /*
00559     ** Iterate over the provided namespaces. If a namespace already appears
00560     ** in our internal map of URI -> ns_id, then store that in the map. If
00561     ** we don't know the namespace yet, then add it to the map and to our
00562     ** table of known namespaces.
00563     */
00564     m->ns_map = pmap = apr_palloc(db->pool, namespaces->nelts * sizeof(*pmap));
00565     for (i = namespaces->nelts, puri = (const char **)namespaces->elts;
00566          i-- > 0;
00567          ++puri, ++pmap) {
00568 
00569         const char *uri = *puri;
00570         apr_size_t uri_len = strlen(uri);
00571         int ns_id = (int)apr_hash_get(db->uri_index, uri, uri_len);
00572 
00573         if (ns_id == 0) {
00574             dav_check_bufsize(db->pool, &db->ns_table, uri_len + 1);
00575             memcpy(db->ns_table.buf + db->ns_table.cur_len, uri, uri_len + 1);
00576             db->ns_table.cur_len += uri_len + 1;
00577 
00578             /* copy the uri in case the passed-in namespaces changes in
00579                some way. */
00580             apr_hash_set(db->uri_index, apr_pstrdup(db->pool, uri), uri_len,
00581                          (void *)(db->ns_count + 1));
00582 
00583             db->ns_table_dirty = 1;
00584 
00585             *pmap = db->ns_count++;
00586         }
00587         else {
00588             *pmap = ns_id - 1;
00589         }
00590     }
00591 
00592     *mapping = m;
00593     return NULL;
00594 }
00595 
00596 static dav_error * dav_propdb_store(dav_db *db, const dav_prop_name *name,
00597                                     const apr_xml_elem *elem,
00598                                     dav_namespace_map *mapping)
00599 {
00600     apr_datum_t key = dav_build_key(db, name);
00601     apr_datum_t value;
00602 
00603     /* Note: mapping->ns_map was set up in dav_propdb_map_namespaces() */
00604 
00605     /* ### use a db- subpool for these values? clear on exit? */
00606 
00607     /* quote all the values in the element */
00608     /* ### be nice to do this without affecting the element itself */
00609     /* ### of course, the cast indicates Badness is occurring here */
00610     apr_xml_quote_elem(db->pool, (apr_xml_elem *)elem);
00611 
00612     /* generate a text blob for the xml:lang plus the contents */
00613     apr_xml_to_text(db->pool, elem, APR_XML_X2T_LANG_INNER, NULL,
00614                     mapping->ns_map,
00615                     (const char **)&value.dptr, &value.dsize);
00616 
00617     return dav_dbm_store(db, key, value);
00618 }
00619 
00620 static dav_error * dav_propdb_remove(dav_db *db, const dav_prop_name *name)
00621 {
00622     apr_datum_t key = dav_build_key(db, name);
00623     return dav_dbm_delete(db, key);
00624 }
00625 
00626 static int dav_propdb_exists(dav_db *db, const dav_prop_name *name)
00627 {
00628     apr_datum_t key = dav_build_key(db, name);
00629     return dav_dbm_exists(db, key);
00630 }
00631 
00632 static const char *dav_get_ns_table_uri(dav_db *db, int ns_id)
00633 {
00634     const char *p = db->ns_table.buf + sizeof(dav_propdb_metadata);
00635 
00636     while (ns_id--)
00637         p += strlen(p) + 1;
00638 
00639     return p;
00640 }
00641 
00642 static void dav_set_name(dav_db *db, dav_prop_name *pname)
00643 {
00644     const char *s = db->iter.dptr;
00645 
00646     if (s == NULL) {
00647         pname->ns = pname->name = NULL;
00648     }
00649     else if (*s == ':') {
00650         pname->ns = "";
00651         pname->name = s + 1;
00652     }
00653     else {
00654         int id = atoi(s);
00655 
00656         pname->ns = dav_get_ns_table_uri(db, id);
00657         if (s[1] == ':') {
00658             pname->name = s + 2;
00659         }
00660         else {
00661             pname->name = ap_strchr_c(s + 2, ':') + 1;
00662         }
00663     }
00664 }
00665 
00666 static dav_error * dav_propdb_next_name(dav_db *db, dav_prop_name *pname)
00667 {
00668     dav_error *err;
00669 
00670     /* free the previous key. note: if the loop is aborted, then the DBM
00671        will toss the key (via pool cleanup) */
00672     if (db->iter.dptr != NULL)
00673         dav_dbm_freedatum(db, db->iter);
00674 
00675     if ((err = dav_dbm_nextkey(db, &db->iter)) != NULL)
00676         return err;
00677 
00678     /* skip past the METADATA key */
00679     if (db->iter.dptr != NULL && *db->iter.dptr == 'M')
00680         return dav_propdb_next_name(db, pname);
00681 
00682     dav_set_name(db, pname);
00683     return NULL;
00684 }
00685 
00686 static dav_error * dav_propdb_first_name(dav_db *db, dav_prop_name *pname)
00687 {
00688     dav_error *err;
00689 
00690     if ((err = dav_dbm_firstkey(db, &db->iter)) != NULL)
00691         return err;
00692 
00693     /* skip past the METADATA key */
00694     if (db->iter.dptr != NULL && *db->iter.dptr == 'M')
00695         return dav_propdb_next_name(db, pname);
00696 
00697     dav_set_name(db, pname);
00698     return NULL;
00699 }
00700 
00701 static dav_error * dav_propdb_get_rollback(dav_db *db,
00702                                            const dav_prop_name *name,
00703                                            dav_deadprop_rollback **prollback)
00704 {
00705     dav_deadprop_rollback *rb = apr_pcalloc(db->pool, sizeof(*rb));
00706     apr_datum_t key;
00707     apr_datum_t value;
00708     dav_error *err;
00709 
00710     key = dav_build_key(db, name);
00711     rb->key.dptr = apr_pstrdup(db->pool, key.dptr);
00712     rb->key.dsize = key.dsize;
00713 
00714     if ((err = dav_dbm_fetch(db, key, &value)) != NULL)
00715         return err;
00716     if (value.dptr != NULL) {
00717         rb->value.dptr = apr_pmemdup(db->pool, value.dptr, value.dsize);
00718         rb->value.dsize = value.dsize;
00719     }
00720 
00721     *prollback = rb;
00722     return NULL;
00723 }
00724 
00725 static dav_error * dav_propdb_apply_rollback(dav_db *db,
00726                                              dav_deadprop_rollback *rollback)
00727 {
00728     if (rollback->value.dptr == NULL) {
00729         /* don't fail if the thing isn't really there. */
00730         (void) dav_dbm_delete(db, rollback->key);
00731         return NULL;
00732     }
00733 
00734     return dav_dbm_store(db, rollback->key, rollback->value);
00735 }
00736 
00737 const dav_hooks_db dav_hooks_db_dbm =
00738 {
00739     dav_propdb_open,
00740     dav_propdb_close,
00741     dav_propdb_define_namespaces,
00742     dav_propdb_output_value,
00743     dav_propdb_map_namespaces,
00744     dav_propdb_store,
00745     dav_propdb_remove,
00746     dav_propdb_exists,
00747     dav_propdb_first_name,
00748     dav_propdb_next_name,
00749     dav_propdb_get_rollback,
00750     dav_propdb_apply_rollback,
00751 
00752     NULL /* ctx */
00753 };