📄 dbm.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//*** DAV extension module for Apache 2.0.*** - Database support using DBM-style databases,** part of the filesystem repository implementation*//*** This implementation uses a SDBM database per file and directory to** record the properties. These databases are kept in a subdirectory (of** the directory in question or the directory that holds the file in** question) named by the macro DAV_FS_STATE_DIR (.DAV). The filename of the** database is equivalent to the target filename, and is** DAV_FS_STATE_FILE_FOR_DIR (.state_for_dir) for the directory itself.*/#include "apr_strings.h"#include "apr_file_io.h"#include "apr_dbm.h"#define APR_WANT_BYTEFUNC#include "apr_want.h" /* for ntohs and htons */#include "mod_dav.h"#include "repos.h"struct dav_db { apr_pool_t *pool; apr_dbm_t *file; /* when used as a property database: */ int version; /* *minor* version of this db */ dav_buffer ns_table; /* table of namespace URIs */ short ns_count; /* number of entries in table */ int ns_table_dirty; /* ns_table was modified */ apr_hash_t *uri_index; /* map URIs to (1-based) table indices */ dav_buffer wb_key; /* work buffer for dav_gdbm_key */ apr_datum_t iter; /* iteration key */};/* ------------------------------------------------------------------------- * * GENERIC DBM ACCESS * * For the most part, this just uses the APR DBM functions. They are wrapped * a bit with some error handling (using the mod_dav error functions). */void dav_dbm_get_statefiles(apr_pool_t *p, const char *fname, const char **state1, const char **state2){ if (fname == NULL) fname = DAV_FS_STATE_FILE_FOR_DIR; apr_dbm_get_usednames(p, fname, state1, state2);}static dav_error * dav_fs_dbm_error(dav_db *db, apr_pool_t *p, apr_status_t status){ int save_errno = errno; int errcode; const char *errstr; dav_error *err; char errbuf[200]; if (status == APR_SUCCESS) return NULL; p = db ? db->pool : p; /* There might not be a <db> if we had problems creating it. */ if (db == NULL) { errcode = 1; errstr = "Could not open property database."; } else { (void) apr_dbm_geterror(db->file, &errcode, errbuf, sizeof(errbuf)); errstr = apr_pstrdup(p, errbuf); } err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, errcode, errstr); err->save_errno = save_errno; return err;}/* ensure that our state subdirectory is present *//* ### does this belong here or in dav_fs_repos.c ?? */void dav_fs_ensure_state_dir(apr_pool_t * p, const char *dirname){ const char *pathname = apr_pstrcat(p, dirname, "/" DAV_FS_STATE_DIR, NULL); /* ### do we need to deal with the umask? */ /* just try to make it, ignoring any resulting errors */ (void) apr_dir_make(pathname, APR_OS_DEFAULT, p);}/* dav_dbm_open_direct: Opens a *dbm database specified by path. * ro = boolean read-only flag. */dav_error * dav_dbm_open_direct(apr_pool_t *p, const char *pathname, int ro, dav_db **pdb){ apr_status_t status; apr_dbm_t *file; *pdb = NULL; if ((status = apr_dbm_open(&file, pathname, ro ? APR_DBM_READONLY : APR_DBM_RWCREATE, APR_OS_DEFAULT, p)) != APR_SUCCESS && !ro) { /* ### do something with 'status' */ /* we can't continue if we couldn't open the file and we need to write */ return dav_fs_dbm_error(NULL, p, status); } /* may be NULL if we tried to open a non-existent db as read-only */ if (file != NULL) { /* we have an open database... return it */ *pdb = apr_pcalloc(p, sizeof(**pdb)); (*pdb)->pool = p; (*pdb)->file = file; } return NULL;}static dav_error * dav_dbm_open(apr_pool_t * p, const dav_resource *resource, int ro, dav_db **pdb){ const char *dirpath; const char *fname; const char *pathname; /* Get directory and filename for resource */ /* ### should test this result value... */ (void) dav_fs_dir_file_name(resource, &dirpath, &fname); /* If not opening read-only, ensure the state dir exists */ if (!ro) { /* ### what are the perf implications of always checking this? */ dav_fs_ensure_state_dir(p, dirpath); } pathname = apr_pstrcat(p, dirpath, "/" DAV_FS_STATE_DIR "/", fname ? fname : DAV_FS_STATE_FILE_FOR_DIR, NULL); /* ### readers cannot open while a writer has this open; we should ### perform a few retries with random pauses. */ /* ### do we need to deal with the umask? */ return dav_dbm_open_direct(p, pathname, ro, pdb);}void dav_dbm_close(dav_db *db){ apr_dbm_close(db->file);}dav_error * dav_dbm_fetch(dav_db *db, apr_datum_t key, apr_datum_t *pvalue){ apr_status_t status = apr_dbm_fetch(db->file, key, pvalue); return dav_fs_dbm_error(db, NULL, status);}dav_error * dav_dbm_store(dav_db *db, apr_datum_t key, apr_datum_t value){ apr_status_t status = apr_dbm_store(db->file, key, value); return dav_fs_dbm_error(db, NULL, status);}dav_error * dav_dbm_delete(dav_db *db, apr_datum_t key){ apr_status_t status = apr_dbm_delete(db->file, key); return dav_fs_dbm_error(db, NULL, status);}int dav_dbm_exists(dav_db *db, apr_datum_t key){ return apr_dbm_exists(db->file, key);}static dav_error * dav_dbm_firstkey(dav_db *db, apr_datum_t *pkey){ apr_status_t status = apr_dbm_firstkey(db->file, pkey); return dav_fs_dbm_error(db, NULL, status);}static dav_error * dav_dbm_nextkey(dav_db *db, apr_datum_t *pkey){ apr_status_t status = apr_dbm_nextkey(db->file, pkey); return dav_fs_dbm_error(db, NULL, status);}void dav_dbm_freedatum(dav_db *db, apr_datum_t data){ apr_dbm_freedatum(db->file, data);}/* ------------------------------------------------------------------------- * * PROPERTY DATABASE FUNCTIONS */#define DAV_GDBM_NS_KEY "METADATA"#define DAV_GDBM_NS_KEY_LEN 8typedef struct { unsigned char major;#define DAV_DBVSN_MAJOR 4 /* ** V4 -- 0.9.9 .. ** Prior versions could have keys or values with invalid ** namespace prefixes as a result of the xmlns="" form not ** resetting the default namespace to be "no namespace". The ** namespace would be set to "" which is invalid; it should ** be set to "no namespace". ** ** V3 -- 0.9.8 ** Prior versions could have values with invalid namespace ** prefixes due to an incorrect mapping of input to propdb ** namespace indices. Version bumped to obsolete the old ** values. ** ** V2 -- 0.9.7 ** This introduced the xml:lang value into the property value's ** record in the propdb. ** ** V1 -- .. 0.9.6 ** Initial version. */ unsigned char minor;#define DAV_DBVSN_MINOR 0 short ns_count;} dav_propdb_metadata;struct dav_deadprop_rollback { apr_datum_t key; apr_datum_t value;};struct dav_namespace_map { int *ns_map;};/*** Internal function to build a key**** WARNING: returns a pointer to a "static" buffer holding the key. The** value must be copied or no longer used if this function is** called again.*/static apr_datum_t dav_build_key(dav_db *db, const dav_prop_name *name){ char nsbuf[20]; apr_size_t l_ns, l_name = strlen(name->name); apr_datum_t key = { 0 }; /* * Convert namespace ID to a string. "no namespace" is an empty string, * so the keys will have the form ":name". Otherwise, the keys will * have the form "#:name". */ if (*name->ns == '\0') { nsbuf[0] = '\0'; l_ns = 0; } else { long ns_id = (long)apr_hash_get(db->uri_index, name->ns, APR_HASH_KEY_STRING); if (ns_id == 0) { /* the namespace was not found(!) */ return key; /* zeroed */ } l_ns = sprintf(nsbuf, "%ld", ns_id - 1); } /* assemble: #:name */ dav_set_bufsize(db->pool, &db->wb_key, l_ns + 1 + l_name + 1); memcpy(db->wb_key.buf, nsbuf, l_ns); db->wb_key.buf[l_ns] = ':'; memcpy(&db->wb_key.buf[l_ns + 1], name->name, l_name + 1); /* build the database key */ key.dsize = l_ns + 1 + l_name + 1; key.dptr = db->wb_key.buf; return key;}static void dav_append_prop(apr_pool_t *pool, const char *name, const char *value, apr_text_header *phdr){ const char *s; const char *lang = value; /* skip past the xml:lang value */ value += strlen(lang) + 1; if (*value == '\0') { /* the property is an empty value */ if (*name == ':') { /* "no namespace" case */ s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name+1); } else { s = apr_psprintf(pool, "<ns%s/>" DEBUG_CR, name); } } else if (*lang != '\0') { if (*name == ':') { /* "no namespace" case */ s = apr_psprintf(pool, "<%s xml:lang=\"%s\">%s</%s>" DEBUG_CR, name+1, lang, value, name+1); } else { s = apr_psprintf(pool, "<ns%s xml:lang=\"%s\">%s</ns%s>" DEBUG_CR, name, lang, value, name); } } else if (*name == ':') { /* "no namespace" case */ s = apr_psprintf(pool, "<%s>%s</%s>" DEBUG_CR, name+1, value, name+1); } else { s = apr_psprintf(pool, "<ns%s>%s</ns%s>" DEBUG_CR, name, value, name); } apr_text_append(pool, phdr, s);}static dav_error * dav_propdb_open(apr_pool_t *pool, const dav_resource *resource, int ro, dav_db **pdb){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -