olefs.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <auth.h>
  5. #include <fcall.h>
  6. #include <thread.h>
  7. #include <9p.h>
  8. /* little endian */
  9. #define SHORT(p) (((uchar*)(p))[0] | (((uchar*)(p))[1] << 8))
  10. #define LONG(p) ((ulong)SHORT(p) |(((ulong)SHORT((p)+2)) << 16))
  11. typedef struct Ofile Ofile;
  12. typedef struct Odir Odir;
  13. enum {
  14. /* special block map entries */
  15. Bspecial = 0xFFFFFFFD,
  16. Bendchain = 0xFFFFFFFE,
  17. Bunused = 0xFFFFFFFF,
  18. Blocksize = 0x200,
  19. Odirsize = 0x80,
  20. /* Odir types */
  21. Tstorage = 1,
  22. Tstream = 2,
  23. Troot = 5,
  24. };
  25. /*
  26. * the file consists of chains of blocks of size 0x200.
  27. * to find what block follows block n, you look at
  28. * blockmap[n]. that block follows it unless it is Bspecial
  29. * or Bendchain.
  30. *
  31. * it's like the MS-DOS file system allocation tables.
  32. */
  33. struct Ofile {
  34. Biobuf *b;
  35. ulong nblock;
  36. ulong *blockmap;
  37. ulong rootblock;
  38. ulong smapblock;
  39. ulong *smallmap;
  40. };
  41. /* Odir headers are found in directory listings in the Olefile */
  42. /* prev and next form a binary tree of directory entries */
  43. struct Odir {
  44. Ofile *f;
  45. Rune name[32+1];
  46. uchar type;
  47. uchar isroot;
  48. ulong left;
  49. ulong right;
  50. ulong dir;
  51. ulong start;
  52. ulong size;
  53. };
  54. void*
  55. emalloc(ulong sz)
  56. {
  57. void *v;
  58. v = malloc(sz);
  59. assert(v != nil);
  60. return v;
  61. }
  62. int
  63. convM2OD(Odir *f, void *buf, int nbuf)
  64. {
  65. int i;
  66. char *p;
  67. int len;
  68. if(nbuf < Odirsize)
  69. return -1;
  70. /*
  71. * the short at 0x40 is the length of the name.
  72. * when zero, it means there is no Odir here.
  73. */
  74. p = buf;
  75. len = SHORT(p+0x40);
  76. if(len == 0)
  77. return 0;
  78. if(len > 32) /* shouldn't happen */
  79. len = 32;
  80. for(i=0; i<len; i++)
  81. f->name[i] = SHORT(p+i*2);
  82. f->name[len] = 0;
  83. f->type = p[0x42];
  84. f->left = LONG(p+0x44);
  85. f->right = LONG(p+0x48);
  86. f->dir = LONG(p+0x4C);
  87. f->start = LONG(p+0x74);
  88. f->size = LONG(p+0x78);
  89. /* BUG: grab time in ms format from here */
  90. return 1;
  91. }
  92. int
  93. oreadblock(Ofile *f, int block, ulong off, char *buf, int nbuf)
  94. {
  95. int n;
  96. if(block < 0 || block >= f->nblock) {
  97. werrstr("attempt to read %x/%lux\n", block, f->nblock);
  98. return -1;
  99. }
  100. if(off >= Blocksize){
  101. print("offset too far into block\n");
  102. return 0;
  103. }
  104. if(off+nbuf > Blocksize)
  105. nbuf = Blocksize-off;
  106. /* blocks start numbering at -1 [sic] */
  107. off += (block+1)*Blocksize;
  108. if(Bseek(f->b, off, 0) != off){
  109. print("seek failed\n");
  110. return -1;
  111. }
  112. n = Bread(f->b, buf, nbuf);
  113. if(n < 0)
  114. print("Bread failed: %r");
  115. return n;
  116. }
  117. int
  118. chainlen(Ofile *f, ulong start)
  119. {
  120. int i;
  121. for(i=0; start < 0xFFFF0000; i++)
  122. start = f->blockmap[start];
  123. return i;
  124. }
  125. /*
  126. * read nbuf bytes starting at offset off from the
  127. * chain whose first block is block. the chain is linked
  128. * together via the blockmap as described above,
  129. * like the MS-DOS file allocation tables.
  130. */
  131. int
  132. oreadchain(Ofile *f, ulong block, int off, char *buf, int nbuf)
  133. {
  134. int i;
  135. int offblock;
  136. offblock = off/Blocksize;
  137. for(i=0; i<offblock && block < 0xFFFF0000; i++)
  138. block = f->blockmap[block];
  139. return oreadblock(f, block, off%Blocksize, buf, nbuf);
  140. }
  141. int
  142. oreadfile(Odir *d, int off, char *buf, int nbuf)
  143. {
  144. /*
  145. * if d->size < 0x1000 then d->start refers
  146. * to a small depot block, else a big one.
  147. * if this is the root entry, it's a big one
  148. * no matter what.
  149. */
  150. if(off >= d->size)
  151. return 0;
  152. if(off+nbuf > d->size)
  153. nbuf = d->size-off;
  154. if(d->size >= 0x1000
  155. || memcmp(d->name, L"Root Entry", 11*sizeof(Rune)) == 0)
  156. return oreadchain(d->f, d->start, off, buf, nbuf);
  157. else { /* small block */
  158. off += d->start*64;
  159. return oreadchain(d->f, d->f->smapblock, off, buf, nbuf);
  160. }
  161. }
  162. int
  163. oreaddir(Ofile *f, int entry, Odir *d)
  164. {
  165. char buf[Odirsize];
  166. if(oreadchain(f, f->rootblock, entry*Odirsize, buf, Odirsize) != Odirsize)
  167. return -1;
  168. d->f = f;
  169. return convM2OD(d, buf, Odirsize);
  170. }
  171. void
  172. dumpdir(Ofile *f, ulong dnum)
  173. {
  174. Odir d;
  175. if(oreaddir(f, dnum, &d) != 1) {
  176. fprint(2, "dumpdir %lux failed\n", dnum);
  177. return;
  178. }
  179. 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);
  180. if(d.left != (ulong)-1)
  181. dumpdir(f, d.left);
  182. if(d.right != (ulong)-1)
  183. dumpdir(f, d.right);
  184. if(d.dir != (ulong)-1)
  185. dumpdir(f, d.dir);
  186. }
  187. Ofile*
  188. oleopen(char *fn)
  189. {
  190. int i, j, k, block;
  191. int ndepot;
  192. ulong u;
  193. Odir rootdir;
  194. ulong extrablock;
  195. uchar buf[Blocksize];
  196. Ofile *f;
  197. Biobuf *b;
  198. static char magic[] = {
  199. 0xD0, 0xCF, 0x11, 0xE0,
  200. 0xA1, 0xB1, 0x1A, 0xE1
  201. };
  202. b = Bopen(fn, OREAD);
  203. if(b == nil)
  204. return nil;
  205. /* the first bytes are magic */
  206. if(Bread(b, buf, sizeof magic) != sizeof magic
  207. || memcmp(buf, magic, sizeof magic) != 0) {
  208. Bterm(b);
  209. werrstr("bad magic: not OLE file");
  210. return nil;
  211. }
  212. f = emalloc(sizeof *f);
  213. f->b = b;
  214. /*
  215. * the header contains a list of depots, which are
  216. * block maps. we assimilate them into one large map,
  217. * kept in main memory.
  218. */
  219. Bseek(b, 0, 0);
  220. if(Bread(b, buf, Blocksize) != Blocksize) {
  221. Bterm(b);
  222. free(f);
  223. print("short read\n");
  224. return nil;
  225. }
  226. ndepot = LONG(buf+0x2C);
  227. f->nblock = ndepot*(Blocksize/4);
  228. // fprint(2, "ndepot = %d f->nblock = %lud\n", ndepot, f->nblock);
  229. f->rootblock = LONG(buf+0x30);
  230. f->smapblock = LONG(buf+0x3C);
  231. f->blockmap = emalloc(sizeof(f->blockmap[0])*f->nblock);
  232. extrablock = LONG(buf+0x44);
  233. u = 0;
  234. /* the big block map fills to the end of the first 512-byte block */
  235. for(i=0; i<ndepot && i<(0x200-0x4C)/4; i++) {
  236. if(Bseek(b, 0x4C+4*i, 0) != 0x4C+4*i
  237. || Bread(b, buf, 4) != 4) {
  238. print("bseek %d fail\n", 0x4C+4*i);
  239. goto Die;
  240. }
  241. block = LONG(buf);
  242. if((ulong)block == Bendchain) {
  243. ndepot = i;
  244. f->nblock = ndepot*(Blocksize/4);
  245. break;
  246. }
  247. if(Bseek(b, (block+1)*Blocksize, 0) != (block+1)*Blocksize) {
  248. print("Xbseek %d fail\n", (block+1)*Blocksize);
  249. goto Die;
  250. }
  251. for(j=0; j<Blocksize/4; j++) {
  252. if(Bread(b, buf, 4) != 4) {
  253. print("Bread fail seek block %x, %d i %d ndepot %d\n", block, (block+1)*Blocksize, i, ndepot);
  254. goto Die;
  255. }
  256. f->blockmap[u++] = LONG(buf);
  257. }
  258. }
  259. /*
  260. * if the first block can't hold it, it continues in the block at LONG(hdr+0x44).
  261. * if that in turn is not big enough, there's a next block number at the end of
  262. * each block.
  263. */
  264. while(i < ndepot) {
  265. for(k=0; k<(0x200-4)/4 && i<ndepot; i++, k++) {
  266. if(Bseek(b, 0x200+extrablock*Blocksize+4*i, 0) != 0x200+extrablock*0x200+4*i
  267. || Bread(b, buf, 4) != 4) {
  268. print("bseek %d fail\n", 0x4C+4*i);
  269. goto Die;
  270. }
  271. block = LONG(buf);
  272. if((ulong)block == Bendchain) {
  273. ndepot = i;
  274. f->nblock = ndepot*(Blocksize/4);
  275. goto Break2;
  276. }
  277. if(Bseek(b, (block+1)*Blocksize, 0) != (block+1)*Blocksize) {
  278. print("Xbseek %d fail\n", (block+1)*Blocksize);
  279. goto Die;
  280. }
  281. for(j=0; j<Blocksize/4; j++) {
  282. if(Bread(b, buf, 4) != 4) {
  283. print("Bread fail seek block %x, %d i %d ndepot %d\n", block, (block+1)*Blocksize, i, ndepot);
  284. goto Die;
  285. }
  286. f->blockmap[u++] = LONG(buf);
  287. }
  288. }
  289. if(Bseek(b, 0x200+extrablock*Blocksize+Blocksize-4, 0) != 0x200+extrablock*Blocksize+Blocksize-4
  290. || Bread(b, buf, 4) != 4) {
  291. print("bseek %d fail\n", 0x4C+4*i);
  292. goto Die;
  293. }
  294. extrablock = LONG(buf);
  295. }
  296. Break2:;
  297. if(oreaddir(f, 0, &rootdir) <= 0){
  298. print("oreaddir could not read root\n");
  299. goto Die;
  300. }
  301. f->smapblock = rootdir.start;
  302. return f;
  303. Die:
  304. Bterm(b);
  305. free(f->blockmap);
  306. free(f);
  307. return nil;
  308. }
  309. void
  310. oleread(Req *r)
  311. {
  312. Odir *d;
  313. char *p;
  314. int e, n;
  315. long c;
  316. vlong o;
  317. o = r->ifcall.offset;
  318. d = r->fid->file->aux;
  319. if(d == nil) {
  320. respond(r, "cannot happen");
  321. return;
  322. }
  323. c = r->ifcall.count;
  324. if(o >= d->size) {
  325. r->ofcall.count = 0;
  326. respond(r, nil);
  327. return;
  328. }
  329. if(o+c > d->size)
  330. c = d->size-o;
  331. /*
  332. * oreadfile returns so little data, it will
  333. * help to read as much as we can.
  334. */
  335. e = c+o;
  336. n = 0;
  337. for(p=r->ofcall.data; o<e; o+=n, p+=n) {
  338. n = oreadfile(d, o, p, e-o);
  339. if(n <= 0)
  340. break;
  341. }
  342. if(n == -1 && o == r->ifcall.offset)
  343. respond(r, "error reading word file");
  344. else {
  345. r->ofcall.count = o - r->ifcall.offset;
  346. respond(r, nil);
  347. }
  348. }
  349. Odir*
  350. copydir(Odir *d)
  351. {
  352. Odir *e;
  353. e = emalloc(sizeof(*d));
  354. *e = *d;
  355. return e;
  356. }
  357. void
  358. filldir(File *t, Ofile *f, int dnum, int nrecur)
  359. {
  360. Odir d;
  361. int i;
  362. Rune rbuf[40];
  363. char buf[UTFmax*nelem(rbuf)];
  364. File *nt;
  365. if(dnum == 0xFFFFFFFF || oreaddir(f, dnum, &d) != 1)
  366. return;
  367. /*
  368. * i hope there are no broken files with
  369. * circular trees. i hate infinite loops.
  370. */
  371. if(nrecur > 100)
  372. sysfatal("tree too large in office file: probably circular");
  373. filldir(t, f, d.left, nrecur+1);
  374. /* add current tree entry */
  375. runestrecpy(rbuf, rbuf+sizeof rbuf, d.name);
  376. for(i=0; rbuf[i]; i++)
  377. if(rbuf[i] == L' ')
  378. rbuf[i] = L'␣';
  379. else if(rbuf[i] <= 0x20 || rbuf[i] == L'/'
  380. || (0x80 <= rbuf[i] && rbuf[i] <= 0x9F))
  381. rbuf[i] = ':';
  382. snprint(buf, sizeof buf, "%S", rbuf);
  383. if(d.dir == 0xFFFFFFFF) {
  384. /* make file */
  385. nt = createfile(t, buf, nil, 0444, nil);
  386. if(nt == nil)
  387. sysfatal("nt nil: create %s: %r", buf);
  388. nt->aux = copydir(&d);
  389. nt->length = d.size;
  390. } else /* make directory */
  391. nt = createfile(t, buf, nil, DMDIR|0777, nil);
  392. filldir(t, f, d.right, nrecur+1);
  393. if(d.dir != 0xFFFFFFFF)
  394. filldir(nt, f, d.dir, nrecur+1);
  395. closefile(nt);
  396. }
  397. Srv olesrv = {
  398. .read= oleread,
  399. };
  400. void
  401. main(int argc, char **argv)
  402. {
  403. char *mtpt;
  404. Ofile *f;
  405. Odir d;
  406. mtpt = "/mnt/doc";
  407. ARGBEGIN{
  408. case 'm':
  409. mtpt = ARGF();
  410. break;
  411. default:
  412. goto Usage;
  413. }ARGEND
  414. if(argc != 1) {
  415. Usage:
  416. fprint(2, "usage: olefs file\n");
  417. exits("usage");
  418. }
  419. f = oleopen(argv[0]);
  420. if(f == nil) {
  421. print("error opening %s: %r\n", argv[0]);
  422. exits("open");
  423. }
  424. // dumpdir(f, 0);
  425. if(oreaddir(f, 0, &d) != 1) {
  426. fprint(2, "oreaddir error: %r\n");
  427. exits("oreaddir");
  428. }
  429. olesrv.tree = alloctree(nil, nil, DMDIR|0777, nil);
  430. filldir(olesrv.tree->root, f, d.dir, 0);
  431. postmountsrv(&olesrv, nil, mtpt, MREPL);
  432. exits(0);
  433. }