10 #define SHORT(p) (((uchar*)(p))[0] | (((uchar*)(p))[1] << 8))
11 #define LONG(p) ((ulong)SHORT(p) |(((ulong)SHORT((p)+2)) << 16))
13 typedef struct Ofile Ofile;
14 typedef struct Odir Odir;
17 /* special block map entries */
18 Bspecial = 0xFFFFFFFD,
19 Bendchain = 0xFFFFFFFE,
33 * the file consists of chains of blocks of size 0x200.
34 * to find what block follows block n, you look at
35 * blockmap[n]. that block follows it unless it is Bspecial
38 * it's like the MS-DOS file system allocation tables.
49 /* Odir headers are found in directory listings in the Olefile */
50 /* prev and next form a binary tree of directory entries */
74 convM2OD(Odir *f, void *buf, int nbuf)
84 * the short at 0x40 is the length of the name.
85 * when zero, it means there is no Odir here.
92 if(len > 32) /* shouldn't happen */
96 f->name[i] = SHORT(p+i*2);
100 f->left = LONG(p+0x44);
101 f->right = LONG(p+0x48);
102 f->dir = LONG(p+0x4C);
103 f->start = LONG(p+0x74);
104 f->size = LONG(p+0x78);
106 /* BUG: grab time in ms format from here */
112 oreadblock(Ofile *f, int block, ulong off, char *buf, int nbuf)
116 if(block < 0 || block >= f->nblock) {
117 werrstr("attempt to read %x/%lux\n", block, f->nblock);
121 if(off >= Blocksize){
122 print("offset too far into block\n");
126 if(off+nbuf > Blocksize)
127 nbuf = Blocksize-off;
129 /* blocks start numbering at -1 [sic] */
130 off += (block+1)*Blocksize;
132 if(Bseek(f->b, off, 0) != off){
133 print("seek failed\n");
137 n = Bread(f->b, buf, nbuf);
139 print("Bread failed: %r");
144 chainlen(Ofile *f, ulong start)
147 for(i=0; start < 0xFFFF0000; i++)
148 start = f->blockmap[start];
154 * read nbuf bytes starting at offset off from the
155 * chain whose first block is block. the chain is linked
156 * together via the blockmap as described above,
157 * like the MS-DOS file allocation tables.
160 oreadchain(Ofile *f, ulong block, int off, char *buf, int nbuf)
165 offblock = off/Blocksize;
166 for(i=0; i<offblock && block < 0xFFFF0000; i++)
167 block = f->blockmap[block];
168 return oreadblock(f, block, off%Blocksize, buf, nbuf);
172 oreadfile(Odir *d, int off, char *buf, int nbuf)
175 * if d->size < 0x1000 then d->start refers
176 * to a small depot block, else a big one.
177 * if this is the root entry, it's a big one
183 if(off+nbuf > d->size)
187 || memcmp(d->name, L"Root Entry", 11*sizeof(Rune)) == 0)
188 return oreadchain(d->f, d->start, off, buf, nbuf);
189 else { /* small block */
191 return oreadchain(d->f, d->f->smapblock, off, buf, nbuf);
196 oreaddir(Ofile *f, int entry, Odir *d)
200 if(oreadchain(f, f->rootblock, entry*Odirsize, buf, Odirsize) != Odirsize)
204 return convM2OD(d, buf, Odirsize);
208 dumpdir(Ofile *f, ulong dnum)
212 if(oreaddir(f, dnum, &d) != 1) {
213 fprint(2, "dumpdir %lux failed\n", dnum);
217 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);
218 if(d.left != (ulong)-1)
220 if(d.right != (ulong)-1)
222 if(d.dir != (ulong)-1)
234 uchar buf[Blocksize];
238 static char magic[] = {
239 0xD0, 0xCF, 0x11, 0xE0,
240 0xA1, 0xB1, 0x1A, 0xE1
243 b = Bopen(fn, OREAD);
247 /* the first bytes are magic */
248 if(Bread(b, buf, sizeof magic) != sizeof magic
249 || memcmp(buf, magic, sizeof magic) != 0) {
251 werrstr("bad magic: not OLE file");
255 f = emalloc(sizeof *f);
259 * the header contains a list of depots, which are
260 * block maps. we assimilate them into one large map,
261 * kept in main memory.
264 if(Bread(b, buf, Blocksize) != Blocksize) {
267 print("short read\n");
271 ndepot = LONG(buf+0x2C);
272 f->nblock = ndepot*(Blocksize/4);
273 // fprint(2, "ndepot = %d f->nblock = %lud\n", ndepot, f->nblock);
274 f->rootblock = LONG(buf+0x30);
275 f->smapblock = LONG(buf+0x3C);
276 f->blockmap = emalloc(sizeof(f->blockmap[0])*f->nblock);
277 extrablock = LONG(buf+0x44);
281 /* the big block map fills to the end of the first 512-byte block */
282 for(i=0; i<ndepot && i<(0x200-0x4C)/4; i++) {
283 if(Bseek(b, 0x4C+4*i, 0) != 0x4C+4*i
284 || Bread(b, buf, 4) != 4) {
285 print("bseek %d fail\n", 0x4C+4*i);
289 if((ulong)block == Bendchain) {
291 f->nblock = ndepot*(Blocksize/4);
295 if(Bseek(b, (block+1)*Blocksize, 0) != (block+1)*Blocksize) {
296 print("Xbseek %d fail\n", (block+1)*Blocksize);
299 for(j=0; j<Blocksize/4; j++) {
300 if(Bread(b, buf, 4) != 4) {
301 print("Bread fail seek block %x, %d i %d ndepot %d\n", block, (block+1)*Blocksize, i, ndepot);
304 f->blockmap[u++] = LONG(buf);
308 * if the first block can't hold it, it continues in the block at LONG(hdr+0x44).
309 * if that in turn is not big enough, there's a next block number at the end of
313 for(k=0; k<(0x200-4)/4 && i<ndepot; i++, k++) {
314 if(Bseek(b, 0x200+extrablock*Blocksize+4*i, 0) != 0x200+extrablock*0x200+4*i
315 || Bread(b, buf, 4) != 4) {
316 print("bseek %d fail\n", 0x4C+4*i);
320 if((ulong)block == Bendchain) {
322 f->nblock = ndepot*(Blocksize/4);
326 if(Bseek(b, (block+1)*Blocksize, 0) != (block+1)*Blocksize) {
327 print("Xbseek %d fail\n", (block+1)*Blocksize);
330 for(j=0; j<Blocksize/4; j++) {
331 if(Bread(b, buf, 4) != 4) {
332 print("Bread fail seek block %x, %d i %d ndepot %d\n", block, (block+1)*Blocksize, i, ndepot);
335 f->blockmap[u++] = LONG(buf);
338 if(Bseek(b, 0x200+extrablock*Blocksize+Blocksize-4, 0) != 0x200+extrablock*Blocksize+Blocksize-4
339 || Bread(b, buf, 4) != 4) {
340 print("bseek %d fail\n", 0x4C+4*i);
343 extrablock = LONG(buf);
347 if(oreaddir(f, 0, &rootdir) <= 0){
348 print("oreaddir could not read root\n");
352 f->smapblock = rootdir.start;
371 o = r->ifcall.offset;
372 d = r->fid->file->aux;
374 respond(r, "cannot happen");
390 * oreadfile returns so little data, it will
391 * help to read as much as we can.
395 for(p=r->ofcall.data; o<e; o+=n, p+=n) {
396 n = oreadfile(d, o, p, e-o);
401 if(n == -1 && o == r->ifcall.offset)
402 respond(r, "error reading word file");
404 r->ofcall.count = o - r->ifcall.offset;
414 e = emalloc(sizeof(*d));
420 filldir(File *t, Ofile *f, int dnum, int nrecur)
425 char buf[UTFmax*nelem(rbuf)];
428 if(dnum == 0xFFFFFFFF || oreaddir(f, dnum, &d) != 1)
432 * i hope there are no broken files with
433 * circular trees. i hate infinite loops.
436 sysfatal("tree too large in office file: probably circular");
438 filldir(t, f, d.left, nrecur+1);
440 /* add current tree entry */
441 runestrecpy(rbuf, rbuf+sizeof rbuf, d.name);
442 for(i=0; rbuf[i]; i++)
445 else if(rbuf[i] <= 0x20 || rbuf[i] == L'/'
446 || (0x80 <= rbuf[i] && rbuf[i] <= 0x9F))
449 snprint(buf, sizeof buf, "%S", rbuf);
451 if(d.dir == 0xFFFFFFFF) {
453 nt = createfile(t, buf, nil, 0444, nil);
455 sysfatal("nt nil: create %s: %r", buf);
456 nt->aux = copydir(&d);
458 } else /* make directory */
459 nt = createfile(t, buf, nil, DMDIR|0777, nil);
461 filldir(t, f, d.right, nrecur+1);
463 if(d.dir != 0xFFFFFFFF)
464 filldir(nt, f, d.dir, nrecur+1);
474 main(int argc, char **argv)
492 fprint(2, "usage: olefs file\n");
496 f = oleopen(argv[0]);
498 print("error opening %s: %r\n", argv[0]);
504 if(oreaddir(f, 0, &d) != 1) {
505 fprint(2, "oreaddir error: %r\n");
509 olesrv.tree = alloctree(nil, nil, DMDIR|0777, nil);
510 filldir(olesrv.tree->root, f, d.dir, 0);
511 postmountsrv(&olesrv, nil, mtpt, MREPL);