summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ext2srv/ext2subs.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ext2srv/ext2subs.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ext2srv/ext2subs.c')
-rwxr-xr-xsys/src/cmd/ext2srv/ext2subs.c1870
1 files changed, 1870 insertions, 0 deletions
diff --git a/sys/src/cmd/ext2srv/ext2subs.c b/sys/src/cmd/ext2srv/ext2subs.c
new file mode 100755
index 000000000..77a0de49b
--- /dev/null
+++ b/sys/src/cmd/ext2srv/ext2subs.c
@@ -0,0 +1,1870 @@
+/*
+ * 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;
+}