blob: 16e2cbde8b2481e50cb05cddffc8af2674cef93b [file] [log] [blame]
/*
* Maintained by Jeff Garzik <jgarzik@pobox.com>
*
* Copyright 2006-2007 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
#include <glib.h>
#include <db.h>
#include "dbfs.h"
struct dbfs *gfs;
static void dbfs_db_syslog(const DB_ENV *dbenv, const char *errpfx,
const char *msg)
{
syslog(LOG_WARNING, "%s: %s", errpfx, msg);
}
static int open_db(DB_ENV *env, DB **db_out, const char *name,
unsigned int page_size, unsigned int flags)
{
int rc;
DB *db;
rc = db_create(db_out, env, 0);
if (rc) {
env->err(env, rc, "db_create");
return -EIO;
}
db = *db_out;
rc = db->set_pagesize(db, page_size);
if (rc) {
db->err(db, rc, "db->set_pagesize");
rc = -EIO;
goto err_out;
}
/* fix everything as little endian */
rc = db->set_lorder(db, 1234);
if (rc) {
db->err(db, rc, "db->set_lorder");
rc = -EIO;
goto err_out;
}
rc = db->open(db, NULL, name, NULL, DB_HASH,
DB_AUTO_COMMIT | flags, S_IRUSR | S_IWUSR);
if (rc) {
db->err(db, rc, "db->open");
rc = -EIO;
goto err_out;
}
return 0;
err_out:
db->close(db, 0);
return rc;
}
int dbfs_open(struct dbfs *fs, unsigned int env_flags, unsigned int flags,
const char *errpfx, gboolean syslog)
{
const char *db_home, *db_password;
int rc;
/*
* open DB environment
*/
db_home = fs->home;
g_assert(db_home != NULL);
/* this isn't a very secure way to handle passwords */
db_password = fs->passwd;
rc = db_env_create(&fs->env, 0);
if (rc) {
fprintf(stderr, "fs->env_create failed: %d\n", rc);
return rc;
}
if (syslog)
fs->env->set_errcall(fs->env, dbfs_db_syslog);
else
fs->env->set_errfile(fs->env, stderr);
fs->env->set_errpfx(fs->env, errpfx);
if (db_password) {
flags |= DB_ENCRYPT;
rc = fs->env->set_encrypt(fs->env, db_password, DB_ENCRYPT_AES);
if (rc) {
fs->env->err(fs->env, rc, "fs->env->set_encrypt");
goto err_out;
}
memset(fs->passwd, 0, strlen(fs->passwd));
free(fs->passwd);
fs->passwd = NULL;
}
/* init DB transactional environment, stored in directory db_home */
rc = fs->env->open(fs->env, db_home,
DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
DB_INIT_TXN | env_flags, S_IRUSR | S_IWUSR);
if (rc) {
fs->env->err(fs->env, rc, "fs->env->open");
goto err_out;
}
/*
* Open metadata database
*/
rc = open_db(fs->env, &fs->meta, "metadata", DBFS_PGSZ_METADATA, flags);
if (rc)
goto err_out;
rc = open_db(fs->env, &fs->hashref, "hash", DBFS_PGSZ_METADATA, flags);
if (rc)
goto err_out;
rc = open_db(fs->env, &fs->data, "data", DBFS_PGSZ_DATA, flags);
if (rc)
goto err_out_meta;
return 0;
err_out_meta:
fs->meta->close(fs->meta, 0);
err_out:
fs->env->close(fs->env, 0);
return rc;
}
void dbfs_close(struct dbfs *fs)
{
fs->data->close(fs->data, 0);
fs->hashref->close(fs->hashref, 0);
fs->meta->close(fs->meta, 0);
fs->env->close(fs->env, 0);
fs->env = NULL;
fs->meta = NULL;
}
struct dbfs *dbfs_new(void)
{
struct dbfs *fs;
char *passwd;
fs = g_new0(struct dbfs, 1);
if (!fs)
return NULL;
fs->next_inode = 2ULL;
fs->home = getenv("DB_HOME");
if (!fs->home) {
fprintf(stderr, "DB_HOME not set, aborting\n");
goto err_out;
}
passwd = getenv("DB_PASSWORD");
if (passwd) {
fs->passwd = strdup(passwd);
/* FIXME: this isn't a very good way to shroud the password */
if (putenv("DB_PASSWORD=X"))
perror("putenv DB_PASSWORD (SECURITY WARNING)");
}
return fs;
err_out:
g_free(fs);
return NULL;
}
void dbfs_free(struct dbfs *fs)
{
g_free(fs);
}
void dbfs_inode_free(struct dbfs_inode *ino)
{
if (!ino) /* permit dbfs_inode_free(NULL) */
return;
free(ino->raw_inode);
g_free(ino);
}
int dbfs_inode_write(DB_TXN *txn, struct dbfs_inode *ino)
{
struct dbfs_raw_inode *raw_ino = ino->raw_inode;
guint64 ino_n = GUINT64_FROM_LE(ino->raw_inode->ino);
DBT key, val;
char key_str[32];
memset(&key, 0, sizeof(key));
memset(&val, 0, sizeof(val));
sprintf(key_str, "/inode/%Lu", (unsigned long long) ino_n);
key.data = key_str;
key.size = strlen(key_str);
val.data = raw_ino;
val.size = ino->raw_ino_size;
raw_ino->version = GUINT64_TO_LE(
GUINT64_FROM_LE(raw_ino->version) + 1);
return gfs->meta->put(gfs->meta, txn, &key, &val, 0) ? -EIO : 0;
}
int dbfs_dir_new(DB_TXN *txn, guint64 parent, guint64 ino_n,
const struct dbfs_inode *ino)
{
void *mem, *p, *q;
struct dbfs_dirent *de;
size_t namelen;
DBT val;
int rc;
p = mem = malloc(128);
memset(mem, 0, 128);
/*
* add entry for "."
*/
de = p;
de->magic = GUINT32_TO_LE(DBFS_DE_MAGIC);
de->namelen = GUINT16_TO_LE(1);
de->ino = GUINT64_TO_LE(ino_n);
q = p + sizeof(struct dbfs_dirent);
memcpy(q, ".", 1);
namelen = GUINT16_FROM_LE(de->namelen);
p += dbfs_dirent_next(namelen);
/*
* add entry for ".."
*/
de = p;
de->magic = GUINT32_TO_LE(DBFS_DE_MAGIC);
de->namelen = GUINT16_TO_LE(2);
de->ino = GUINT64_TO_LE(parent);
q = p + sizeof(struct dbfs_dirent);
memcpy(q, "..", 2);
namelen = GUINT16_FROM_LE(de->namelen);
p += dbfs_dirent_next(namelen);
/*
* add terminating entry
*/
de = p;
de->magic = GUINT32_TO_LE(DBFS_DE_MAGIC);
namelen = GUINT16_FROM_LE(de->namelen);
p += dbfs_dirent_next(namelen);
/*
* store dir in database
*/
memset(&val, 0, sizeof(val));
val.data = mem;
val.size = p - mem;
rc = dbfs_dir_write(txn, ino_n, &val);
free(mem);
return rc;
}
int dbfs_dir_write(DB_TXN *txn, guint64 ino, DBT *val)
{
DBT key;
char key_str[32];
memset(&key, 0, sizeof(key));
sprintf(key_str, "/dir/%Lu", (unsigned long long) ino);
key.data = key_str;
key.size = strlen(key_str);
return gfs->meta->put(gfs->meta, txn, &key, val, 0) ? -EIO : 0;
}