⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 olefs.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#include <u.h>#include <libc.h>#include <bio.h>#include <auth.h>#include <fcall.h>#include <thread.h>#include <9p.h>/* little endian */#define SHORT(p)	(((uchar*)(p))[0] | (((uchar*)(p))[1] << 8))#define LONG(p)	((ulong)SHORT(p) |(((ulong)SHORT((p)+2)) << 16))typedef struct Ofile	Ofile;typedef struct Odir	Odir;enum {	/* special block map entries */	Bspecial = 0xFFFFFFFD,	Bendchain = 0xFFFFFFFE,	Bunused = 0xFFFFFFFF,	Blocksize = 0x200,	Odirsize = 0x80,	/* Odir types */	Tstorage = 1,	Tstream = 2,	Troot = 5,};/* * the file consists of chains of blocks of size 0x200. * to find what block follows block n, you look at  * blockmap[n].  that block follows it unless it is Bspecial * or Bendchain. *  * it's like the MS-DOS file system allocation tables. */struct Ofile {	Biobuf *b;	ulong nblock;	ulong *blockmap;	ulong rootblock;	ulong smapblock;	ulong *smallmap;};/* Odir headers are found in directory listings in the Olefile *//* prev and next form a binary tree of directory entries */struct Odir {	Ofile *f;	Rune name[32+1];	uchar type;	uchar isroot;	ulong left;	ulong right;	ulong dir;	ulong start;	ulong size;};void*emalloc(ulong sz){	void *v;	v = malloc(sz);	assert(v != nil);	return v;}intconvM2OD(Odir *f, void *buf, int nbuf){	int i;	char *p;	int len;	if(nbuf < Odirsize)		return -1;	/*	 * the short at 0x40 is the length of the name.	 * when zero, it means there is no Odir here.	 */	p = buf;	len = SHORT(p+0x40);	if(len == 0)		return 0;	if(len > 32)	/* shouldn't happen */		len = 32;	for(i=0; i<len; i++)		f->name[i] = SHORT(p+i*2);	f->name[len] = 0;	f->type = p[0x42];	f->left = LONG(p+0x44);	f->right = LONG(p+0x48);	f->dir = LONG(p+0x4C);	f->start = LONG(p+0x74);	f->size = LONG(p+0x78);	/* BUG: grab time in ms format from here */	return 1;}intoreadblock(Ofile *f, int block, ulong off, char *buf, int nbuf){	int n;	if(block < 0 || block >= f->nblock) {		werrstr("attempt to read %x/%lux\n", block, f->nblock);		return -1;	}	if(off >= Blocksize){		print("offset too far into block\n");		return 0;	}	if(off+nbuf > Blocksize)		nbuf = Blocksize-off;	/* blocks start numbering at -1 [sic] */	off += (block+1)*Blocksize;	if(Bseek(f->b, off, 0) != off){		print("seek failed\n");		return -1;	}	n = Bread(f->b, buf, nbuf);	if(n < 0)		print("Bread failed: %r");	return n;}intchainlen(Ofile *f, ulong start){	int i;	for(i=0; start < 0xFFFF0000; i++)		start = f->blockmap[start];	return i;}/* * read nbuf bytes starting at offset off from the  * chain whose first block is block.  the chain is linked * together via the blockmap as described above, * like the MS-DOS file allocation tables. */intoreadchain(Ofile *f, ulong block, int off, char *buf, int nbuf){	int i;	int offblock;	offblock = off/Blocksize;	for(i=0; i<offblock && block < 0xFFFF0000; i++)		block = f->blockmap[block];	return oreadblock(f, block, off%Blocksize, buf, nbuf);}int oreadfile(Odir *d, int off, char *buf, int nbuf){	/*	 * if d->size < 0x1000 then d->start refers	 * to a small depot block, else a big one.	 * if this is the root entry, it's a big one	 * no matter what.	 */	if(off >= d->size)		return 0;	if(off+nbuf > d->size)		nbuf = d->size-off;	if(d->size >= 0x1000 	|| memcmp(d->name, L"Root Entry", 11*sizeof(Rune)) == 0)		return oreadchain(d->f, d->start, off, buf, nbuf);	else {	/* small block */		off += d->start*64;		return oreadchain(d->f, d->f->smapblock, off, buf, nbuf);	}}intoreaddir(Ofile *f, int entry, Odir *d){	char buf[Odirsize];	if(oreadchain(f, f->rootblock, entry*Odirsize, buf, Odirsize) != Odirsize)		return -1;	d->f = f;	return convM2OD(d, buf, Odirsize);}voiddumpdir(Ofile *f, ulong dnum){	Odir d;	if(oreaddir(f, dnum, &d) != 1) {		fprint(2, "dumpdir %lux failed\n", dnum);		return;	}	fprint(2, "%.8lux type %d size %lud l %.8lux r %.8lux d %.8lux (%S)\n", dnum, d.type, d.size, d.left, d.right, d.dir, d.name);	if(d.left != (ulong)-1) 		dumpdir(f, d.left);	if(d.right != (ulong)-1)		dumpdir(f, d.right);	if(d.dir != (ulong)-1)		dumpdir(f, d.dir);}Ofile*oleopen(char *fn){	int i, j, k, block;	int ndepot;	ulong u;	Odir rootdir;	ulong extrablock;	uchar buf[Blocksize];	Ofile *f;	Biobuf *b;	static char magic[] = {		0xD0, 0xCF, 0x11, 0xE0,		0xA1, 0xB1, 0x1A, 0xE1	};	b = Bopen(fn, OREAD);	if(b == nil)		return nil;	/* the first bytes are magic */	if(Bread(b, buf, sizeof magic) != sizeof magic	|| memcmp(buf, magic, sizeof magic) != 0) {		Bterm(b);		werrstr("bad magic: not OLE file");		return nil;	}	f = emalloc(sizeof *f);	f->b = b;	/*	 * the header contains a list of depots, which are	 * block maps.  we assimilate them into one large map,	 * kept in main memory.	 */	Bseek(b, 0, 0);	if(Bread(b, buf, Blocksize) != Blocksize) {		Bterm(b);		free(f);		print("short read\n");		return nil;	}	ndepot = LONG(buf+0x2C);	f->nblock = ndepot*(Blocksize/4);//	fprint(2, "ndepot = %d f->nblock = %lud\n", ndepot, f->nblock);	f->rootblock = LONG(buf+0x30);	f->smapblock = LONG(buf+0x3C);	f->blockmap = emalloc(sizeof(f->blockmap[0])*f->nblock);	extrablock = LONG(buf+0x44);	u = 0;	/* the big block map fills to the end of the first 512-byte block */	for(i=0; i<ndepot && i<(0x200-0x4C)/4; i++) {		if(Bseek(b, 0x4C+4*i, 0) != 0x4C+4*i		|| Bread(b, buf, 4) != 4) {			print("bseek %d fail\n", 0x4C+4*i);			goto Die;		}		block = LONG(buf);		if((ulong)block == Bendchain) {			ndepot = i;			f->nblock = ndepot*(Blocksize/4);			break;		}		if(Bseek(b, (block+1)*Blocksize, 0) != (block+1)*Blocksize) {			print("Xbseek %d fail\n", (block+1)*Blocksize);			goto Die;		}		for(j=0; j<Blocksize/4; j++) {			if(Bread(b, buf, 4) != 4) {				print("Bread fail seek block %x, %d i %d ndepot %d\n", block, (block+1)*Blocksize, i, ndepot);				goto Die;			}			f->blockmap[u++] = LONG(buf);		}	}	/*	 * if the first block can't hold it, it continues in the block at LONG(hdr+0x44).	 * if that in turn is not big enough, there's a next block number at the end of 	 * each block.	 */	while(i < ndepot) {		for(k=0; k<(0x200-4)/4 && i<ndepot; i++, k++) {			if(Bseek(b, 0x200+extrablock*Blocksize+4*i, 0) != 0x200+extrablock*0x200+4*i			|| Bread(b, buf, 4) != 4) {				print("bseek %d fail\n", 0x4C+4*i);				goto Die;			}			block = LONG(buf);			if((ulong)block == Bendchain) {				ndepot = i;				f->nblock = ndepot*(Blocksize/4);				goto Break2;			}			if(Bseek(b, (block+1)*Blocksize, 0) != (block+1)*Blocksize) {				print("Xbseek %d fail\n", (block+1)*Blocksize);				goto Die;			}			for(j=0; j<Blocksize/4; j++) {				if(Bread(b, buf, 4) != 4) {					print("Bread fail seek block %x, %d i %d ndepot %d\n", block, (block+1)*Blocksize, i, ndepot);					goto Die;				}				f->blockmap[u++] = LONG(buf);			}		}		if(Bseek(b, 0x200+extrablock*Blocksize+Blocksize-4, 0) != 0x200+extrablock*Blocksize+Blocksize-4		|| Bread(b, buf, 4) != 4) {			print("bseek %d fail\n", 0x4C+4*i);			goto Die;		}		extrablock = LONG(buf);	}Break2:;	if(oreaddir(f, 0, &rootdir) <= 0){		print("oreaddir could not read root\n");		goto Die;	}	f->smapblock = rootdir.start;	return f;Die:	Bterm(b);	free(f->blockmap);	free(f);	return nil;}voidoleread(Req *r){	Odir *d;	char *p;	int e, n;	long c;	vlong o;	o = r->ifcall.offset;	d = r->fid->file->aux;	if(d == nil) {		respond(r, "cannot happen");		return;	}	c = r->ifcall.count;	if(o >= d->size) {		r->ofcall.count = 0;		respond(r, nil);		return;	}	if(o+c > d->size)		c = d->size-o;	/*	 * oreadfile returns so little data, it will	 * help to read as much as we can.	 */	e = c+o;	n = 0;	for(p=r->ofcall.data; o<e; o+=n, p+=n) {		n = oreadfile(d, o, p, e-o);		if(n <= 0)			break;	}	if(n == -1 && o == r->ifcall.offset)		respond(r, "error reading word file");	else {		r->ofcall.count = o - r->ifcall.offset;		respond(r, nil);	}}Odir*copydir(Odir *d){	Odir *e;	e = emalloc(sizeof(*d));	*e = *d;	return e;}		voidfilldir(File *t, Ofile *f, int dnum, int nrecur){	Odir d;	int i;	Rune rbuf[40];	char buf[UTFmax*nelem(rbuf)];	File *nt;	if(dnum == 0xFFFFFFFF || oreaddir(f, dnum, &d) != 1)		return;	/*	 * i hope there are no broken files with	 * circular trees.  i hate infinite loops.	 */	if(nrecur > 100)		sysfatal("tree too large in office file: probably circular");	filldir(t, f, d.left, nrecur+1);	/* add current tree entry */	runestrecpy(rbuf, rbuf+sizeof rbuf, d.name);	for(i=0; rbuf[i]; i++)		if(rbuf[i] == L' ')			rbuf[i] = L'␣';		else if(rbuf[i] <= 0x20 || rbuf[i] == L'/' 			|| (0x80 <= rbuf[i] && rbuf[i] <= 0x9F))				rbuf[i] = ':';		snprint(buf, sizeof buf, "%S", rbuf);	if(d.dir == 0xFFFFFFFF) {		/* make file */		nt = createfile(t, buf, nil, 0444, nil);		if(nt == nil)			sysfatal("nt nil: create %s: %r", buf);		nt->aux = copydir(&d);		nt->length = d.size;	} else /* make directory */		nt = createfile(t, buf, nil, DMDIR|0777, nil);	filldir(t, f, d.right, nrecur+1);	if(d.dir != 0xFFFFFFFF)		filldir(nt, f, d.dir, nrecur+1);	closefile(nt);}Srv olesrv = {	.read=	oleread,};voidmain(int argc, char **argv){	char *mtpt;	Ofile *f;	Odir d;	mtpt = "/mnt/doc";	ARGBEGIN{	case 'm':		mtpt = ARGF();		break;		default:		goto Usage;	}ARGEND	if(argc != 1) {	Usage:		fprint(2, "usage: olefs file\n");		exits("usage");	}	f = oleopen(argv[0]);	if(f == nil) {		print("error opening %s: %r\n", argv[0]);		exits("open");	}//	dumpdir(f, 0);	if(oreaddir(f, 0, &d) != 1) {		fprint(2, "oreaddir error: %r\n");		exits("oreaddir");	}	olesrv.tree = alloctree(nil, nil, DMDIR|0777, nil);	filldir(olesrv.tree->root, f, d.dir, 0);	postmountsrv(&olesrv, nil, mtpt, MREPL);	exits(0);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -