/*
* ext2subs.c version 0.20
*
* Some strategic functions come from linux/fs/ext2
* kernel sources written by Remy Card.
*
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "dat.h"
#include "fns.h"
#define putext2(e) putbuf((e).buf)
#define dirtyext2(e) dirtybuf((e).buf)
static Intmap *uidmap, *gidmap;
static int
getnum(char *s, int *n)
{
char *r;
*n = strtol(s, &r, 10);
return (r != s);
}
static Intmap*
idfile(char *f)
{
Biobuf *bin;
Intmap *map;
char *fields[3];
char *s;
int nf, id;
map = allocmap(0);
bin = Bopen(f, OREAD);
if (bin == 0)
return 0;
while ((s = Brdline(bin, '\n')) != 0) {
s[Blinelen(bin)-1] = '\0';
nf = getfields(s, fields, 3, 0, ":");
if (nf == 3 && getnum(fields[2], &id))
insertkey(map, id, strdup(fields[0]));
}
Bterm(bin);
return map;
}
void
uidfile(char *f)
{
uidmap = idfile(f);
}
void
gidfile(char *f)
{
gidmap = idfile(f);
}
static char*
mapuid(int id)
{
static char s[12];
char *p;
if (uidmap && (p = lookupkey(uidmap, id)) != 0)
return p;
sprint(s, "%d", id);
return s;
}
static char*
mapgid(int id)
{
static char s[12];
char *p;
if (gidmap && (p = lookupkey(gidmap, id)) != 0)
return p;
sprint(s, "%d", id);
return s;
}
int
ext2fs(Xfs *xf)
{
SuperBlock superblock;
/* get the super block */
seek(xf->dev, OFFSET_SUPER_BLOCK, 0);
if( sizeof(SuperBlock) !=
read(xf->dev, &superblock, sizeof(SuperBlock)) ){
chat("can't read super block %r...", xf->dev);
errno = Eformat;
return -1;
}
if( superblock.s_magic != EXT2_SUPER_MAGIC ){
chat("Bad super block...");
errno = Eformat;
return -1;
}
if( !(superblock.s_state & EXT2_VALID_FS) ){
chat("fs not checked...");
errno = Enotclean;
return -1;
}
xf->block_size = EXT2_MIN_BLOCK_SIZE << superblock.s_log_block_size;
xf->desc_per_block = xf->block_size / sizeof (GroupDesc);
xf->inodes_per_group = superblock.s_inodes_per_group;
xf->inodes_per_block = xf->block_size / sizeof (Inode);
xf->addr_per_block = xf->block_size / sizeof (uint);
xf->blocks_per_group = superblock.s_blocks_per_group;
if( xf->block_size == OFFSET_SUPER_BLOCK )
xf->superaddr = 1, xf->superoff = 0, xf->grpaddr = 2;
else if( xf->block_size == 2*OFFSET_SUPER_BLOCK ||
xf->block_size == 4*OFFSET_SUPER_BLOCK )
xf->superaddr = 0, xf->superoff = OFFSET_SUPER_BLOCK, xf->grpaddr = 1;
else {
chat(" blocks of %d bytes are not supported...", xf->block_size);
errno = Eformat;
return -1;
}
chat("good super block...");
xf->ngroups = (superblock.s_blocks_count -
superblock.s_first_data_block +
superblock.s_blocks_per_group -1) /
superblock.s_blocks_per_group;
superblock.s_state &= ~EXT2_VALID_FS;
superblock.s_mnt_count++;
seek(xf->dev, OFFSET_SUPER_BLOCK, 0);
if( !rdonly && sizeof(SuperBlock) !=
write(xf->dev, &superblock, sizeof(SuperBlock)) ){
chat("can't write super block...");
errno = Eio;
return -1;
}
return 0;
}
Ext2
getext2(Xfs *xf, char type, int n)
{
Iobuf *bd;
Ext2 e;
switch(type){
case EXT2_SUPER:
e.buf = getbuf(xf, xf->superaddr);
if( !e.buf ) goto error;
e.u.sb = (SuperBlock *)(e.buf->iobuf + xf->superoff);
e.type = EXT2_SUPER;
break;
case EXT2_DESC:
e.buf = getbuf(xf, DESC_ADDR(xf, n));
if( !e.buf ) goto error;
e.u.gd = DESC_OFFSET(xf, e.buf->iobuf, n);
e.type = EXT2_DESC;
break;
case EXT2_BBLOCK:
bd = getbuf(xf, DESC_ADDR(xf, n));
if( !bd ) goto error;
e.buf = getbuf(xf, DESC_OFFSET(xf, bd->iobuf, n)->bg_block_bitmap);
if( !e.buf ){
putbuf(bd);
goto error;
}
putbuf(bd);
e.u.bmp = (char *)e.buf->iobuf;
e.type = EXT2_BBLOCK;
break;
case EXT2_BINODE:
bd = getbuf(xf, DESC_ADDR(xf, n));
if( !bd ) goto error;
e.buf = getbuf(xf, DESC_OFFSET(xf, bd->iobuf, n)->bg_inode_bitmap);
if( !e.buf ){
putbuf(bd);
goto error;
}
putbuf(bd);
e.u.bmp = (char *)e.buf->iobuf;
e.type = EXT2_BINODE;
break;
default:
goto error;
}
return e;
error:
panic("getext2");
return e;
}
int
get_inode( Xfile *file, uint nr )
{
unsigned long block_group, block;
Xfs *xf = file->xf;
Ext2 ed, es;
es = getext2(xf, EXT2_SUPER, 0);
if(nr > es.u.sb->s_inodes_count ){
chat("inode number %d is too big...", nr);
putext2(es);
errno = Eio;
return -1;
}
putext2(es);
block_group = (nr - 1) / xf->inodes_per_group;
if( block_group >= xf->ngroups ){
chat("block group (%d) > groups count...", block_group);
errno = Eio;
return -1;
}
ed = getext2(xf, EXT2_DESC, block_group);
block = ed.u.gd->bg_inode_table + (((nr-1) % xf->inodes_per_group) /
xf->inodes_per_block);
putext2(ed);
file->bufoffset = (nr-1) % xf->inodes_per_block;
file->inbr = nr;
file->bufaddr= block;
return 1;
}
int
get_file( Xfile *f, char *name)
{
uint offset, nr, i;
Xfs *xf = f->xf;
Inode *inode;
int nblock;
DirEntry *dir;
Iobuf *buf, *ibuf;
if( !S_ISDIR(getmode(f)) )
return -1;
ibuf = getbuf(xf, f->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
nblock = (inode->i_blocks * 512) / xf->block_size;
for(i=0 ; (i < nblock) && (i < EXT2_NDIR_BLOCKS) ; i++){
buf = getbuf(xf, inode->i_block[i]);
if( !buf ){
putbuf(ibuf);
return -1;
}
for(offset=0 ; offset < xf->block_size ; ){
dir = (DirEntry *)(buf->iobuf + offset);
if( dir->name_len==strlen(name) &&
!strncmp(name, dir->name, dir->name_len) ){
nr = dir->inode;
putbuf(buf);
putbuf(ibuf);
return nr;
}
offset += dir->rec_len;
}
putbuf(buf);
}
putbuf(ibuf);
errno = Enonexist;
return -1;
}
char *
getname(Xfile *f, char *str)
{
Xfile ft;
int offset, i, len;
Xfs *xf = f->xf;
Inode *inode;
int nblock;
DirEntry *dir;
Iobuf *buf, *ibuf;
ft = *f;
if( get_inode(&ft, f->pinbr) < 0 )
return 0;
if( !S_ISDIR(getmode(&ft)) )
return 0;
ibuf = getbuf(xf, ft.bufaddr);
if( !ibuf )
return 0;
inode = ((Inode *)ibuf->iobuf) + ft.bufoffset;
nblock = (inode->i_blocks * 512) / xf->block_size;
for(i=0 ; (i < nblock) && (i < EXT2_NDIR_BLOCKS) ; i++){
buf = getbuf(xf, inode->i_block[i]);
if( !buf ){
putbuf(ibuf);
return 0;
}
for(offset=0 ; offset < xf->block_size ; ){
dir = (DirEntry *)(buf->iobuf + offset);
if( f->inbr == dir->inode ){
len = (dir->name_len < EXT2_NAME_LEN) ? dir->name_len : EXT2_NAME_LEN;
if (str == 0)
str = malloc(len+1);
strncpy(str, dir->name, len);
str[len] = 0;
putbuf(buf);
putbuf(ibuf);
return str;
}
offset += dir->rec_len;
}
putbuf(buf);
}
putbuf(ibuf);
errno = Enonexist;
return 0;
}
void
dostat(Qid qid, Xfile *f, Dir *dir )
{
Inode *inode;
Iobuf *ibuf;
char *name;
memset(dir, 0, sizeof(Dir));
if( f->inbr == EXT2_ROOT_INODE ){
dir->name = estrdup9p("/");
dir->qid = (Qid){0,0,QTDIR};
dir->mode = DMDIR | 0777;
}else{
ibuf = getbuf(f->xf, f->bufaddr);
if( !ibuf )
return;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
dir->length = inode->i_size;
dir->atime = inode->i_atime;
dir->mtime = inode->i_mtime;
putbuf(ibuf);
name = getname(f, 0);
dir->name = name;
dir->uid = estrdup9p(mapuid(inode->i_uid));
dir->gid = estrdup9p(mapgid(inode->i_gid));
dir->qid = qid;
dir->mode = getmode(f);
if( qid.type & QTDIR )
dir->mode |= DMDIR;
}
}
int
dowstat(Xfile *f, Dir *stat)
{
Xfs *xf = f->xf;
Inode *inode;
Xfile fdir;
Iobuf *ibuf;
char name[EXT2_NAME_LEN+1];
/* change name */
getname(f, name);
if( stat->name && stat->name[0] != 0 && strcmp(name, stat->name) ){
/* get dir */
fdir = *f;
if( get_inode(&fdir, f->pinbr) < 0 ){
chat("can't get inode %d...", f->pinbr);
return -1;
}
ibuf = getbuf(xf, fdir.bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) +fdir.bufoffset;
/* Clean old dir entry */
if( delete_entry(xf, inode, f->inbr) < 0 ){
chat("delete entry failed...");
putbuf(ibuf);
return -1;
}
putbuf(ibuf);
/* add the new entry */
if( add_entry(&fdir, stat->name, f->inbr) < 0 ){
chat("add entry failed...");
return -1;
}
}
ibuf = getbuf(xf, f->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
if (stat->mode != ~0)
if( (getmode(f) & 0777) != (stat->mode & 0777) ){
inode->i_mode = (getmode(f) & ~0777) | (stat->mode & 0777);
dirtybuf(ibuf);
}
if (stat->mtime != ~0)
if( inode->i_mtime != stat->mtime ){
inode->i_mtime = stat->mtime;
dirtybuf(ibuf);
}
putbuf(ibuf);
return 1;
}
long
readfile(Xfile *f, void *vbuf, vlong offset, long count)
{
Xfs *xf = f->xf;
Inode *inode;
Iobuf *buffer, *ibuf;
long rcount;
int len, o, cur_block, baddr;
uchar *buf;
buf = vbuf;
ibuf = getbuf(xf, f->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
if( offset >= inode->i_size ){
putbuf(ibuf);
return 0;
}
if( offset + count > inode->i_size )
count = inode->i_size - offset;
/* fast link */
if( S_ISLNK(getmode(f)) && (inode->i_size <= EXT2_N_BLOCKS<<2) ){
memcpy(&buf[0], ((char *)inode->i_block)+offset, count);
putbuf(ibuf);
return count;
}
chat("read block [ ");
cur_block = offset / xf->block_size;
o = offset % xf->block_size;
rcount = 0;
while( count > 0 ){
baddr = bmap(f, cur_block++);
if( !baddr ){
putbuf(ibuf);
return -1;
}
buffer = getbuf(xf, baddr);
if( !buffer ){
putbuf(ibuf);
return -1;
}
chat("%d ", baddr);
len = xf->block_size - o;
if( len > count )
len = count;
memcpy(&buf[rcount], &buffer->iobuf[o], len);
rcount += len;
count -= len;
o = 0;
putbuf(buffer);
}
chat("] ...");
inode->i_atime = time(0);
dirtybuf(ibuf);
putbuf(ibuf);
return rcount;
}
long
readdir(Xfile *f, void *vbuf, vlong offset, long count)
{
int off, i, len;
long rcount;
Xfs *xf = f->xf;
Inode *inode, *tinode;
int nblock;
DirEntry *edir;
Iobuf *buffer, *ibuf, *tbuf;
Dir pdir;
Xfile ft;
uchar *buf;
char name[EXT2_NAME_LEN+1];
unsigned int dirlen;
int index;
buf = vbuf;
if (offset == 0)
f->dirindex = 0;
if( !S_ISDIR(getmode(f)) )
return -1;
ibuf = getbuf(xf, f->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
nblock = (inode->i_blocks * 512) / xf->block_size;
ft = *f;
chat("read block [ ");
index = 0;
for(i=0, rcount=0 ; (i < nblock) && (i < EXT2_NDIR_BLOCKS) ; i++){
buffer = getbuf(xf, inode->i_block[i]);
if( !buffer ){
putbuf(ibuf);
return -1;
}
chat("%d, ", buffer->addr);
for(off=0 ; off < xf->block_size ; ){
edir = (DirEntry *)(buffer->iobuf + off);
off += edir->rec_len;
if( (edir->name[0] == '.' ) && (edir->name_len == 1))
continue;
if(edir->name[0] == '.' && edir->name[1] == '.' &&
edir->name_len == 2)
continue;
if( edir->inode == 0 ) /* for lost+found dir ... */
continue;
if( index++ < f->dirindex )
continue;
if( get_inode(&ft, edir->inode) < 0 ){
chat("can't find ino no %d ] ...", edir->inode);
error: putbuf(buffer);
putbuf(ibuf);
return -1;
}
tbuf = getbuf(xf, ft.bufaddr);
if( !tbuf )
goto error;
tinode = ((Inode *)tbuf->iobuf) + ft.bufoffset;
memset(&pdir, 0, sizeof(Dir));
/* fill plan9 dir struct */
pdir.name = name;
len = (edir->name_len < EXT2_NAME_LEN) ? edir->name_len : EXT2_NAME_LEN;
strncpy(pdir.name, edir->name, len);
pdir.name[len] = 0;
// chat("name %s len %d\n", pdir.name, edir->name_len);
pdir.uid = mapuid(tinode->i_uid);
pdir.gid = mapgid(tinode->i_gid);
pdir.qid.path = edir->inode;
pdir.mode = tinode->i_mode;
if( edir->inode == EXT2_ROOT_INODE )
pdir.qid.path = f->xf->rootqid.path;
else if( S_ISDIR( tinode->i_mode) )
pdir.qid.type |= QTDIR;
if( pdir.qid.type & QTDIR )
pdir.mode |= DMDIR;
pdir.length = tinode->i_size;
pdir.atime = tinode->i_atime;
pdir.mtime = tinode->i_mtime;
putbuf(tbuf);
dirlen = convD2M(&pdir, &buf[rcount], count-rcount);
if ( dirlen <= BIT16SZ ) {
chat("] ...");
putbuf(buffer);
putbuf(ibuf);
return rcount;
}
rcount += dirlen;
f->dirindex++;
}
putbuf(buffer);
}
chat("] ...");
putbuf(ibuf);
return rcount;
}
int
bmap( Xfile *f, int block )
{
Xfs *xf = f->xf;
Inode *inode;
Iobuf *buf, *ibuf;
int addr;
int addr_per_block = xf->addr_per_block;
int addr_per_block_bits = ffz(~addr_per_block);
if(block < 0) {
chat("bmap() block < 0 ...");
return 0;
}
if(block >= EXT2_NDIR_BLOCKS + addr_per_block +
(1 << (addr_per_block_bits * 2)) +
((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
chat("bmap() block > big...");
return 0;
}
ibuf = getbuf(xf, f->bufaddr);
if( !ibuf )
return 0;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
/* direct blocks */
if(block < EXT2_NDIR_BLOCKS){
putbuf(ibuf);
return inode->i_block[block];
}
block -= EXT2_NDIR_BLOCKS;
/* indirect blocks*/
if(block < addr_per_block) {
addr = inode->i_block[EXT2_IND_BLOCK];
if (!addr) goto error;
buf = getbuf(xf, addr);
if( !buf ) goto error;
addr = *(((uint *)buf->iobuf) + block);
putbuf(buf);
putbuf(ibuf);
return addr;
}
block -= addr_per_block;
/* double indirect blocks */
if(block < (1 << (addr_per_block_bits * 2))) {
addr = inode->i_block[EXT2_DIND_BLOCK];
if (!addr) goto error;
buf = getbuf(xf, addr);
if( !buf ) goto error;
addr = *(((uint *)buf->iobuf) + (block >> addr_per_block_bits));
putbuf(buf);
buf = getbuf(xf, addr);
if( !buf ) goto error;
addr = *(((uint *)buf->iobuf) + (block & (addr_per_block - 1)));
putbuf(buf);
putbuf(ibuf);
return addr;
}
block -= (1 << (addr_per_block_bits * 2));
/* triple indirect blocks */
addr = inode->i_block[EXT2_TIND_BLOCK];
if(!addr) goto error;
buf = getbuf(xf, addr);
if( !buf ) goto error;
addr = *(((uint *)buf->iobuf) + (block >> (addr_per_block_bits * 2)));
putbuf(buf);
if(!addr) goto error;
buf = getbuf(xf, addr);
if( !buf ) goto error;
addr = *(((uint *)buf->iobuf) +
((block >> addr_per_block_bits) & (addr_per_block - 1)));
putbuf(buf);
if(!addr) goto error;
buf = getbuf(xf, addr);
if( !buf ) goto error;
addr = *(((uint *)buf->iobuf) + (block & (addr_per_block - 1)));
putbuf(buf);
putbuf(ibuf);
return addr;
error:
putbuf(ibuf);
return 0;
}
long
writefile(Xfile *f, void *vbuf, vlong offset, long count)
{
Xfs *xf = f->xf;
Inode *inode;
Iobuf *buffer, *ibuf;
long w;
int len, o, cur_block, baddr;
char *buf;
buf = vbuf;
ibuf = getbuf(xf, f->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
chat("write block [ ");
cur_block = offset / xf->block_size;
o = offset % xf->block_size;
w = 0;
while( count > 0 ){
baddr = getblk(f, cur_block++);
if( baddr <= 0 )
goto end;
buffer = getbuf(xf, baddr);
if( !buffer )
goto end;
chat("%d ", baddr);
len = xf->block_size - o;
if( len > count )
len = count;
memcpy(&buffer->iobuf[o], &buf[w], len);
dirtybuf(buffer);
w += len;
count -= len;
o = 0;
putbuf(buffer);
}
end:
if( inode->i_size < offset + w )
inode->i_size = offset + w;
inode->i_atime = inode->i_mtime = time(0);
dirtybuf(ibuf);
putbuf(ibuf);
chat("]...");
if( errno )
return -1;
return w;
}
int
new_block( Xfile *f, int goal )
{
Xfs *xf= f->xf;
int group, block, baddr, k, redo;
ulong lmap;
char *p, *r;
Iobuf *buf;
Ext2 ed, es, eb;
es = getext2(xf, EXT2_SUPER, 0);
redo = 0;
repeat:
if( goal < es.u.sb->s_first_data_block || goal >= es.u.sb->s_blocks_count )
goal = es.u.sb->s_first_data_block;
group = (goal - es.u.sb->s_first_data_block) / xf->blocks_per_group;
ed = getext2(xf, EXT2_DESC, group);
eb = getext2(xf, EXT2_BBLOCK, group);
/*
* First, test if goal block is free
*/
if( ed.u.gd->bg_free_blocks_count > 0 ){
block = (goal - es.u.sb->s_first_data_block) % xf->blocks_per_group;
if( !test_bit(block, eb.u.bmp) )
goto got_block;
if( block ){
/*
* goal wasn't free ; search foward for a free
* block within the next 32 blocks
*/
lmap = (((ulong *)eb.u.bmp)[block>>5]) >>
((block & 31) + 1);
if( block < xf->blocks_per_group - 32 )
lmap |= (((ulong *)eb.u.bmp)[(block>>5)+1]) <<
( 31-(block & 31) );
else
lmap |= 0xffffffff << ( 31-(block & 31) );
if( lmap != 0xffffffffl ){
k = ffz(lmap) + 1;
if( (block + k) < xf->blocks_per_group ){
block += k;
goto got_block;
}
}
}
/*
* Search in the remaider of the group
*/
p = eb.u.bmp + (block>>3);
r = memscan(p, 0, (xf->blocks_per_group - block + 7) >>3);
k = ( r - eb.u.bmp )<<3;
if( k < xf->blocks_per_group ){
block = k;
goto search_back;
}
k = find_next_zero_bit((unsigned long *)eb.u.bmp,
xf->blocks_per_group>>3, block);
if( k < xf->blocks_per_group ){
block = k;
goto got_block;
}
}
/*
* Search the rest of groups
*/
putext2(ed); putext2(eb);
for(k=0 ; k < xf->ngroups ; k++){
group++;
if( group >= xf->ngroups )
group = 0;
ed = getext2(xf, EXT2_DESC, group);
if( ed.u.gd->bg_free_blocks_count > 0 )
break;
putext2(ed);
}
if( redo && group == xf->ngroups-1 ){
putext2(ed);
goto full;
}
if( k >=xf->ngroups ){
/*
* All groups are full or
* we have retry (because the last block) and all other
* groups are also full.
*/
full:
chat("no free blocks ...");
putext2(es);
errno = Enospace;
return 0;
}
eb = getext2(xf, EXT2_BBLOCK, group);
r = memscan(eb.u.bmp, 0, xf->blocks_per_group>>3);
block = (r - eb.u.bmp) <<3;
if( block < xf->blocks_per_group )
goto search_back;
else
block = find_first_zero_bit((ulong *)eb.u.bmp,
xf->blocks_per_group>>3);
if( block >= xf->blocks_per_group ){
chat("Free block count courupted for block group %d...", group);
putext2(ed); putext2(eb); putext2(es);
errno = Ecorrupt;
return 0;
}
search_back:
/*
* A free byte was found in the block. Now search backwards up
* to 7 bits to find the start of this group of free block.
*/
for(k=0 ; k < 7 && block > 0 &&
!test_bit(block-1, eb.u.bmp) ; k++, block--);
got_block:
baddr = block + (group * xf->blocks_per_group) +
es.u.sb->s_first_data_block;
if( baddr == ed.u.gd->bg_block_bitmap ||
baddr == ed.u.gd->bg_inode_bitmap ){
chat("Allocating block in system zone...");
putext2(ed); putext2(eb); putext2(es);
errno = Eintern;
return 0;
}
if( set_bit(block, eb.u.bmp) ){
chat("bit already set (%d)...", block);
putext2(ed); putext2(eb); putext2(es);
errno = Ecorrupt;
return 0;
}
dirtyext2(eb);
if( baddr >= es.u.sb->s_blocks_count ){
chat("block >= blocks count...");
errno = Eintern;
error:
clear_bit(block, eb.u.bmp);
putext2(eb); putext2(ed); putext2(es);
return 0;
}
buf = getbuf(xf, baddr);
if( !buf ){
if( !redo ){
/*
* It's perhaps the last block of the disk and
* it can't be acceded because the last sector.
* Therefore, we try one more time with goal at 0
* to force scanning all groups.
*/
clear_bit(block, eb.u.bmp);
putext2(eb); putext2(ed);
goal = 0; errno = 0; redo++;
goto repeat;
}
goto error;
}
memset(&buf->iobuf[0], 0, xf->block_size);
dirtybuf(buf);
putbuf(buf);
es.u.sb->s_free_blocks_count--;
dirtyext2(es);
ed.u.gd->bg_free_blocks_count--;
dirtyext2(ed);
putext2(eb);
putext2(ed);
putext2(es);
chat("new ");
return baddr;
}
int
getblk(Xfile *f, int block)
{
Xfs *xf = f->xf;
int baddr;
int addr_per_block = xf->addr_per_block;
if (block < 0) {
chat("getblk() block < 0 ...");
return 0;
}
if(block > EXT2_NDIR_BLOCKS + addr_per_block +
addr_per_block * addr_per_block +
addr_per_block * addr_per_block * addr_per_block ){
chat("getblk() block > big...");
errno = Eintern;
return 0;
}
if( block < EXT2_NDIR_BLOCKS )
return inode_getblk(f, block);
block -= EXT2_NDIR_BLOCKS;
if( block < addr_per_block ){
baddr = inode_getblk(f, EXT2_IND_BLOCK);
baddr = block_getblk(f, baddr, block);
return baddr;
}
block -= addr_per_block;
if( block < addr_per_block * addr_per_block ){
baddr = inode_getblk(f, EXT2_DIND_BLOCK);
baddr = block_getblk(f, baddr, block / addr_per_block);
baddr = block_getblk(f, baddr, block & ( addr_per_block-1));
return baddr;
}
block -= addr_per_block * addr_per_block;
baddr = inode_getblk(f, EXT2_TIND_BLOCK);
baddr = block_getblk(f, baddr, block / (addr_per_block * addr_per_block));
baddr = block_getblk(f, baddr, (block / addr_per_block) & ( addr_per_block-1));
return block_getblk(f, baddr, block & ( addr_per_block-1));
}
int
block_getblk(Xfile *f, int rb, int nr)
{
Xfs *xf = f->xf;
Inode *inode;
int tmp, goal = 0;
int blocks = xf->block_size / 512;
Iobuf *buf, *ibuf;
uint *p;
Ext2 es;
if( !rb )
return 0;
buf = getbuf(xf, rb);
if( !buf )
return 0;
p = (uint *)(buf->iobuf) + nr;
if( *p ){
tmp = *p;
putbuf(buf);
return tmp;
}
for(tmp=nr - 1 ; tmp >= 0 ; tmp--){
if( ((uint *)(buf->iobuf))[tmp] ){
goal = ((uint *)(buf->iobuf))[tmp];
break;
}
}
if( !goal ){
es = getext2(xf, EXT2_SUPER, 0);
goal = (((f->inbr -1) / xf->inodes_per_group) *
xf->blocks_per_group) +
es.u.sb->s_first_data_block;
putext2(es);
}
tmp = new_block(f, goal);
if( !tmp ){
putbuf(buf);
return 0;
}
*p = tmp;
dirtybuf(buf);
putbuf(buf);
ibuf = getbuf(xf, f->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
inode->i_blocks += blocks;
dirtybuf(ibuf);
putbuf(ibuf);
return tmp;
}
int
inode_getblk(Xfile *f, int block)
{
Xfs *xf = f->xf;
Inode *inode;
Iobuf *ibuf;
int tmp, goal = 0;
int blocks = xf->block_size / 512;
Ext2 es;
ibuf = getbuf(xf, f->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
if( inode->i_block[block] ){
putbuf(ibuf);
return inode->i_block[block];
}
for(tmp=block - 1 ; tmp >= 0 ; tmp--){
if( inode->i_block[tmp] ){
goal = inode->i_block[tmp];
break;
}
}
if( !goal ){
es = getext2(xf, EXT2_SUPER, 0);
goal = (((f->inbr -1) / xf->inodes_per_group) *
xf->blocks_per_group) +
es.u.sb->s_first_data_block;
putext2(es);
}
tmp = new_block(f, goal);
if( !tmp ){
putbuf(ibuf);
return 0;
}
inode->i_block[block] = tmp;
inode->i_blocks += blocks;
dirtybuf(ibuf);
putbuf(ibuf);
return tmp;
}
int
new_inode(Xfile *f, int mode)
{
Xfs *xf = f->xf;
Inode *inode, *finode;
Iobuf *buf, *ibuf;
int ave,group, i, j;
Ext2 ed, es, eb;
group = -1;
es = getext2(xf, EXT2_SUPER, 0);
if( S_ISDIR(mode) ){ /* create directory inode */
ave = es.u.sb->s_free_inodes_count / xf->ngroups;
for(i=0 ; i < xf->ngroups ; i++){
ed = getext2(xf, EXT2_DESC, i);
if( ed.u.gd->bg_free_inodes_count &&
ed.u.gd->bg_free_inodes_count >= ave ){
if( group<0 || ed.u.gd->bg_free_inodes_count >
ed.u.gd->bg_free_inodes_count )
group = i;
}
putext2(ed);
}
}else{ /* create file inode */
/* Try to put inode in its parent directory */
i = (f->inbr -1) / xf->inodes_per_group;
ed = getext2(xf, EXT2_DESC, i);
if( ed.u.gd->bg_free_inodes_count ){
group = i;
putext2(ed);
}else{
/*
* Use a quadratic hash to find a group whith
* a free inode
*/
putext2(ed);
for( j=1 ; j < xf->ngroups ; j <<= 1){
i += j;
if( i >= xf->ngroups )
i -= xf->ngroups;
ed = getext2(xf, EXT2_DESC, i);
if( ed.u.gd->bg_free_inodes_count ){
group = i;
putext2(ed);
break;
}
putext2(ed);
}
}
if( group < 0 ){
/* try a linear search */
i = ((f->inbr -1) / xf->inodes_per_group) + 1;
for(j=2 ; j < xf->ngroups ; j++){
if( ++i >= xf->ngroups )
i = 0;
ed = getext2(xf, EXT2_DESC, i);
if( ed.u.gd->bg_free_inodes_count ){
group = i;
putext2(ed);
break;
}
putext2(ed);
}
}
}
if( group < 0 ){
chat("group < 0...");
putext2(es);
return 0;
}
ed = getext2(xf, EXT2_DESC, group);
eb = getext2(xf, EXT2_BINODE, group);
if( (j = find_first_zero_bit(eb.u.bmp,
xf->inodes_per_group>>3)) < xf->inodes_per_group){
if( set_bit(j, eb.u.bmp) ){
chat("inode %d of group %d is already allocated...", j, group);
putext2(ed); putext2(eb); putext2(es);
errno = Ecorrupt;
return 0;
}
dirtyext2(eb);
}else if( ed.u.gd->bg_free_inodes_count != 0 ){
chat("free inodes count corrupted for group %d...", group);
putext2(ed); putext2(eb); putext2(es);
errno = Ecorrupt;
return 0;
}
i = j;
j += group * xf->inodes_per_group + 1;
if( j < EXT2_FIRST_INO || j >= es.u.sb->s_inodes_count ){
chat("reserved inode or inode > inodes count...");
errno = Ecorrupt;
error:
clear_bit(i, eb.u.bmp);
putext2(eb); putext2(ed); putext2(es);
return 0;
}
buf = getbuf(xf, ed.u.gd->bg_inode_table +
(((j-1) % xf->inodes_per_group) /
xf->inodes_per_block));
if( !buf )
goto error;
inode = ((struct Inode *) buf->iobuf) +
((j-1) % xf->inodes_per_block);
memset(inode, 0, sizeof(Inode));
inode->i_mode = mode;
inode->i_links_count = 1;
inode->i_uid = DEFAULT_UID;
inode->i_gid = DEFAULT_GID;
inode->i_mtime = inode->i_atime = inode->i_ctime = time(0);
dirtybuf(buf);
ibuf = getbuf(xf, f->bufaddr);
if( !ibuf ){
putbuf(buf);
goto error;
}
finode = ((Inode *)ibuf->iobuf) + f->bufoffset;
inode->i_flags = finode->i_flags;
inode->i_uid = finode->i_uid;
inode->i_gid = finode->i_gid;
dirtybuf(ibuf);
putbuf(ibuf);
putbuf(buf);
ed.u.gd->bg_free_inodes_count--;
if( S_ISDIR(mode) )
ed.u.gd->bg_used_dirs_count++;
dirtyext2(ed);
es.u.sb->s_free_inodes_count--;
dirtyext2(es);
putext2(eb);
putext2(ed);
putext2(es);
return j;
}
int
create_file(Xfile *fdir, char *name, int mode)
{
int inr;
inr = new_inode(fdir, mode);
if( !inr ){
chat("create one new inode failed...");
return -1;
}
if( add_entry(fdir, name, inr) < 0 ){
chat("add entry failed...");
free_inode(fdir->xf, inr);
return -1;
}
return inr;
}
void
free_inode( Xfs *xf, int inr)
{
Inode *inode;
ulong b, bg;
Iobuf *buf;
Ext2 ed, es, eb;
bg = (inr -1) / xf->inodes_per_group;
b = (inr -1) % xf->inodes_per_group;
ed = getext2(xf, EXT2_DESC, bg);
buf = getbuf(xf, ed.u.gd->bg_inode_table +
(b / xf->inodes_per_block));
if( !buf ){
putext2(ed);
return;
}
inode = ((struct Inode *) buf->iobuf) +
((inr-1) % xf->inodes_per_block);
if( S_ISDIR(inode->i_mode) )
ed.u.gd->bg_used_dirs_count--;
memset(inode, 0, sizeof(Inode));
inode->i_dtime = time(0);
dirtybuf(buf);
putbuf(buf);
ed.u.gd->bg_free_inodes_count++;
dirtyext2(ed);
putext2(ed);
eb = getext2(xf, EXT2_BINODE, bg);
clear_bit(b, eb.u.bmp);
dirtyext2(eb);
putext2(eb);
es = getext2(xf, EXT2_SUPER, 0);
es.u.sb->s_free_inodes_count++;
dirtyext2(es); putext2(es);
}
int
create_dir(Xfile *fdir, char *name, int mode)
{
Xfs *xf = fdir->xf;
DirEntry *de;
Inode *inode;
Iobuf *buf, *ibuf;
Xfile tf;
int inr, baddr;
inr = new_inode(fdir, mode);
if( inr == 0 ){
chat("create one new inode failed...");
return -1;
}
if( add_entry(fdir, name, inr) < 0 ){
chat("add entry failed...");
free_inode(fdir->xf, inr);
return -1;
}
/* create the empty dir */
tf = *fdir;
if( get_inode(&tf, inr) < 0 ){
chat("can't get inode %d...", inr);
free_inode(fdir->xf, inr);
return -1;
}
ibuf = getbuf(xf, tf.bufaddr);
if( !ibuf ){
free_inode(fdir->xf, inr);
return -1;
}
inode = ((Inode *)ibuf->iobuf) + tf.bufoffset;
baddr = inode_getblk(&tf, 0);
if( !baddr ){
putbuf(ibuf);
ibuf = getbuf(xf, fdir->bufaddr);
if( !ibuf ){
free_inode(fdir->xf, inr);
return -1;
}
inode = ((Inode *)ibuf->iobuf) + fdir->bufoffset;
delete_entry(fdir->xf, inode, inr);
putbuf(ibuf);
free_inode(fdir->xf, inr);
return -1;
}
inode->i_size = xf->block_size;
buf = getbuf(xf, baddr);
de = (DirEntry *)buf->iobuf;
de->inode = inr;
de->name_len = 1;
de->rec_len = DIR_REC_LEN(de->name_len);
strcpy(de->name, ".");
de = (DirEntry *)( (char *)de + de->rec_len);
de->inode = fdir->inbr;
de->name_len = 2;
de->rec_len = xf->block_size - DIR_REC_LEN(1);
strcpy(de->name, "..");
dirtybuf(buf);
putbuf(buf);
inode->i_links_count = 2;
dirtybuf(ibuf);
putbuf(ibuf);
ibuf = getbuf(xf, fdir->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + fdir->bufoffset;
inode->i_links_count++;
dirtybuf(ibuf);
putbuf(ibuf);
return inr;
}
int
add_entry(Xfile *f, char *name, int inr)
{
Xfs *xf = f->xf;
DirEntry *de, *de1;
int offset, baddr;
int rec_len, cur_block;
int namelen = strlen(name);
Inode *inode;
Iobuf *buf, *ibuf;
ibuf = getbuf(xf, f->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
if( inode->i_size == 0 ){
chat("add_entry() no entry !!!...");
putbuf(ibuf);
return -1;
}
cur_block = offset = 0;
rec_len = DIR_REC_LEN(namelen);
buf = getbuf(xf, inode->i_block[cur_block++]);
if( !buf ){
putbuf(ibuf);
return -1;
}
de = (DirEntry *)buf->iobuf;
for(;;){
if( ((char *)de) >= (xf->block_size + buf->iobuf) ){
putbuf(buf);
if( cur_block >= EXT2_NDIR_BLOCKS ){
errno = Enospace;
putbuf(ibuf);
return -1;
}
if( (baddr = inode_getblk(f, cur_block++)) == 0 ){
putbuf(ibuf);
return -1;
}
buf = getbuf(xf, baddr);
if( !buf ){
putbuf(ibuf);
return -1;
}
if( inode->i_size <= offset ){
de = (DirEntry *)buf->iobuf;
de->inode = 0;
de->rec_len = xf->block_size;
dirtybuf(buf);
inode->i_size = offset + xf->block_size;
dirtybuf(ibuf);
}else{
de = (DirEntry *)buf->iobuf;
}
}
if( de->inode != 0 && de->name_len == namelen &&
!strncmp(name, de->name, namelen) ){
errno = Eexist;
putbuf(ibuf); putbuf(buf);
return -1;
}
offset += de->rec_len;
if( (de->inode == 0 && de->rec_len >= rec_len) ||
(de->rec_len >= DIR_REC_LEN(de->name_len) + rec_len) ){
if( de->inode ){
de1 = (DirEntry *) ((char *)de + DIR_REC_LEN(de->name_len));
de1->rec_len = de->rec_len - DIR_REC_LEN(de->name_len);
de->rec_len = DIR_REC_LEN(de->name_len);
de = de1;
}
de->inode = inr;
de->name_len = namelen;
memcpy(de->name, name, namelen);
dirtybuf(buf);
putbuf(buf);
inode->i_mtime = inode->i_ctime = time(0);
dirtybuf(ibuf);
putbuf(ibuf);
return 0;
}
de = (DirEntry *)((char *)de + de->rec_len);
}
/* not reached */
}
int
unlink( Xfile *file )
{
Xfs *xf = file->xf;
Inode *dir;
int bg, b;
Inode *inode;
Iobuf *buf, *ibuf;
Ext2 ed, es, eb;
if( S_ISDIR(getmode(file)) && !empty_dir(file) ){
chat("non empty directory...");
errno = Eperm;
return -1;
}
es = getext2(xf, EXT2_SUPER, 0);
/* get dir inode */
if( file->pinbr >= es.u.sb->s_inodes_count ){
chat("inode number %d is too big...", file->pinbr);
putext2(es);
errno = Eintern;
return -1;
}
bg = (file->pinbr - 1) / xf->inodes_per_group;
if( bg >= xf->ngroups ){
chat("block group (%d) > groups count...", bg);
putext2(es);
errno = Eintern;
return -1;
}
ed = getext2(xf, EXT2_DESC, bg);
b = ed.u.gd->bg_inode_table +
(((file->pinbr-1) % xf->inodes_per_group) /
xf->inodes_per_block);
putext2(ed);
buf = getbuf(xf, b);
if( !buf ){
putext2(es);
return -1;
}
dir = ((struct Inode *) buf->iobuf) +
((file->pinbr-1) % xf->inodes_per_block);
/* Clean dir entry */
if( delete_entry(xf, dir, file->inbr) < 0 ){
putbuf(buf);
putext2(es);
return -1;
}
if( S_ISDIR(getmode(file)) ){
dir->i_links_count--;
dirtybuf(buf);
}
putbuf(buf);
/* clean blocks */
ibuf = getbuf(xf, file->bufaddr);
if( !ibuf ){
putext2(es);
return -1;
}
inode = ((Inode *)ibuf->iobuf) + file->bufoffset;
if( !S_ISLNK(getmode(file)) ||
(S_ISLNK(getmode(file)) && (inode->i_size > EXT2_N_BLOCKS<<2)) )
if( free_block_inode(file) < 0 ){
chat("error while freeing blocks...");
putext2(es);
putbuf(ibuf);
return -1;
}
/* clean inode */
bg = (file->inbr -1) / xf->inodes_per_group;
b = (file->inbr -1) % xf->inodes_per_group;
eb = getext2(xf, EXT2_BINODE, bg);
clear_bit(b, eb.u.bmp);
dirtyext2(eb);
putext2(eb);
inode->i_dtime = time(0);
inode->i_links_count--;
if( S_ISDIR(getmode(file)) )
inode->i_links_count = 0;
es.u.sb->s_free_inodes_count++;
dirtyext2(es);
putext2(es);
ed = getext2(xf, EXT2_DESC, bg);
ed.u.gd->bg_free_inodes_count++;
if( S_ISDIR(getmode(file)) )
ed.u.gd->bg_used_dirs_count--;
dirtyext2(ed);
putext2(ed);
dirtybuf(ibuf);
putbuf(ibuf);
return 1;
}
int
empty_dir(Xfile *dir)
{
Xfs *xf = dir->xf;
int nblock;
uint offset, i,count;
DirEntry *de;
Inode *inode;
Iobuf *buf, *ibuf;
if( !S_ISDIR(getmode(dir)) )
return 0;
ibuf = getbuf(xf, dir->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + dir->bufoffset;
nblock = (inode->i_blocks * 512) / xf->block_size;
for(i=0, count=0 ; (i < nblock) && (i < EXT2_NDIR_BLOCKS) ; i++){
buf = getbuf(xf, inode->i_block[i]);
if( !buf ){
putbuf(ibuf);
return 0;
}
for(offset=0 ; offset < xf->block_size ; ){
de = (DirEntry *)(buf->iobuf + offset);
if(de->inode)
count++;
offset += de->rec_len;
}
putbuf(buf);
if( count > 2 ){
putbuf(ibuf);
return 0;
}
}
putbuf(ibuf);
return 1;
}
int
free_block_inode(Xfile *file)
{
Xfs *xf = file->xf;
int i, j, k;
ulong b, *y, *z;
uint *x;
int naddr;
Inode *inode;
Iobuf *buf, *buf1, *buf2, *ibuf;
ibuf = getbuf(xf, file->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + file->bufoffset;
for(i=0 ; i < EXT2_IND_BLOCK ; i++){
x = inode->i_block + i;
if( *x == 0 ){ putbuf(ibuf); return 0; }
free_block(xf, *x);
}
naddr = xf->addr_per_block;
/* indirect blocks */
if( (b=inode->i_block[EXT2_IND_BLOCK]) ){
buf = getbuf(xf, b);
if( !buf ){ putbuf(ibuf); return -1; }
for(i=0 ; i < naddr ; i++){
x = ((uint *)buf->iobuf) + i;
if( *x == 0 ) break;
free_block(xf, *x);
}
free_block(xf, b);
putbuf(buf);
}
/* double indirect block */
if( (b=inode->i_block[EXT2_DIND_BLOCK]) ){
buf = getbuf(xf, b);
if( !buf ){ putbuf(ibuf); return -1; }
for(i=0 ; i < naddr ; i++){
x = ((uint *)buf->iobuf) + i;
if( *x== 0 ) break;
buf1 = getbuf(xf, *x);
if( !buf1 ){ putbuf(buf); putbuf(ibuf); return -1; }
for(j=0 ; j < naddr ; j++){
y = ((ulong *)buf1->iobuf) + j;
if( *y == 0 ) break;
free_block(xf, *y);
}
free_block(xf, *x);
putbuf(buf1);
}
free_block(xf, b);
putbuf(buf);
}
/* triple indirect block */
if( (b=inode->i_block[EXT2_TIND_BLOCK]) ){
buf = getbuf(xf, b);
if( !buf ){ putbuf(ibuf); return -1; }
for(i=0 ; i < naddr ; i++){
x = ((uint *)buf->iobuf) + i;
if( *x == 0 ) break;
buf1 = getbuf(xf, *x);
if( !buf1 ){ putbuf(buf); putbuf(ibuf); return -1; }
for(j=0 ; j < naddr ; j++){
y = ((ulong *)buf1->iobuf) + j;
if( *y == 0 ) break;
buf2 = getbuf(xf, *y);
if( !buf2 ){ putbuf(buf); putbuf(buf1); putbuf(ibuf); return -1; }
for(k=0 ; k < naddr ; k++){
z = ((ulong *)buf2->iobuf) + k;
if( *z == 0 ) break;
free_block(xf, *z);
}
free_block(xf, *y);
putbuf(buf2);
}
free_block(xf, *x);
putbuf(buf1);
}
free_block(xf, b);
putbuf(buf);
}
putbuf(ibuf);
return 0;
}
void free_block( Xfs *xf, ulong block )
{
ulong bg;
Ext2 ed, es, eb;
es = getext2(xf, EXT2_SUPER, 0);
bg = (block - es.u.sb->s_first_data_block) / xf->blocks_per_group;
block = (block - es.u.sb->s_first_data_block) % xf->blocks_per_group;
eb = getext2(xf, EXT2_BBLOCK, bg);
clear_bit(block, eb.u.bmp);
dirtyext2(eb);
putext2(eb);
es.u.sb->s_free_blocks_count++;
dirtyext2(es);
putext2(es);
ed = getext2(xf, EXT2_DESC, bg);
ed.u.gd->bg_free_blocks_count++;
dirtyext2(ed);
putext2(ed);
}
int
delete_entry(Xfs *xf, Inode *inode, int inbr)
{
int nblock = (inode->i_blocks * 512) / xf->block_size;
uint offset, i;
DirEntry *de, *pde;
Iobuf *buf;
if( !S_ISDIR(inode->i_mode) )
return -1;
for(i=0 ; (i < nblock) && (i < EXT2_NDIR_BLOCKS) ; i++){
buf = getbuf(xf, inode->i_block[i]);
if( !buf )
return -1;
pde = 0;
for(offset=0 ; offset < xf->block_size ; ){
de = (DirEntry *)(buf->iobuf + offset);
if( de->inode == inbr ){
if( pde )
pde->rec_len += de->rec_len;
de->inode = 0;
dirtybuf(buf);
putbuf(buf);
return 1;
}
offset += de->rec_len;
pde = de;
}
putbuf(buf);
}
errno = Enonexist;
return -1;
}
int
truncfile(Xfile *f)
{
Inode *inode;
Iobuf *ibuf;
chat("trunc(fid=%d) ...", f->fid);
ibuf = getbuf(f->xf, f->bufaddr);
if( !ibuf )
return -1;
inode = ((Inode *)ibuf->iobuf) + f->bufoffset;
if( free_block_inode(f) < 0 ){
chat("error while freeing blocks...");
putbuf(ibuf);
return -1;
}
inode->i_atime = inode->i_mtime = time(0);
inode->i_blocks = 0;
inode->i_size = 0;
memset(inode->i_block, 0, EXT2_N_BLOCKS*sizeof(ulong));
dirtybuf(ibuf);
putbuf(ibuf);
chat("trunc ok...");
return 0;
}
long
getmode(Xfile *f)
{
Iobuf *ibuf;
long mode;
ibuf = getbuf(f->xf, f->bufaddr);
if( !ibuf )
return -1;
mode = (((Inode *)ibuf->iobuf) + f->bufoffset)->i_mode;
putbuf(ibuf);
return mode;
}
void
CleanSuper(Xfs *xf)
{
Ext2 es;
es = getext2(xf, EXT2_SUPER, 0);
es.u.sb->s_state = EXT2_VALID_FS;
dirtyext2(es);
putext2(es);
}
int
test_bit(int i, void *data)
{
char *pt = (char *)data;
return pt[i>>3] & (0x01 << (i&7));
}
int
set_bit(int i, void *data)
{
char *pt;
if( test_bit(i, data) )
return 1; /* bit already set !!! */
pt = (char *)data;
pt[i>>3] |= (0x01 << (i&7));
return 0;
}
int
clear_bit(int i, void *data)
{
char *pt;
if( !test_bit(i, data) )
return 1; /* bit already clear !!! */
pt = (char *)data;
pt[i>>3] &= ~(0x01 << (i&7));
return 0;
}
void *
memscan( void *data, int c, int count )
{
char *pt = (char *)data;
while( count ){
if( *pt == c )
return (void *)pt;
count--;
pt++;
}
return (void *)pt;
}
int
find_first_zero_bit( void *data, int count /* in byte */)
{
char *pt = (char *)data;
int n, i;
n = 0;
while( n < count ){
for(i=0 ; i < 8 ; i++)
if( !(*pt & (0x01 << (i&7))) )
return (n<<3) + i;
n++; pt++;
}
return n << 3;
}
int
find_next_zero_bit( void *data, int count /* in byte */, int where)
{
char *pt = (((char *)data) + (where >> 3));
int n, i;
n = where >> 3;
i = where & 7;
while( n < count ){
for(; i < 8 ; i++)
if( !(*pt & (0x01 << (i&7))) )
return (n<<3) + i;
n++; pt++; i=0;
}
return n << 3;
}
int
ffz( int x )
{
int c = 0;
while( x&1 ){
c++;
x >>= 1;
}
return c;
}
|