diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ext2srv |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ext2srv')
-rwxr-xr-x | sys/src/cmd/ext2srv/chat.c | 53 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/dat.h | 222 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/errstr.h | 17 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/ext2fs.c | 348 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/ext2srv.man | 110 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/ext2subs.c | 1870 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/fns.h | 70 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/iobuf.c | 174 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/mkfile | 18 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/readme | 53 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/version | 36 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/xfile.c | 161 | ||||
-rwxr-xr-x | sys/src/cmd/ext2srv/xfssrv.c | 95 |
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]; +} |