summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ext2srv
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
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ext2srv')
-rwxr-xr-xsys/src/cmd/ext2srv/chat.c53
-rwxr-xr-xsys/src/cmd/ext2srv/dat.h222
-rwxr-xr-xsys/src/cmd/ext2srv/errstr.h17
-rwxr-xr-xsys/src/cmd/ext2srv/ext2fs.c348
-rwxr-xr-xsys/src/cmd/ext2srv/ext2srv.man110
-rwxr-xr-xsys/src/cmd/ext2srv/ext2subs.c1870
-rwxr-xr-xsys/src/cmd/ext2srv/fns.h70
-rwxr-xr-xsys/src/cmd/ext2srv/iobuf.c174
-rwxr-xr-xsys/src/cmd/ext2srv/mkfile18
-rwxr-xr-xsys/src/cmd/ext2srv/readme53
-rwxr-xr-xsys/src/cmd/ext2srv/version36
-rwxr-xr-xsys/src/cmd/ext2srv/xfile.c161
-rwxr-xr-xsys/src/cmd/ext2srv/xfssrv.c95
13 files changed, 3227 insertions, 0 deletions
diff --git a/sys/src/cmd/ext2srv/chat.c b/sys/src/cmd/ext2srv/chat.c
new file mode 100755
index 000000000..abc789703
--- /dev/null
+++ b/sys/src/cmd/ext2srv/chat.c
@@ -0,0 +1,53 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "dat.h"
+#include "fns.h"
+
+#define SIZE 1024
+#define DOTDOT (&fmt+1)
+
+int chatty;
+
+void
+chat(char *fmt, ...)
+{
+ char buf[SIZE], *out;
+ va_list arg;
+
+ if (!chatty)
+ return;
+
+ va_start(arg, fmt);
+ out = vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ write(2, buf, (long)(out-buf));
+}
+
+void
+mchat(char *fmt, ...)
+{
+ char buf[SIZE], *out;
+ va_list arg;
+
+ va_start(arg, fmt);
+ out = vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ write(2, buf, (long)(out-buf));
+}
+void
+panic(char *fmt, ...)
+{
+ char buf[SIZE];
+ va_list arg;
+ int n;
+
+ n = sprint(buf, "%s %d: panic ", argv0, getpid());
+ va_start(arg, fmt);
+ vseprint(buf+n, buf+sizeof(buf)-n, fmt, arg);
+ va_end(arg);
+ fprint(2, "%s: %r\n", buf);
+ exits("panic");
+}
diff --git a/sys/src/cmd/ext2srv/dat.h b/sys/src/cmd/ext2srv/dat.h
new file mode 100755
index 000000000..ce4e61e35
--- /dev/null
+++ b/sys/src/cmd/ext2srv/dat.h
@@ -0,0 +1,222 @@
+typedef struct Xfs Xfs;
+typedef struct Xfile Xfile;
+typedef struct Iobuf Iobuf;
+typedef struct Ext2 Ext2;
+
+typedef struct SuperBlock SuperBlock;
+typedef struct GroupDesc GroupDesc;
+typedef struct Inode Inode;
+typedef struct DirEntry DirEntry;
+
+#define SECTORSIZE 512
+#define OFFSET_SUPER_BLOCK 1024
+
+#define EXT2_SUPER_MAGIC 0xEF53
+#define EXT2_MIN_BLOCK_SIZE 1024
+#define EXT2_MAX_BLOCK_SIZE 4096
+#define EXT2_ROOT_INODE 2
+#define EXT2_FIRST_INO 11
+#define EXT2_VALID_FS 0x0001
+#define EXT2_ERROR_FS 0x0002
+
+/*
+ * Structure of the super block
+ */
+struct SuperBlock {
+ uint s_inodes_count; /* Inodes count */
+ uint s_blocks_count; /* Blocks count */
+ uint s_r_blocks_count; /* Reserved blocks count */
+ uint s_free_blocks_count; /* Free blocks count */
+ uint s_free_inodes_count; /* Free inodes count */
+ uint s_first_data_block; /* First Data Block */
+ uint s_log_block_size; /* Block size */
+ int s_log_frag_size; /* Fragment size */
+ uint s_blocks_per_group; /* # Blocks per group */
+ uint s_frags_per_group; /* # Fragments per group */
+ uint s_inodes_per_group; /* # Inodes per group */
+ uint s_mtime; /* Mount time */
+ uint s_wtime; /* Write time */
+ ushort s_mnt_count; /* Mount count */
+ short s_max_mnt_count; /* Maximal mount count */
+ ushort s_magic; /* Magic signature */
+ ushort s_state; /* File system state */
+ ushort s_errors; /* Behaviour when detecting errors */
+ ushort s_pad;
+ uint s_lastcheck; /* time of last check */
+ uint s_checkinterval; /* max. time between checks */
+ uint s_creator_os; /* OS */
+ uint s_rev_level; /* Revision level */
+ ushort s_def_resuid; /* Default uid for reserved blocks */
+ ushort s_def_resgid; /* Default gid for reserved blocks */
+ uint s_reserved[235]; /* Padding to the end of the block */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct GroupDesc
+{
+ uint bg_block_bitmap; /* Blocks bitmap block */
+ uint bg_inode_bitmap; /* Inodes bitmap block */
+ uint bg_inode_table; /* Inodes table block */
+ ushort bg_free_blocks_count; /* Free blocks count */
+ ushort bg_free_inodes_count; /* Free inodes count */
+ ushort bg_used_dirs_count; /* Directories count */
+ ushort bg_pad;
+ uint bg_reserved[3];
+};
+
+/*
+ * Constants relative to the data blocks
+ */
+#define EXT2_NDIR_BLOCKS 12
+#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct Inode {
+ ushort i_mode; /* File mode */
+ ushort i_uid; /* Owner Uid */
+ uint i_size; /* Size in bytes */
+ uint i_atime; /* Access time */
+ uint i_ctime; /* Creation time */
+ uint i_mtime; /* Modification time */
+ uint i_dtime; /* Deletion Time */
+ ushort i_gid; /* Group Id */
+ ushort i_links_count; /* Links count */
+ uint i_blocks; /* Blocks count */
+ uint i_flags; /* File flags */
+ uint osd1;
+ uint i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ uint i_version; /* File version (for NFS) */
+ uint i_file_acl; /* File ACL */
+ uint i_dir_acl; /* Directory ACL */
+ uint i_faddr; /* Fragment address */
+ uchar osd2[12];
+};
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT2_NAME_LEN 255
+#define DIR_REC_LEN(name_len) (((name_len) + 8 + 3) & ~3)
+
+struct DirEntry {
+ uint inode; /* Inode number */
+ ushort rec_len; /* Directory entry length */
+ uchar name_len; /* Name length */
+ uchar reserved;
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+
+#define S_IFMT 00170000
+#define S_IFLNK 0120000
+#define S_IFREG 0100000
+#define S_IFDIR 0040000
+
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+
+#define DEFAULT_UID 200
+#define DEFAULT_GID 100
+
+struct Iobuf
+{
+ Xfs *dev;
+ long addr;
+ Iobuf *next;
+ Iobuf *prev;
+ Iobuf *hash;
+ int busy;
+ int dirty;
+ char *iobuf;
+};
+
+struct Xfs{
+ Xfs *next;
+ char *name; /* of file containing external f.s. */
+ Qid qid; /* of file containing external f.s. */
+ long ref; /* attach count */
+ Qid rootqid; /* of plan9 constructed root directory */
+ short dev;
+ short fmt;
+ void *ptr;
+
+ /* data from super block */
+
+ int block_size;
+ int desc_per_block;
+ int inodes_per_group;
+ int inodes_per_block;
+ int addr_per_block;
+ int blocks_per_group;
+
+ int ngroups;
+ int superaddr, superoff;
+ int grpaddr;
+};
+
+struct Xfile{
+ Xfile *next; /* in hash bucket */
+ long client;
+ long fid;
+ Xfs * xf;
+ void * ptr;
+
+ uint inbr; /* inode nbr */
+ uint pinbr; /* parrent inode */
+ ulong bufaddr; /* addr of inode block */
+ ulong bufoffset;
+ int root; /* true on attach for ref count */
+ int dirindex; /* next dir entry to read */
+};
+
+#define EXT2_SUPER 1
+#define EXT2_DESC 2
+#define EXT2_BBLOCK 3
+#define EXT2_BINODE 4
+
+struct Ext2{
+ char type;
+ union{
+ SuperBlock *sb;
+ GroupDesc *gd;
+ char *bmp;
+ }u;
+ Iobuf *buf;
+};
+
+#define DESC_ADDR(xf,n) ( (xf)->grpaddr + ((n)/(xf)->desc_per_block) )
+#define DESC_OFFSET(xf,d,n) ( ((GroupDesc *)(d)) + ((n)%(xf)->desc_per_block) )
+
+enum{
+ Asis, Clean, Clunk
+};
+
+enum{
+ Enevermind,
+ Eformat,
+ Eio,
+ Enomem,
+ Enonexist,
+ Eexist,
+ Eperm,
+ Enofilsys,
+ Eauth,
+ Enospace,
+ Elink,
+ Elongname,
+ Eintern,
+ Ecorrupt,
+ Enotclean
+};
+
+extern int chatty;
+extern int errno;
+extern char *deffile;
+extern int rdonly;
diff --git a/sys/src/cmd/ext2srv/errstr.h b/sys/src/cmd/ext2srv/errstr.h
new file mode 100755
index 000000000..8ee3a54b2
--- /dev/null
+++ b/sys/src/cmd/ext2srv/errstr.h
@@ -0,0 +1,17 @@
+char *errmsg[] = {
+ [Enevermind] "never mind",
+ [Eformat] "unknown format",
+ [Eio] "I/O error",
+ [Enomem] "server out of memory",
+ [Enonexist] "file does not exist",
+ [Eexist] "file already exist",
+ [Eperm] "permission denied",
+ [Enofilsys] "no file system device specified",
+ [Eauth] "authentication failed",
+ [Enospace] "no space on device",
+ [Elink] "write is only allowed in regular files",
+ [Elongname] "name is too long",
+ [Eintern] "internal Ext2 error",
+ [Ecorrupt] "corrupt filesystem",
+ [Enotclean] "fs not clean ... running e2fsck is recommended"
+};
diff --git a/sys/src/cmd/ext2srv/ext2fs.c b/sys/src/cmd/ext2srv/ext2fs.c
new file mode 100755
index 000000000..458b56bfc
--- /dev/null
+++ b/sys/src/cmd/ext2srv/ext2fs.c
@@ -0,0 +1,348 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "dat.h"
+#include "fns.h"
+
+#define thdr r->ifcall
+#define rhdr r->ofcall
+
+extern int errno;
+
+static void
+response(Req *r)
+{
+ char *err;
+
+ if (errno) {
+ err = xerrstr(errno);
+ chat("%s\n", err);
+ respond(r, err);
+ } else {
+ chat("OK\n");
+ respond(r, nil);
+ }
+}
+
+static void
+rattach(Req *r)
+{
+ Xfs *xf;
+ Xfile *root;
+
+ chat("attach(fid=%d,uname=\"%s\",aname=\"%s\",afid=\"%d\")...",
+ thdr.fid, thdr.uname, thdr.aname, thdr.afid);
+
+ errno = 0;
+ root = xfile(r->fid, Clean);
+ if(!root){
+ errno = Enomem;
+ goto error;
+ }
+ root->xf = xf = getxfs(thdr.aname);
+ if(!xf)
+ goto error;
+
+ /* now attach root inode */
+ if( get_inode(root, EXT2_ROOT_INODE) < 0 )
+ goto error;
+
+ r->fid->qid.type = QTDIR;
+ r->fid->qid.vers = 0;
+ root->xf->rootqid = r->fid->qid;
+ root->pinbr = EXT2_ROOT_INODE;
+ root->root = 1;
+ rhdr.qid = r->fid->qid;
+
+error:
+ response(r);
+}
+static char *
+rclone(Fid *fid, Fid *newfid)
+{
+ Xfile *of = xfile(fid, Asis);
+ Xfile *nf = xfile(newfid, Clean);
+
+ chat("clone(fid=%d,newfid=%d)...", fid->fid, newfid->fid);
+ errno = 0;
+ if(!of)
+ errno = Eio;
+ else if(!nf)
+ errno = Enomem;
+ else{
+ Xfile *next = nf->next;
+ *nf = *of;
+ nf->next = next;
+ nf->fid = newfid->fid;
+ nf->root = 0;
+ }
+ chat("%s\n", errno? xerrstr(errno) : "OK");
+ return errno ? xerrstr(errno) : 0;
+}
+static char *
+rwalk1(Fid *fid, char *name, Qid *qid)
+{
+ Xfile *f=xfile(fid, Asis);
+ int nr, sinbr = 0;
+
+ chat("walk1(fid=%d,name=\"%s\")...", fid->fid, name);
+ errno = 0;
+ if( !f ){
+ chat("no xfile...");
+ goto error;
+ }
+ if( !(fid->qid.type & QTDIR) ){
+ chat("qid.type=0x%x...", fid->qid.type);
+ goto error;
+ }
+ sinbr = f->pinbr;
+ if( name == 0 || name[0] == 0 || !strcmp(name, ".") ){
+ *qid = fid->qid;
+ goto ok;
+ }else if( !strcmp(name, "..") ){
+ if( fid->qid.path == f->xf->rootqid.path ){
+ chat("walkup from root...");
+ *qid = fid->qid;
+ goto ok;
+ }
+ if( get_inode(f, f->pinbr) < 0 )
+ goto error;
+ if( f->pinbr == EXT2_ROOT_INODE ){
+ *qid = f->xf->rootqid;
+ f->pinbr = EXT2_ROOT_INODE;
+ } else {
+ *qid = (Qid){f->pinbr,0,QTDIR};
+ f->inbr = f->pinbr;
+ if( (nr = get_file(f, "..")) < 0 )
+ goto error;
+ f->pinbr = nr;
+ }
+ }else{
+ f->pinbr = f->inbr;
+ if( (nr = get_file(f, name)) < 0 )
+ goto error;
+ if( get_inode(f, nr) < 0 )
+ goto error;
+ *qid = (Qid){nr,0,0};
+ if( nr == EXT2_ROOT_INODE )
+ *qid = f->xf->rootqid;
+ else if( S_ISDIR(getmode(f)) )
+ qid->type = QTDIR;
+ /*strcpy(f->name, thdr.name);*/
+ }
+ok:
+ chat("OK\n");
+ return 0;
+error:
+ f->pinbr = sinbr;
+ chat("%s\n", xerrstr(Enonexist));
+ return xerrstr(Enonexist);
+}
+static void
+rstat(Req *r)
+{
+ Xfile *f=xfile(r->fid, Asis);
+
+ chat("stat(fid=%d)...", thdr.fid);
+ errno = 0;
+ if( !f )
+ errno = Eio;
+ else{
+ dostat(r->fid->qid, f, &r->d);
+ }
+ response(r);
+}
+static void
+rwstat(Req *r)
+{
+ Xfile *f=xfile(r->fid, Asis);
+
+ chat("wstat(fid=%d)...", thdr.fid);
+ errno = 0;
+ if( !f )
+ errno = Eio;
+ else
+ dowstat(f, &r->d);
+ response(r);
+}
+static void
+rread(Req *r)
+{
+ Xfile *f;
+ int nr;
+
+ chat("read(fid=%d,offset=%lld,count=%d)...",
+ thdr.fid, thdr.offset, thdr.count);
+ errno = 0;
+ if ( !(f=xfile(r->fid, Asis)) )
+ goto error;
+ if( r->fid->qid.type & QTDIR ){
+ nr = readdir(f, r->rbuf, thdr.offset, thdr.count);
+ }else
+ nr = readfile(f, r->rbuf, thdr.offset, thdr.count);
+
+ if(nr >= 0){
+ rhdr.count = nr;
+ chat("rcnt=%d...OK\n", nr);
+ respond(r, nil);
+ return;
+ }
+error:
+ errno = Eio;
+ response(r);
+}
+static void
+rwrite(Req *r)
+{
+ Xfile *f; int nr;
+
+ chat("write(fid=%d,offset=%lld,count=%d)...",
+ thdr.fid, thdr.offset, thdr.count);
+
+ errno = 0;
+ if (!(f=xfile(r->fid, Asis)) ){
+ errno = Eio;
+ goto error;
+ }
+ if( !S_ISREG(getmode(f)) ){
+ errno = Elink;
+ goto error;
+ }
+ nr = writefile(f, thdr.data, thdr.offset, thdr.count);
+ if(nr >= 0){
+ rhdr.count = nr;
+ chat("rcnt=%d...OK\n", nr);
+ respond(r, nil);
+ return;
+ }
+ errno = Eio;
+error:
+ response(r);
+}
+static void
+destroyfid(Fid *fid)
+{
+ chat("destroy(fid=%d)\n", fid->fid);
+ xfile(fid, Clunk);
+ /*syncbuf(xf);*/
+}
+static void
+ropen(Req *r)
+{
+ Xfile *f;
+
+ chat("open(fid=%d,mode=%d)...", thdr.fid, thdr.mode);
+
+ errno = 0;
+ f = xfile(r->fid, Asis);
+ if( !f ){
+ errno = Eio;
+ goto error;
+ }
+
+ if(thdr.mode & OTRUNC){
+ if( !S_ISREG(getmode(f)) ){
+ errno = Eperm;
+ goto error;
+ }
+ if(truncfile(f) < 0){
+ goto error;
+ }
+ }
+ chat("f->qid=0x%8.8lux...", r->fid->qid.path);
+ rhdr.qid = r->fid->qid;
+error:
+ response(r);
+}
+static void
+rcreate(Req *r)
+{
+ Xfile *f;
+ int inr, perm;
+
+ chat("create(fid=%d,name=\"%s\",perm=%uo,mode=%d)...",
+ thdr.fid, thdr.name, thdr.perm, thdr.mode);
+
+ errno = 0;
+ if(strcmp(thdr.name, ".") == 0 || strcmp(thdr.name, "..") == 0){
+ errno = Eperm;
+ goto error;
+ }
+ f = xfile(r->fid, Asis);
+ if( !f ){
+ errno = Eio;
+ goto error;
+ }
+ if( strlen(thdr.name) > EXT2_NAME_LEN ){
+ chat("name too long ...");
+ errno = Elongname;
+ goto error;
+ }
+
+ /* create */
+ errno = 0;
+ if( thdr.perm & DMDIR ){
+ perm = (thdr.perm & ~0777) |
+ (getmode(f) & thdr.perm & 0777);
+ perm |= S_IFDIR;
+ inr = create_dir(f, thdr.name, perm);
+ }else{
+ perm = (thdr.perm & (~0777|0111)) |
+ (getmode(f) & thdr.perm & 0666);
+ perm |= S_IFREG;
+ inr = create_file(f, thdr.name, perm);
+
+ }
+ if( inr < 0 )
+ goto error;
+
+ /* fill with new inode */
+ f->pinbr = f->inbr;
+ if( get_inode(f, inr) < 0 ){
+ errno = Eio;
+ goto error;
+ }
+ r->fid->qid = (Qid){inr, 0, 0};
+ if( S_ISDIR(getmode(f)) )
+ r->fid->qid.type |= QTDIR;
+ chat("f->qid=0x%8.8lux...", r->fid->qid.path);
+ rhdr.qid = r->fid->qid;
+error:
+ response(r);
+}
+static void
+rremove(Req *r)
+{
+ Xfile *f=xfile(r->fid, Asis);
+
+ chat("remove(fid=%d) ...", thdr.fid);
+
+ errno = 0;
+ if(!f){
+ errno = Eio;
+ goto error;
+ }
+
+ /* check permission here !!!!*/
+
+ unlink(f);
+
+error:
+ response(r);
+}
+
+Srv ext2srv = {
+ .destroyfid = destroyfid,
+ .attach = rattach,
+ .stat = rstat,
+ .wstat = rwstat,
+ .clone = rclone,
+ .walk1 = rwalk1,
+ .open = ropen,
+ .read = rread,
+ .write = rwrite,
+ .create = rcreate,
+ .remove = rremove,
+};
diff --git a/sys/src/cmd/ext2srv/ext2srv.man b/sys/src/cmd/ext2srv/ext2srv.man
new file mode 100755
index 000000000..6dcb950fb
--- /dev/null
+++ b/sys/src/cmd/ext2srv/ext2srv.man
@@ -0,0 +1,110 @@
+.TH EXT2SRV 4
+.SH NAME
+ext2srv \- ext2 file system
+.SH SYNOPSIS
+.B ext2srv
+[
+.B -vrs
+] [
+.B -f
+.I file
+] [
+.B -p
+.I passwd
+] [
+.B -g
+.I group
+] [
+.I service
+]
+.SH DESCRIPTION
+.I Ext2srv
+is a file server that interprets the Linux Second Extended File System.
+A single instance of
+.I ext2srv
+can provide access to multiple ext2 partitions simultaneously.
+.PP
+.I Ext2srv
+posts a file descriptor named
+.I service
+(default
+.BR ext2 )
+in the
+.B /srv
+directory.
+To access an ext2 file system on a device, use
+.B mount
+with the
+.I spec
+argument
+(see
+.IR bind (1))
+the name of the file holding the raw ext2 file system, typically the disk or partition.
+If
+.I spec
+is undefined in the
+.BR mount ,
+.I ext2srv
+will use
+.I file
+as the default name for the device holding the file system.
+.PP
+Normally
+.I ext2srv
+creates a pipe to act as the communications channel between
+itself and its clients.
+The
+.B -s
+flag instructs
+.I ext2srv
+to use its standard input and output instead.
+This flag also prevents the creation of an explicit service file in
+.BR /srv .
+.PP
+The
+.B -v
+flag causes verbose output for debugging, while
+the
+.B -r
+flag (recommended) makes the file system read-only.
+The optional
+.B -p
+and
+.B -g
+flags specify Unix-format password (respectively group) files
+that give the mapping between the numeric user- and group-ID
+numbers in the ext2 file system and the strings reported by Plan 9 status
+inquiries.
+.PP
+There is no authentication or permission checking.
+Anyone who can access the ext2 file system will have full access
+to all its files, including write access if
+.I ext2srv
+is not started with the
+.B -r
+flag, irrespective of file ownership and permission flags.
+.PP
+Some file system state is cached in memory, and may
+be flushed only when the file system is unmounted.
+Therefore if
+.I ext2srv
+is stopped or the machine is rebooted while an ext2 file system
+is still mounted,
+the superblock on the device will have been marked `not valid'
+(unless the
+.B -r
+flag was used),
+and a
+.I fsck
+will be required before that file system may be mounted again.
+.SH BUGS
+There is no authentication or permission checking.
+The implementation has not tracked any changes to the ext2
+specification since it was written.
+There may be other bugs.
+It is advisable to use
+.I ext2srv
+in read-only mode whenever possible.
+.SH AUTHOR
+Bodet Laurent (bl@mime.univ-paris8.fr),
+with later updates by Russ Cox and Richard Miller.
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;
+}
diff --git a/sys/src/cmd/ext2srv/fns.h b/sys/src/cmd/ext2srv/fns.h
new file mode 100755
index 000000000..e53488d7e
--- /dev/null
+++ b/sys/src/cmd/ext2srv/fns.h
@@ -0,0 +1,70 @@
+void chat(char*, ...);
+Xfile * clean(Xfile*);
+void dirdump(void*);
+int dosfs(Xfs*);
+int emptydir(Xfile*);
+int falloc(Xfs*);
+int fileaddr(Xfile*, int, int);
+int getfat(Xfs*, int);
+int getfile(Xfile*);
+Xfs * getxfs(char*);
+void panic(char*, ...);
+void putfat(Xfs*, int, int);
+void putfile(Xfile*);
+void refxfs(Xfs*, int);
+long writefile(Xfile*, void*, vlong, long);
+char * xerrstr(int);
+Xfile * xfile(Fid*, int);
+int xfspurge(void);
+
+int ext2fs(Xfs *);
+int get_inode( Xfile *, uint);
+char *getname(Xfile *, char *);
+int get_file(Xfile *, char *);
+int bmap( Xfile *f, int block );
+int ffz(int);
+long readdir(Xfile*, void*, vlong, long);
+long readfile(Xfile*, void*, vlong, long);
+void dostat(Qid, Xfile *, Dir *);
+int new_block( Xfile *, int);
+int test_bit(int, void *);
+int set_bit(int, void *);
+int clear_bit(int , void *);
+void *memscan(void *, int, int);
+int find_first_zero_bit(void *, int);
+int find_next_zero_bit(void *, int, int);
+int block_getblk(Xfile *, int, int);
+int inode_getblk(Xfile *, int);
+int getblk(Xfile *, int);
+int new_inode(Xfile *, int);
+int add_entry(Xfile *, char *, int);
+int create_file(Xfile *, char *, int);
+int create_dir(Xfile *, char *, int);
+int unlink(Xfile *);
+int delete_entry(Xfs *, Inode *, int);
+int free_block_inode(Xfile *);
+void free_block( Xfs *, ulong);
+void free_inode( Xfs *, int);
+int empty_dir(Xfile *);
+int truncfile(Xfile *);
+int dowstat(Xfile *, Dir *);
+long getmode(Xfile *);
+Ext2 getext2(Xfs *, char, int);
+void CleanSuper(Xfs *);
+
+/* Iobuf operations */
+
+Iobuf *getbuf(Xfs *, long addr);
+void putbuf(Iobuf *);
+void purgebuf(Xfs *);
+void iobuf_init(void);
+int xread(Xfs *, Iobuf *, long);
+void syncbuf(void);
+void xwrite(Iobuf *);
+void dirtybuf(Iobuf *);
+
+void mchat(char *fmt, ...);
+void dumpbuf(void);
+
+void gidfile(char*);
+void uidfile(char*);
diff --git a/sys/src/cmd/ext2srv/iobuf.c b/sys/src/cmd/ext2srv/iobuf.c
new file mode 100755
index 000000000..07e834c3c
--- /dev/null
+++ b/sys/src/cmd/ext2srv/iobuf.c
@@ -0,0 +1,174 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "dat.h"
+#include "fns.h"
+
+#define NIOBUF 100
+#define HIOB (NIOBUF/3)
+
+static Iobuf* hiob[HIOB]; /* hash buckets */
+static Iobuf iobuf[NIOBUF]; /* buffer headers */
+static Iobuf* iohead;
+static Iobuf* iotail;
+
+Iobuf*
+getbuf(Xfs *dev, long addr)
+{
+ Iobuf *p, *h, **l, **f;
+
+ l = &hiob[addr%HIOB];
+ for(p = *l; p; p = p->hash) {
+ if(p->addr == addr && p->dev == dev) {
+ p->busy++;
+ return p;
+ }
+ }
+ /* Find a non-busy buffer from the tail */
+ for(p = iotail; p && (p->busy > 0); p = p->prev)
+ ;
+ if(!p)
+ panic("all buffers busy");
+ if(p->dirty){
+ xwrite(p);
+ p->dirty = 0;
+ }
+
+ if( xread(dev, p, addr) < 0)
+ return 0;
+ /* Delete from hash chain */
+ f = &hiob[p->addr%HIOB];
+ if( *f == p )
+ *f = p->hash;
+ else {
+ for(h = *f; h ; h = h->hash)
+ if( h->hash == p ){
+ h->hash = p->hash;
+ break;
+ }
+ }
+ /* Fill and hash */
+ p->hash = *l;
+ *l = p;
+ p->addr = addr;
+ p->dev = dev;
+ p->busy=1;
+
+ return p;
+}
+void
+putbuf(Iobuf *p)
+{
+ if(p->busy <= 0)
+ panic("putbuf");
+ p->busy--;
+
+ /* Link onto head for lru */
+ if(p == iohead)
+ return;
+ if( p == iotail ){
+ p->prev->next = 0;
+ iotail = p->prev;
+ }else{
+ p->prev->next = p->next;
+ p->next->prev = p->prev;
+ }
+
+ p->prev = 0;
+ p->next = iohead;
+ iohead->prev = p;
+ iohead = p;
+}
+void
+dirtybuf(Iobuf *p)
+{
+ if(p->busy <=0)
+ panic("dirtybuf");
+ p->dirty = 1;
+}
+void
+syncbuf(void)
+{
+ Iobuf *p;
+
+ for(p=&iobuf[0] ; p<&iobuf[NIOBUF]; p++)
+ if( p->dirty ){
+ xwrite(p);
+ p->dirty = 0;
+ }
+}
+void
+purgebuf(Xfs *dev)
+{
+ Iobuf *p;
+
+ for(p=&iobuf[0]; p<&iobuf[NIOBUF]; p++)
+ if(p->dev == dev)
+ p->busy = 0;
+
+ /* Blow hash chains */
+ memset(hiob, 0, sizeof(hiob));
+}
+void
+iobuf_init(void)
+{
+ Iobuf *p;
+
+ iohead = iobuf;
+ iotail = iobuf+NIOBUF-1;
+
+ for(p = iobuf; p <= iotail; p++) {
+ p->next = p+1;
+ p->prev = p-1;
+
+ p->iobuf = (char *)malloc(EXT2_MAX_BLOCK_SIZE);
+ if(p->iobuf == 0)
+ panic("iobuf_init");
+ }
+
+ iohead->prev = 0;
+ iotail->next = 0;
+}
+int
+xread(Xfs *dev, Iobuf *p, long addr)
+{
+ /*chat("xread %d,%d...", dev->dev, addr);*/
+
+ seek(dev->dev, (vlong)addr*dev->block_size, 0);
+ if(read(dev->dev, p->iobuf, dev->block_size) != dev->block_size){
+ chat("xread %d, block=%d failed ...", dev->dev, addr);
+ errno = Eio;
+ return -1;
+ }
+ /*chat("xread ok...");*/
+ return 0;
+}
+void
+xwrite(Iobuf *p)
+{
+ Xfs *dev;
+ long addr;
+
+ dev = p->dev;
+ addr = p->addr;
+ /*chat("xwrite %d,%d...", dev->dev, addr);*/
+
+ seek(dev->dev, (vlong)addr*dev->block_size, 0);
+ if(write(dev->dev, p->iobuf, dev->block_size) != dev->block_size){
+ chat("xwrite %d, block=%d failed ...", dev->dev, addr);
+ errno = Eio;
+ return;
+ }
+ /*chat("xwrite ok...");*/
+}
+void
+dumpbuf(void)
+{
+ Iobuf *p;
+
+ for(p = iotail; p ; p = p->prev)
+ if( p->busy )
+ mchat("\nHi ERROR buf(%x, %d, %d)\n", p, p->addr, p->busy);
+}
diff --git a/sys/src/cmd/ext2srv/mkfile b/sys/src/cmd/ext2srv/mkfile
new file mode 100755
index 000000000..09543f22e
--- /dev/null
+++ b/sys/src/cmd/ext2srv/mkfile
@@ -0,0 +1,18 @@
+</$objtype/mkfile
+
+TARG=ext2srv
+OFILES=\
+ xfssrv.$O\
+ xfile.$O\
+ ext2fs.$O\
+ ext2subs.$O\
+ chat.$O\
+ iobuf.$O\
+
+HFILES=dat.h\
+ fns.h\
+
+BIN=/$objtype/bin
+</sys/src/cmd/mkone
+
+xfssrv.$O: errstr.h
diff --git a/sys/src/cmd/ext2srv/readme b/sys/src/cmd/ext2srv/readme
new file mode 100755
index 000000000..30d99f705
--- /dev/null
+++ b/sys/src/cmd/ext2srv/readme
@@ -0,0 +1,53 @@
+Ext2srv Version 0.2
+----------------
+
+Ext2srv is a file server that interprets EXT2 file systems. Ext2srv is identical
+to dossrv in specification.
+
+I added just one option. By default ext2srv search for the first ext2 partition
+on the device (typically a disk) given by the mount spec option (see bind(1)).
+So, if you have different ext2 partitions on the same disk you can select one
+of them by adding the partition number at the end of the device in the mount
+system call. For example
+
+ mount -c /srv/ext2 /n/linux /dev/hd1disk:3
+
+forces the server to look for ext2 filesystem on the third partition of your second
+hard drive.
+
+
+WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+
+ Ext2srv uses some cache. So you must unmount the directory where you
+ mount your ext2 partition. It's the only way to synchronise dirty buffers
+ with the disk.
+
+ Don't reboot your terminal (^t^t r) without explicitly unmount.
+
+ Using something like this script is recommended :
+
+ #!/bin/rc
+
+ unmount /n/linux >[2] /dev/null
+ unmount /n/linux2 >[2] /dev/null
+ disk/kfscmd halt
+
+WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+
+
+I provide this software `as is' and without any warranty. Feed back are welcome !!!
+
+bl@mime.univ-paris8.fr
+
+changes 5/17/2000 - threw away partition table
+walking, fixed name_len (it's a uchar not a ushort).
+
+-rsc
+
+changes for 4th edition 13 May 2002 - miller@hamnavoe.demon.co.uk
+ - adapted for 9P2000
+ - added [-p passwd] [-g group] args as in tapefs(4)
+ - create makes files with user and group of parent directory (not 100/200)
+ - prevent writing to non-regular files
+ - correct calculation of group descriptor block location when bsize!=1024
+
diff --git a/sys/src/cmd/ext2srv/version b/sys/src/cmd/ext2srv/version
new file mode 100755
index 000000000..5111935d3
--- /dev/null
+++ b/sys/src/cmd/ext2srv/version
@@ -0,0 +1,36 @@
+# ext2srv
+# [bl]
+
+on trouve le numero de version sur les 2 premières lignes du
+fichier ext2subs.c.
+
+
+Version 0.1 :
+
+1) il n'ya plus de copie d'inode
+ tous les iobuf utilisés dans un fonction
+ sont libérés. Un getbuf() => Un putbuf.
+
+2) Tous dans les iobufs : super, group desc et bitmaps
+
+3) Il n'ya plus aucune reférence au contenu d'une inode dans la
+ structure Xfile.
+
+4) Choix de la parition en passant /dev/hd?disk:n lors du mount
+
+
+Version 0.11 :
+
+1) -v affiche les blocks manipulés en lecture et écriture [18/10/96]
+
+2) bug pour open avec TRUNC sur les liens... fixed [19/10/96]
+
+3) maintenant on jette si la taille des blocks != 1024 dans ext2fs() [21/10/96]
+ (c'est quand même mieux pour le moment ...)
+
+
+Version 0.20 :
+
+1) les blocks de 1024, 2048, 4096 octets sont supportés. [22/10/96]
+
+2) le bug sur le qid.vers est détecté mais non corrigé... \ No newline at end of file
diff --git a/sys/src/cmd/ext2srv/xfile.c b/sys/src/cmd/ext2srv/xfile.c
new file mode 100755
index 000000000..2950398a1
--- /dev/null
+++ b/sys/src/cmd/ext2srv/xfile.c
@@ -0,0 +1,161 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "dat.h"
+#include "fns.h"
+
+
+static Xfs *xhead;
+static Xfile *freelist;
+static Lock xlock, freelock;
+
+int client;
+
+Xfs *
+getxfs(char *name)
+{
+ int fd;
+ Dir *dir;
+ Xfs *xf, *fxf;
+
+ if(name==0 || name[0]==0)
+ name = deffile;
+ if(name == 0){
+ errno = Enofilsys;
+ return 0;
+ }
+ fd = open(name, rdonly ? OREAD : ORDWR);
+ if(fd < 0){
+ errno = Enonexist;
+ return 0;
+ }
+ if((dir = dirfstat(fd)) == 0){
+ errno = Eio;
+ close(fd);
+ return 0;
+ }
+ lock(&xlock);
+ for(fxf=0, xf=xhead; xf; xf=xf->next){
+ if(xf->ref == 0){
+ if(fxf == 0)
+ fxf = xf;
+ continue;
+ }
+ if(xf->qid.path != dir->qid.path || xf->qid.vers != dir->qid.vers)
+ continue;
+ if(strcmp(xf->name, name) != 0 || xf->dev < 0)
+ continue;
+ chat("incref \"%s\", dev=%d...", xf->name, xf->dev);
+ ++xf->ref;
+ unlock(&xlock);
+ close(fd);
+ free(dir);
+ return xf;
+ }
+ if(fxf==0){
+ fxf = malloc(sizeof(Xfs));
+ if(fxf==0){
+ unlock(&xlock);
+ close(fd);
+ free(dir);
+ errno = Enomem;
+ return 0;
+ }
+ fxf->next = xhead;
+ xhead = fxf;
+ }
+ chat("alloc \"%s\", dev=%d...", name, fd);
+ fxf->name = strdup(name);
+ fxf->ref = 1;
+ fxf->qid = dir->qid;
+ fxf->dev = fd;
+ fxf->fmt = 0;
+ fxf->ptr = 0;
+ free(dir);
+ if( ext2fs(fxf)<0 ){
+ xhead = fxf->next;
+ free(fxf);
+ unlock(&xlock);
+ return 0;
+ }
+ unlock(&xlock);
+ return fxf;
+}
+
+void
+refxfs(Xfs *xf, int delta)
+{
+ lock(&xlock);
+ xf->ref += delta;
+ if(xf->ref == 0){
+ /*mchat("free \"%s\", dev=%d...", xf->name, xf->dev);
+ dumpbuf();*/
+ CleanSuper(xf);
+ syncbuf();
+ free(xf->name);
+ purgebuf(xf);
+ if(xf->dev >= 0){
+ close(xf->dev);
+ xf->dev = -1;
+ }
+ }
+ unlock(&xlock);
+}
+
+Xfile *
+xfile(Fid *fid, int flag)
+{
+ Xfile *f;
+
+ f = (Xfile*)fid->aux;
+ switch(flag){
+ default:
+ panic("xfile");
+ case Asis:
+ return (f && f->xf && f->xf->dev < 0) ? 0 : f;
+ case Clean:
+ if (f) chat("Clean and fid->aux already exists\n");
+ break;
+ case Clunk:
+ if(f){
+ clean(f);
+ lock(&freelock);
+ f->next = freelist;
+ freelist = f;
+ unlock(&freelock);
+ fid->aux = 0;
+ }
+ return 0;
+ }
+ if(f)
+ return clean(f);
+ lock(&freelock);
+ if(f = freelist){ /* assign = */
+ freelist = f->next;
+ unlock(&freelock);
+ } else {
+ unlock(&freelock);
+ f = malloc(sizeof(Xfile));
+ }
+ fid->aux = f;
+ f->fid = fid->fid;
+ f->client = client;
+ f->xf = 0;
+ f->ptr = 0;
+ f->root = 0;
+ return f;
+}
+Xfile *
+clean(Xfile *f)
+{
+ if(f->xf && f->root){
+ refxfs(f->xf, -1);
+ f->xf = 0;
+ }
+ f->xf = 0;
+ f->root = 0;
+ f->dirindex = 0;
+ return f;
+}
diff --git a/sys/src/cmd/ext2srv/xfssrv.c b/sys/src/cmd/ext2srv/xfssrv.c
new file mode 100755
index 000000000..c8bafd9ce
--- /dev/null
+++ b/sys/src/cmd/ext2srv/xfssrv.c
@@ -0,0 +1,95 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "dat.h"
+#include "fns.h"
+
+#include "errstr.h"
+
+int errno;
+int rdonly;
+char *srvfile;
+char *deffile;
+
+extern void iobuf_init(void);
+extern Srv ext2srv;
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-v] [-s] [-r] [-p passwd] [-g group] [-f devicefile] [srvname]\n", argv0);
+ exits("usage");
+}
+
+/*void handler(void *v, char *sig)
+{
+ USED(v,sig);
+ syncbuf();
+ noted(NDFLT);
+}*/
+
+void
+main(int argc, char **argv)
+{
+ int stdio;
+
+ stdio = 0;
+ ARGBEGIN{
+ case 'D':
+ ++chatty9p;
+ break;
+ case 'v':
+ ++chatty;
+ break;
+ case 'f':
+ deffile = ARGF();
+ break;
+ case 'g':
+ gidfile(ARGF());
+ break;
+ case 'p':
+ uidfile(ARGF());
+ break;
+ case 's':
+ stdio = 1;
+ break;
+ case 'r':
+ rdonly = 1;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc == 0)
+ srvfile = "ext2";
+ else if(argc == 1)
+ srvfile = argv[0];
+ else
+ usage();
+
+ iobuf_init();
+ /*notify(handler);*/
+
+ if(!chatty){
+ close(2);
+ open("#c/cons", OWRITE);
+ }
+ if(stdio){
+ srv(&ext2srv);
+ }else{
+ chat("%s %d: serving %s\n", argv0, getpid(), srvfile);
+ postmountsrv(&ext2srv, srvfile, 0, 0);
+ }
+ exits(0);
+}
+
+char *
+xerrstr(int e)
+{
+ if (e < 0 || e >= sizeof errmsg/sizeof errmsg[0])
+ return "no such error";
+ else
+ return errmsg[e];
+}