/* ib1_driver.c: INode Dased Driver 1 SuperOps
Copyright (C) 2000 Maurer IT Systemlösungen KEG
The Gnome Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The Gnome Library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the Gnome Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
Author: Dietmar Maurer <dietmar@maurer-it.com>
*/
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <zlib.h>
#include "ib1_driver.h"
static EFSResult ib1_open (EFSDir **dir, EFSDriver *driver,
const char *path, gint flags, gchar *passwd);
static EFSResult ib1_create (EFSDir **dir, EFSDriver *driver,
const char *path, gint flags, gint mode,
gchar *passwd);
static EFSResult ib1_close (EFSDir *root);
static EFSResult ib1_commit (EFSDir *root);
static EFSResult ib1_revert (EFSDir *root);
static EFSResult ib1_fsstat (EFSDir *root, EFSFSStat *fsstat);
EFSSuperOps super_ops_ib1 = {
ib1_open,
ib1_create,
ib1_close,
ib1_commit,
ib1_revert,
ib1_fsstat,
};
EFSDriver efs_driver_ib1 = {
"IB1", FALSE,
"INode based driver (IB1)",
&super_ops_ib1,
&file_ops_ib1,
};
EFSDriver efs_driver_ib1_enc = {
"IB1ENC", TRUE,
"INode based driver with encryption (IB1ENC)",
&super_ops_ib1,
&file_ops_ib1,
};
static gint
flag_conv (gint flags)
{
gint pflags;
if (flags&EFS_WRITE) pflags = O_RDWR;
else pflags = O_RDONLY;
if (flags&EFS_CREATE) pflags |= O_CREAT;
if (flags&EFS_EXCL) pflags |= O_EXCL;
return pflags;
}
void
ib1_encrypt (IB1EFS *efs, guint32 *buf, gint count)
{
gint i;
g_return_if_fail ((count&1) == 0); /* only 64 bit blocks */
for (i = 0; i < count; i+=2) {
blowfish_encrypt (&efs->ctx, &buf[i], &buf[i+1]);
}
}
void
ib1_decrypt (IB1EFS *efs, guint32 *buf, gint count)
{
gint i;
g_return_if_fail ((count&1) == 0); /* only 64 bit blocks */
for (i = 0; i < count; i+=2) {
blowfish_decrypt (&efs->ctx, &buf[i], &buf[i+1]);
}
}
static guint32
ib1_calc_crc (IB1Header *head)
{
guint32 crc = adler32 (0L, NULL, 0);
crc = adler32 (crc, (guint8 *)head,
(guint8 *)&head->crc-(guint8 *)head);
return crc;
}
static gint
ib1_read_head (IB1EFS *efs, IB1Header *head)
{
if (lseek (efs->fd, 0, SEEK_SET) == -1) return FALSE;
if (read (efs->fd, head, 512) != 512) return FALSE;
((EFS *)efs)->type = GINT32_FROM_LE(head->head.type);
if (GINT32_FROM_LE(head->head.protected) &&
(((EFS*)efs)->driver->encrypted)) {
ib1_decrypt (efs, &head->cb, (512 - sizeof(EFSHeader))/4);
}
if (ib1_calc_crc (head) != GUINT32_FROM_LE(head->crc)) return FALSE;
head->head.protected = GINT32_FROM_LE (head->head.protected);
head->head.type = GINT32_FROM_LE (head->head.protected);
head->crc = 0;
head->cb = GUINT32_FROM_LE(head->cb);
head->version = GUINT32_FROM_LE(head->version);
head->imap_start = GUINT32_FROM_LE(head->imap_start);
head->inode_count = GUINT32_FROM_LE(head->inode_count);
head->free_blocks = GUINT32_FROM_LE(head->free_blocks);
return TRUE;
}
static gint
ib1_write_head (IB1EFS *efs, IB1Header *head)
{
IB1Header tmp;
guint32 crc;
memcpy (&tmp, head, 512);
strncpy(tmp.head.efs_id, EFS_FILE_ID, 4);
strncpy(tmp.head.drivername, ((EFS *)efs)->driver->drivername, 12);
tmp.cb = GUINT32_TO_LE(head->cb);
tmp.version = GUINT32_TO_LE(head->version);
tmp.imap_start = GUINT32_TO_LE(head->imap_start);
tmp.inode_count = GUINT32_TO_LE(head->inode_count);
tmp.free_blocks = GUINT32_TO_LE(head->free_blocks);
tmp.head.type = GINT32_TO_LE(((EFS *)efs)->type);
tmp.head.protected = GINT32_TO_LE(head->head.protected);
crc=ib1_calc_crc (&tmp);
tmp.crc = GUINT32_TO_LE(crc);
if (head->head.protected && (((EFS*)efs)->driver->encrypted)) {
ib1_encrypt (efs, &tmp.cb, (512 - sizeof(EFSHeader))/4);
}
if (lseek (efs->fd, 0, SEEK_SET) == -1) return FALSE;
if (write(efs->fd, &tmp, 512) == 512) return TRUE;
return FALSE;
}
static EFSResult
create_default_inodes (IB1EFS *efs)
{
guint32 inode;
/* create all inodes in the first block
* the first block containst is special
* since it contains the IMAP
*/
if (!(inode = ib1_inode_create(efs)) || (inode != IB1_IMAP_INODE))
return EFS_ERR_INT;
if (!(inode = ib1_inode_create(efs)) || (inode != IB1_ROOT_INODE))
return EFS_ERR_INT;
if (!(inode = ib1_inode_create(efs)) || (inode != IB1_TYPE_INODE))
return EFS_ERR_INT;
return EFS_ERR_OK;
}
static void
ib1_create_typefd (IB1EFS *efs)
{
((EFS *)efs)->typefd = (EFSFile *)g_new0 (IB1File, 1);
((EFS *)efs)->typefd->efs = (EFS *)efs;
((EFS *)efs)->typefd->mode = (((EFS *)efs)->mode&EFS_RDWR) | EFS_FILE;
((IB1File *)((EFS *)efs)->typefd)->inode = IB1_TYPE_INODE;
ib1_inode_ref (efs, IB1_TYPE_INODE);
}
static EFSResult
ib1_open (EFSDir **dir, EFSDriver *driver, const char *path, gint flags,
gchar *passwd)
{
IB1EFS *efs;
IB1CacheEntry *ce;
g_return_val_if_fail (sizeof(IB1Header) == 512, EFS_ERR_INT);
g_return_val_if_fail (sizeof(IB1INodeBlock) == 512, EFS_ERR_INT);
efs = g_new0 (IB1EFS, 1);
((EFS *)efs)->driver = driver;
if (flags&EFS_WRITE) ((EFS *)efs)->mode = EFS_RDWR;
else ((EFS *)efs)->mode = EFS_READ;
if ((efs->fd = open(path, flag_conv(flags))) == -1) {
g_free (efs);
return EFS_ERR_ERRNO;
}
if (passwd && (((EFS*)efs)->driver->encrypted))
blowfish_init (&efs->ctx, passwd, strlen(passwd));
if (!ib1_read_head (efs, &efs->head)) {
close (efs->fd);
g_free (efs);
return EFS_ERR_ERRNO;
}
/* Fixme: make more consistency checks ?*/
if (!efs->head.cb) return EFS_ERR_FORMAT;
ib1_bitmap_init (efs, efs->head.cb);
if (efs->head.cb == 1) {
if (efs->head.imap_start != 1) return EFS_ERR_FORMAT;
if (efs->head.inode_count != 3) return EFS_ERR_FORMAT;
efs->head.inode_count = 0;
if (ib1_block_alloc (efs) != 1) return EFS_ERR_INT;
if (create_default_inodes (efs) != EFS_ERR_OK) {
g_free (efs);
return EFS_ERR_INT;
}
}
if (flags&EFS_WRITE) {
if (!(ce = ib1_cache_map_clone (efs, efs->head.imap_start))) {
g_free (efs);
return EFS_ERR_INT;
}
((IB1INodeBlock *)(ce->data))->pblock = efs->head.imap_start;
((IB1INodeBlock *)(ce->data))->version =
GUINT32_TO_LE (efs->head.version);
efs->head.imap_start = ce->block;
}
ib1_create_typefd (efs);
(*dir) = (EFSDir *)g_new0 (IB1Dir, 1);
(*dir)->efs = (EFS *)efs;
(*dir)->pos = 0;
((IB1Dir *)(*dir))->inode = IB1_ROOT_INODE;
return EFS_ERR_OK;
}
static EFSResult
ib1_create (EFSDir **dir, EFSDriver *driver, const char *path, gint flags,
gint mode, gchar *passwd)
{
IB1EFS *efs;
g_return_val_if_fail (sizeof(IB1Header) == 512, EFS_ERR_INT);
g_return_val_if_fail (sizeof(IB1INodeBlock) == 512, EFS_ERR_INT);
efs = g_new0 (IB1EFS, 1);
((EFS *)efs)->driver = driver;
if (flags&EFS_WRITE) ((EFS *)efs)->mode = EFS_RDWR;
else ((EFS *)efs)->mode = EFS_READ;
if ((efs->fd = open(path, flag_conv(flags), mode)) == -1) {
g_free (efs);
return EFS_ERR_ERRNO;
}
efs->head.cb = 1;
ib1_bitmap_init (efs, 1);
if (passwd) {
efs->head.head.protected = 1;
if (!driver->encrypted) efs_passwd_set(&efs->head.head,passwd);
else blowfish_init (&efs->ctx, passwd, strlen(passwd));
}
if (!(efs->head.imap_start = ib1_block_alloc (efs)))
return EFS_ERR_INT;
if (create_default_inodes (efs) != EFS_ERR_OK) {
g_free (efs);
return EFS_ERR_INT;
}
if (!ib1_write_head (efs, &efs->head)) {
g_free (efs);
return EFS_ERR_ERRNO;
}
ib1_create_typefd (efs);
(*dir) = (EFSDir *)g_new0 (IB1Dir, 1);
(*dir)->efs = (EFS *)efs;
(*dir)->pos = 0;
((IB1Dir *)(*dir))->inode = IB1_ROOT_INODE;
return EFS_ERR_OK;
}
static EFSResult
ib1_close (EFSDir *root)
{
IB1EFS *efs = (IB1EFS *)root->efs;
ib1_inode_list_free (efs);
ib1_cache_flush (efs);
ib1_bitmap_free (efs);
if (root->efs->mode&EFS_WRITE) ftruncate(efs->fd, efs->head.cb*512);
if (close (efs->fd)) return EFS_ERR_ERRNO;
return EFS_ERR_OK;
}
static EFSResult
ib1_commit (EFSDir *root)
{
IB1EFS *efs = (IB1EFS *)root->efs;
IB1CacheEntry *ce;
gint i, free_blocks;
ib1_inode_list_free (efs);
free_blocks = ib1_block_get_fbc (efs);
ib1_cache_flush (efs);
efs->head.free_blocks += free_blocks;
efs->head.cb = efs->bmap.bc + 1;
efs->head.version++;
if (!ib1_write_head (efs, &efs->head)) return EFS_ERR_ERRNO;
ftruncate(efs->fd, efs->head.cb*512);
sync ();
for (i=0; i< IB1_ICACHE_SIZE; i++) efs->icache[i].inode = 0;
for (i=0; i< IB1_IBCACHE_SIZE; i++) efs->ibcache[i].inode = 0;
ib1_bitmap_free (efs);
ib1_bitmap_init (efs, efs->head.cb);
if (!(ce = ib1_cache_map_clone (efs, efs->head.imap_start)))
return EFS_ERR_INT;
((IB1INodeBlock *)(ce->data))->pblock = efs->head.imap_start;
((IB1INodeBlock *)(ce->data))->version =
GUINT32_TO_LE (efs->head.version);
efs->head.imap_start = ce->block;
return EFS_ERR_OK;
}
static EFSResult
ib1_revert (EFSDir *root)
{
IB1EFS *efs = (IB1EFS *)root->efs;
IB1CacheEntry *ce;
gint i;
ib1_inode_list_free (efs);
ib1_cache_flush (efs);
ftruncate(efs->fd, efs->head.cb*512);
if (!ib1_read_head (efs, &efs->head)) return EFS_ERR_ERRNO;
for (i=0; i< IB1_ICACHE_SIZE; i++) efs->icache[i].inode = 0;
for (i=0; i< IB1_IBCACHE_SIZE; i++) efs->ibcache[i].inode = 0;
ib1_bitmap_free (efs);
ib1_bitmap_init (efs, efs->head.cb);
if (!(ce=ib1_cache_map_clone (efs, efs->head.imap_start)))
return EFS_ERR_INT;
((IB1INodeBlock *)(ce->data))->pblock = efs->head.imap_start;
((IB1INodeBlock *)(ce->data))->version =
GUINT32_TO_LE (efs->head.version);
efs->head.imap_start = ce->block;
return EFS_ERR_OK;
}
static EFSResult
ib1_fsstat (EFSDir *root, EFSFSStat *fsstat)
{
IB1EFS *efs;
gint free = 0;
efs = (IB1EFS *)root->efs;
if (root->efs->mode&EFS_WRITE) free = ib1_block_get_fbc (efs);
fsstat->drivername = ((EFS *)efs)->driver->drivername;
fsstat->size = efs->head.cb+efs->bmap.bc*512;
fsstat->free = (efs->head.free_blocks + free)*512;
fsstat->namelen = IB1_NAME_LEN;
fsstat->version = efs->head.version;
fsstat->protected = efs->head.head.protected;
fsstat->encrypted = root->efs->driver->encrypted;
return EFS_ERR_OK;
}
syntax highlighted by Code2HTML, v. 0.9.1