📄 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 8
typedef 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 {
int ns_id = (int)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, "%d", 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 + -