/* ib1_file.c: INode Dased Driver 1 INode functions
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 <time.h>
#include "ib1_driver.h"
#define INODE_CREATE 1
#define INODE_CLONE 2
#define INODE_ERASE 4
#define USE_ICACHE
#define USE_IBCACHE
static guint32
ib1_imap_lookup (IB1EFS *efs, guint32 inode, gint flags, guint32 *oblock)
{
IB1CacheEntry *ce, *ce1;
guint32 block, imap_block, ind, *p;
if (oblock) *oblock = 0;
if (flags&INODE_CREATE) flags |= INODE_CLONE;
if (inode < 4) return efs->head.imap_start;
if ((inode > efs->head.inode_count) && !(flags&INODE_CREATE)) return 0;
if (inode > (efs->head.inode_count+1)) return 0;
/* we can store the first IB1_IDATA_LEN*2 imap entries
in the imap_start block! */
if (inode < (IB1_IDATA_LEN*2)) {
IB1INode *node;
if (!(ce = ib1_cache_map(efs, efs->head.imap_start, FALSE)))
return 0;
node = NODEP(ce, (inode/IB1_IDATA_LEN));
p = (guint32 *)(node->data+((inode%IB1_IDATA_LEN)&0xfffffffc));
ind = GUINT32_FROM_LE(*p);
} else {
imap_block = (inode-(IB1_IDATA_LEN*2))/508;
if (!(ce = ib1_inode_bmap(efs, 1, imap_block, (flags!=0))))
return 0;
p = ((guint32 *)ce->data+((inode-(IB1_IDATA_LEN*2))/4)%127);
ind = GUINT32_FROM_LE(*p);
}
if ((block = (ind>>4))) {
if (!(flags&INODE_CREATE) && !(ind&(1<<(inode%4)))) return 0;
if (!(ind&(1<<(inode%4)))) {
if (flags&INODE_ERASE) return 0;
ind |= (1<<(inode%4));
*p = GUINT32_TO_LE(ind);
ib1_cache_touch (ce, TRUE);
}
if (flags&INODE_ERASE) {
g_assert (block >= efs->head.cb);
ind &= ~(1<<(inode%4));
*p = GUINT32_TO_LE(ind);
ib1_cache_touch (ce, TRUE);
if (!(ind&15)) {
*p = 0;
ib1_block_free (efs, block);
}
} else {
if ((flags&INODE_CLONE) && (block < efs->head.cb)) {
ce1 = ib1_cache_map_clone (efs, block);
if (oblock) *oblock = block;
block = ce1->block;
ind = (block<<4)|(ind&0xf);
*p = GUINT32_TO_LE(ind);
ib1_cache_touch (ce, TRUE);
}
}
} else {
if (flags&INODE_ERASE) return 0;
if (!(flags&INODE_CREATE)) return 0;
block = ib1_block_alloc (efs);
ind = (block<<4)|(1<<(inode%4));
*p = GUINT32_TO_LE(ind);
ib1_cache_touch (ce, TRUE);
}
return block;
}
guint32
ib1_inode_create (IB1EFS *efs)
{
guint32 inode, block;
IB1CacheEntry *ce;
IB1INode *node;
gint i;
inode = ++efs->head.inode_count;
block = ib1_imap_lookup (efs, inode, INODE_CREATE, NULL);
if (!block) return 0;
if (!(ce = ib1_cache_map(efs, block, FALSE))) return 0;
node = NODEP(ce, inode);
((IB1INodeBlock *)ce)->inum = GUINT32_TO_LE((inode/4));
node->size = 0;
node->blocks = 0;
node->type = 0;
for (i = 0; i < (IB1_N_BLOCKS-3); i++) node->block[i] = 0;
ib1_cache_touch(ce, TRUE);
return inode;
}
static gint
ib1_inode_erase_real (IB1EFS *efs, guint32 inode)
{
if (inode < 4) return 0; /* don´t erase system inodes */
ib1_inode_trunc(efs, inode, 0);
return ib1_imap_lookup (efs, inode, INODE_ERASE, NULL);
}
gint
ib1_inode_erase (IB1EFS *efs, guint32 inode)
{
GList *l;
IB1INodeLEntry *e;
l = efs->inode_list;
while (l) {
e = (IB1INodeLEntry *)l->data;
if (e->inode == inode) {
e->erase = TRUE;
return 0;
}
l = l->next;
}
return ib1_inode_erase_real (efs, inode);
}
static void
trunc_ind (IB1EFS *efs, IB1CacheEntry *ce, guint32 *bref, guint32 block,
guint32 level)
{
IB1CacheEntry *ce1;
guint32 *buf, rb, hb;
gint i, c;
if (!(*bref)) return;
if (!(ce1 = ib1_cache_map_clone (efs, GUINT32_FROM_LE(*bref)))) return;
if (ce1->block != GUINT32_FROM_LE(*bref)) {
*bref = GUINT32_TO_LE (ce1->block);
ib1_cache_touch (ce, TRUE);
}
buf = (guint32 *)ce1->data;
CLOCK(ce1);
for (i = block/level, rb = block%level; i<127; i++) {
hb = GUINT32_FROM_LE(buf[i]);
if ((level > 1) && (((guint32*)ce1->data)[i]))
trunc_ind (efs,ce1,&(((guint32*)ce1->data)[i]),
rb, level/127);
else if (hb) {
if (hb >= efs->head.cb) ib1_block_free (efs, hb);
buf[i] = 0;
ib1_cache_touch(ce1, TRUE);
}
rb = 0;
}
CUNLOCK(ce1);
c = 0; for (i=0; i<127;i++) if (buf[i]) c++;
if (!c) {
ib1_block_free (efs, GUINT32_FROM_LE(*bref));
*bref = 0;
ib1_cache_touch(ce, TRUE);
} else {
ib1_cache_touch(ce1, TRUE);
}
}
gint
ib1_inode_trunc (IB1EFS *efs, guint32 inode, gint32 block)
{
IB1CacheEntry *ce;
IB1INode *node;
gint32 hb;
gint i;
if (!(ce = ib1_inode_map (efs, inode, TRUE))) return -1;
node = NODEP(ce, inode);
if (block == GUINT32_FROM_LE(node->blocks)) return 0;
if (block > GUINT32_FROM_LE(node->blocks)) return -1;
ib1_cache_touch (ce, TRUE);
CLOCK (ce);
for (i=block; i < (IB1_N_BLOCKS-3); i++) {
hb = GUINT32_FROM_LE(node->block[i]);
if (hb && (hb >= efs->head.cb)) ib1_block_free(efs, hb);
node->block[i] = 0;
}
if ((block -= (IB1_N_BLOCKS-3)) < 0) block = 0;
if ((block < 127) && (node->block[IB1_N_BLOCKS-3]))
trunc_ind (efs, ce, &node->block[IB1_N_BLOCKS-3], block, 1);
if ((block -= 127) < 0) block = 0;
if ((block < (127*127)) && (node->block[IB1_N_BLOCKS-2]))
trunc_ind (efs, ce, &node->block[IB1_N_BLOCKS-2], block, 127);
if ((block -= (127*127)) < 0) block = 0;
if ((block < (127*127*127)) && (node->block[IB1_N_BLOCKS-1]))
trunc_ind (efs,ce,&node->block[IB1_N_BLOCKS-1],block,127*127);
CUNLOCK(ce);
return 0;
}
IB1CacheEntry*
ib1_inode_map (IB1EFS *efs, guint32 inode, gboolean clone)
{
IB1CacheEntry *ce;
IB1INode *node;
gint ind = inode%IB1_ICACHE_SIZE;
guint32 ib = 0, ob = 0;
g_return_val_if_fail (inode != 0, NULL);
#ifdef USE_ICACHE
if (efs->icache[ind].inode == inode) {
ib = efs->icache[ind].block;
if (clone && (ib < efs->head.cb)) {
ib = 0;
}
}
#endif
if (!ib) {
if (clone) ib = ib1_imap_lookup (efs, inode, INODE_CLONE, &ob);
else ib = ib1_imap_lookup (efs, inode, 0, NULL);
}
efs->icache[ind].inode = inode;
efs->icache[ind].block = ib;
if (ib && (ce = ib1_cache_map(efs, ib, FALSE))) {
if (clone) {
node = NODEP(ce, inode);
if (ob) {
((IB1INodeBlock *)(ce->data))->pblock =
GUINT32_TO_LE (ob);
((IB1INodeBlock *)(ce->data))->version =
GUINT32_TO_LE (efs->head.version);
}
}
return ce;
}
return NULL;
}
static IB1CacheEntry*
map_ind (IB1EFS *efs, IB1CacheEntry *ce, guint32 *bref, gboolean clone)
{
IB1CacheEntry *ce1;
guint32 nb;
gint i;
if (!(*bref)) {
if (!clone) return NULL;
nb = ib1_block_alloc(efs);
*bref = GUINT32_TO_LE(nb);
ib1_cache_touch(ce, TRUE);
ce1 = ib1_cache_map (efs, nb, TRUE);
for (i=0;i<128;i++) ((guint32 *)ce1->data)[i]=0;
} else {
nb = GUINT32_FROM_LE(*bref);
if (clone) ce1 = ib1_cache_map_clone (efs, nb);
else ce1 = ib1_cache_map (efs, nb, FALSE);
if (nb != ce1->block) {
*bref = GUINT32_TO_LE(ce1->block);
ib1_cache_touch(ce, TRUE);
}
}
return ce1;
}
static IB1CacheEntry*
ib1_ibcache_lookup (IB1EFS *efs, guint32 inode, guint32 block, gboolean clone)
{
guint32 ibb, ind;
#ifndef USE_IBCACHE
return NULL;
#endif
ibb = 0;
ind = (inode+block)%IB1_IBCACHE_SIZE;
if ((efs->ibcache[ind].inode == inode) &&
(efs->ibcache[ind].iblk == block)) {
ibb = efs->ibcache[ind].block;
if (clone && (ibb < efs->head.cb)) {
efs->ibcache[ind].inode = 0;
return NULL;
}
}
if (ibb) return ib1_cache_map (efs, ibb, FALSE);
return NULL;
}
static void
ib1_ibcache_add (IB1EFS *efs, guint32 inode, guint32 block, guint32 blk)
{
guint32 ind;
ind = (inode+block)%IB1_IBCACHE_SIZE;
efs->ibcache[ind].inode = inode;
efs->ibcache[ind].iblk = block;
efs->ibcache[ind].block = blk;
}
IB1CacheEntry*
ib1_inode_bmap (IB1EFS *efs, guint32 inode, guint32 block, gboolean clone)
{
IB1CacheEntry *ce, *ce1, *ce2, *ce3, *ce4;
IB1INode *node;
guint32 cb;
if (block >= IB1_N_BLOCKS-3+127+127*127+127*127*127) return NULL;
if ((ce = ib1_ibcache_lookup (efs, inode, block, clone))) return ce;
if (!(ce = ib1_inode_map (efs, inode, clone))) return NULL;
node = NODEP(ce, inode);
if (block > GUINT32_FROM_LE(node->blocks)) return NULL; /* no holes */
if (block == GUINT32_FROM_LE(node->blocks)) {
if (!clone) return NULL;
if (!((((EFS *)efs)->mode)&EFS_WRITE)) return NULL;
node->blocks = GUINT32_TO_LE(block+1);
ib1_cache_touch (ce, TRUE);
}
if (block<(IB1_N_BLOCKS-3)) {
ce1 = map_ind (efs, ce, &node->block[block], clone);
return ce1;
}
cb = block - (IB1_N_BLOCKS-3);
if (cb < 127) {
ce1 = map_ind(efs, ce, &node->block[IB1_N_BLOCKS-3], clone);
if (!ce1) return NULL;
ce2 = map_ind(efs,ce1,&(((guint32*)ce1->data)[cb]),clone);
if (ce2) ib1_ibcache_add (efs, inode, block, ce2->block);
return ce2;
}
cb -= 127;
if (cb<127*127) {
ce1=map_ind(efs,ce,&node->block[IB1_N_BLOCKS-2],clone);
if (!ce1) return NULL;
ce2=map_ind(efs,ce1,&(((guint32*)ce1->data)[cb/127]),clone);
if (!ce2) return NULL;
ce3=map_ind(efs,ce2,&(((guint32*)ce2->data)[cb%127]),clone);
if (ce3) ib1_ibcache_add (efs, inode, block, ce3->block);
return ce3;
}
cb -= 127*127;
ce1 = map_ind (efs, ce, &node->block[IB1_N_BLOCKS-1], clone);
if (!ce1) return NULL;
ce2 = map_ind (efs, ce1, &(((guint32*)ce1->data)[cb/(127*127)]), clone);
if (!ce2) return NULL;
ce3 = map_ind (efs,ce2,&(((guint32*)ce2->data)[(cb/127)%127]),clone);
if (!ce3) return NULL;
ce4 = map_ind (efs, ce3, &(((guint32*)ce3->data)[cb%127]), clone);
if (ce4) ib1_ibcache_add (efs, inode, block, ce4->block);
return ce4;
}
gint
ib1_inode_ref (IB1EFS *efs, guint32 inode)
{
GList *l;
IB1INodeLEntry *e;
l = efs->inode_list;
while (l) {
e = (IB1INodeLEntry *)l->data;
if (e->inode == inode) {
e->ref_count++;
return e->ref_count;
}
l = l->next;
}
e = g_new0 (IB1INodeLEntry, 1);
e->inode = inode;
e->ref_count = 1;
efs->inode_list = g_list_prepend (efs->inode_list, e);
return 1;
}
gint
ib1_inode_unref (IB1EFS *efs, guint32 inode)
{
GList *l;
IB1INodeLEntry *e;
l = efs->inode_list;
while (l) {
e = (IB1INodeLEntry *)l->data;
if (e->inode == inode) {
e->ref_count--;
if (!e->ref_count) {
if (e->erase)
ib1_inode_erase_real (efs, inode);
efs->inode_list =
g_list_remove (efs->inode_list, e);
g_free (e);
return 0;
} else return e->ref_count;
}
l = l->next;
}
return -1;
}
gint
ib1_inode_refcount (IB1EFS *efs, guint32 inode)
{
GList *l;
IB1INodeLEntry *e;
l = efs->inode_list;
while (l) {
e = (IB1INodeLEntry *)l->data;
if (e->inode == inode) return e->ref_count;
l = l->next;
}
return -1;
}
void
ib1_inode_list_free (IB1EFS *efs)
{
GList *l;
l = efs->inode_list;
while (l) {
IB1INodeLEntry *le = (IB1INodeLEntry *)l->data;
if (le->erase) ib1_inode_erase_real (efs, le->inode);
g_free (l->data);
l = l->next;
}
g_list_free (efs->inode_list);
efs->inode_list = NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1