olefs.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  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. if(block < 0 || block >= f->nblock) {
  96. werrstr("attempt to read %x/%lux\n", block, f->nblock);
  97. return -1;
  98. }
  99. if(off >= Blocksize)
  100. return 0;
  101. if(off+nbuf > Blocksize)
  102. nbuf = Blocksize-off;
  103. /* blocks start numbering at -1 [sic] */
  104. off += (block+1)*Blocksize;
  105. if(Bseek(f->b, off, 0) != off)
  106. return -1;
  107. return Bread(f->b, buf, nbuf);
  108. }
  109. int
  110. chainlen(Ofile *f, ulong start)
  111. {
  112. int i;
  113. for(i=0; start < 0xFFFF0000; i++)
  114. start = f->blockmap[start];
  115. return i;
  116. }
  117. /*
  118. * read nbuf bytes starting at offset off from the
  119. * chain whose first block is block. the chain is linked
  120. * together via the blockmap as described above,
  121. * like the MS-DOS file allocation tables.
  122. */
  123. int
  124. oreadchain(Ofile *f, ulong block, int off, char *buf, int nbuf)
  125. {
  126. int i;
  127. int offblock;
  128. offblock = off/Blocksize;
  129. for(i=0; i<offblock && block < 0xFFFF0000; i++)
  130. block = f->blockmap[block];
  131. return oreadblock(f, block, off%Blocksize, buf, nbuf);
  132. }
  133. int
  134. oreadfile(Odir *d, int off, char *buf, int nbuf)
  135. {
  136. /*
  137. * if d->size < 0x1000 then d->start refers
  138. * to a small depot block, else a big one.
  139. * if this is the root entry, it's a big one
  140. * no matter what.
  141. */
  142. if(off >= d->size)
  143. return 0;
  144. if(off+nbuf > d->size)
  145. nbuf = d->size-off;
  146. if(d->size >= 0x1000
  147. || memcmp(d->name, L"Root Entry", 11*sizeof(Rune)) == 0)
  148. return oreadchain(d->f, d->start, off, buf, nbuf);
  149. else { /* small block */
  150. off += d->start*64;
  151. return oreadchain(d->f, d->f->smapblock, off, buf, nbuf);
  152. }
  153. }
  154. int
  155. oreaddir(Ofile *f, int entry, Odir *d)
  156. {
  157. char buf[Odirsize];
  158. if(oreadchain(f, f->rootblock, entry*Odirsize, buf, Odirsize) != Odirsize)
  159. return -1;
  160. d->f = f;
  161. return convM2OD(d, buf, Odirsize);
  162. }
  163. void
  164. dumpdir(Ofile *f, ulong dnum)
  165. {
  166. Odir d;
  167. if(oreaddir(f, dnum, &d) != 1) {
  168. fprint(2, "dumpdir %lux failed\n", dnum);
  169. return;
  170. }
  171. 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);
  172. if(d.left != (ulong)-1)
  173. dumpdir(f, d.left);
  174. if(d.right != (ulong)-1)
  175. dumpdir(f, d.right);
  176. if(d.dir != (ulong)-1)
  177. dumpdir(f, d.dir);
  178. }
  179. Ofile*
  180. oleopen(char *fn)
  181. {
  182. int i, j, k, block;
  183. int ndepot;
  184. ulong u;
  185. Odir rootdir;
  186. ulong extrablock;
  187. uchar buf[Blocksize];
  188. Ofile *f;
  189. Biobuf *b;
  190. static char magic[] = {
  191. 0xD0, 0xCF, 0x11, 0xE0,
  192. 0xA1, 0xB1, 0x1A, 0xE1
  193. };
  194. b = Bopen(fn, OREAD);
  195. if(b == nil)
  196. return nil;
  197. /* the first bytes are magic */
  198. if(Bread(b, buf, sizeof magic) != sizeof magic
  199. || memcmp(buf, magic, sizeof magic) != 0) {
  200. werrstr("bad magic: not OLE file");
  201. Bterm(b);
  202. return nil;
  203. }
  204. f = emalloc(sizeof *f);
  205. f->b = b;
  206. /*
  207. * the header contains a list of depots, which are
  208. * block maps. we assimilate them into one large map,
  209. * kept in main memory.
  210. */
  211. Bseek(b, 0, 0);
  212. if(Bread(b, buf, Blocksize) != Blocksize) {
  213. Bterm(b);
  214. free(f);
  215. return nil;
  216. }
  217. ndepot = LONG(buf+0x2C);
  218. f->nblock = ndepot*(Blocksize/4);
  219. // fprint(2, "ndepot = %d f->nblock = %lud\n", ndepot, f->nblock);
  220. f->rootblock = LONG(buf+0x30);
  221. f->smapblock = LONG(buf+0x3C);
  222. f->blockmap = emalloc(sizeof(f->blockmap[0])*f->nblock);
  223. extrablock = LONG(buf+0x44);
  224. u = 0;
  225. /* the big block map fills to the end of the first 512-byte block */
  226. for(i=0; i<ndepot && i<(0x200-0x4C)/4; i++) {
  227. if(Bseek(b, 0x4C+4*i, 0) != 0x4C+4*i
  228. || Bread(b, buf, 4) != 4) {
  229. print("bseek %d fail\n", 0x4C+4*i);
  230. goto Die;
  231. }
  232. block = LONG(buf);
  233. if((ulong)block == Bendchain) {
  234. ndepot = i;
  235. f->nblock = ndepot*(Blocksize/4);
  236. break;
  237. }
  238. if(Bseek(b, (block+1)*Blocksize, 0) != (block+1)*Blocksize) {
  239. print("Xbseek %d fail\n", (block+1)*Blocksize);
  240. goto Die;
  241. }
  242. for(j=0; j<Blocksize/4; j++) {
  243. if(Bread(b, buf, 4) != 4) {
  244. print("Bread fail seek block %x, %d i %d ndepot %d\n", block, (block+1)*Blocksize, i, ndepot);
  245. goto Die;
  246. }
  247. f->blockmap[u++] = LONG(buf);
  248. }
  249. }
  250. /*
  251. * if the first block can't hold it, it continues in the block at LONG(hdr+0x44).
  252. * if that in turn is not big enough, there's a next block number at the end of
  253. * each block.
  254. */
  255. while(i < ndepot) {
  256. for(k=0; k<(0x200-4)/4 && i<ndepot; i++, k++) {
  257. if(Bseek(b, 0x200+extrablock*Blocksize+4*i, 0) != 0x200+extrablock*0x200+4*i
  258. || Bread(b, buf, 4) != 4) {
  259. print("bseek %d fail\n", 0x4C+4*i);
  260. goto Die;
  261. }
  262. block = LONG(buf);
  263. if((ulong)block == Bendchain) {
  264. ndepot = i;
  265. f->nblock = ndepot*(Blocksize/4);
  266. goto Break2;
  267. }
  268. if(Bseek(b, (block+1)*Blocksize, 0) != (block+1)*Blocksize) {
  269. print("Xbseek %d fail\n", (block+1)*Blocksize);
  270. goto Die;
  271. }
  272. for(j=0; j<Blocksize/4; j++) {
  273. if(Bread(b, buf, 4) != 4) {
  274. print("Bread fail seek block %x, %d i %d ndepot %d\n", block, (block+1)*Blocksize, i, ndepot);
  275. goto Die;
  276. }
  277. f->blockmap[u++] = LONG(buf);
  278. }
  279. }
  280. if(Bseek(b, 0x200+extrablock*Blocksize+Blocksize-4, 0) != 0x200+extrablock*Blocksize+Blocksize-4
  281. || Bread(b, buf, 4) != 4) {
  282. print("bseek %d fail\n", 0x4C+4*i);
  283. goto Die;
  284. }
  285. extrablock = LONG(buf);
  286. }
  287. Break2:;
  288. if(oreaddir(f, 0, &rootdir) <= 0)
  289. goto Die;
  290. f->smapblock = rootdir.start;
  291. return f;
  292. Die:
  293. Bterm(b);
  294. free(f->blockmap);
  295. free(f);
  296. return nil;
  297. }
  298. void
  299. oleread(Req *r)
  300. {
  301. Odir *d;
  302. char *p;
  303. int e, n;
  304. long c;
  305. vlong o;
  306. o = r->ifcall.offset;
  307. d = r->fid->file->aux;
  308. if(d == nil) {
  309. respond(r, "cannot happen");
  310. return;
  311. }
  312. c = r->ifcall.count;
  313. if(o >= d->size) {
  314. r->ofcall.count = 0;
  315. respond(r, nil);
  316. return;
  317. }
  318. if(o+c > d->size)
  319. c = d->size-o;
  320. /*
  321. * oreadfile returns so little data, it will
  322. * help to read as much as we can.
  323. */
  324. e = c+o;
  325. n = 0;
  326. for(p=r->ofcall.data; o<e; o+=n, p+=n) {
  327. n = oreadfile(d, o, p, e-o);
  328. if(n <= 0)
  329. break;
  330. }
  331. if(n == -1 && o == r->ifcall.offset)
  332. respond(r, "error reading word file");
  333. else {
  334. r->ofcall.count = o - r->ifcall.offset;
  335. respond(r, nil);
  336. }
  337. }
  338. Odir*
  339. copydir(Odir *d)
  340. {
  341. Odir *e;
  342. e = emalloc(sizeof(*d));
  343. *e = *d;
  344. return e;
  345. }
  346. void
  347. filldir(File *t, Ofile *f, int dnum, int nrecur)
  348. {
  349. Odir d;
  350. int i;
  351. Rune rbuf[40];
  352. char buf[UTFmax*nelem(rbuf)];
  353. File *nt;
  354. if(dnum == 0xFFFFFFFF || oreaddir(f, dnum, &d) != 1)
  355. return;
  356. /*
  357. * i hope there are no broken files with
  358. * circular trees. i hate infinite loops.
  359. */
  360. if(nrecur > 100)
  361. sysfatal("tree too large in office file: probably circular");
  362. filldir(t, f, d.left, nrecur+1);
  363. /* add current tree entry */
  364. runestrecpy(rbuf, rbuf+sizeof rbuf, d.name);
  365. for(i=0; rbuf[i]; i++)
  366. if(rbuf[i] == L' ')
  367. rbuf[i] = L'␣';
  368. else if(rbuf[i] <= 0x20 || rbuf[i] == L'/'
  369. || (0x80 <= rbuf[i] && rbuf[i] <= 0x9F))
  370. rbuf[i] = ':';
  371. snprint(buf, sizeof buf, "%S", rbuf);
  372. if(d.dir == 0xFFFFFFFF) {
  373. /* make file */
  374. nt = createfile(t, buf, nil, 0444, nil);
  375. if(nt ==nil)
  376. fprint(2, "nt nil: create %s: %r\n", buf);
  377. nt->aux = copydir(&d);
  378. nt->length = d.size;
  379. } else /* make directory */
  380. nt = createfile(t, buf, nil, DMDIR|0777, nil);
  381. filldir(t, f, d.right, nrecur+1);
  382. if(d.dir != 0xFFFFFFFF)
  383. filldir(nt, f, d.dir, nrecur+1);
  384. closefile(nt);
  385. }
  386. Srv olesrv = {
  387. .read= oleread,
  388. };
  389. void
  390. main(int argc, char **argv)
  391. {
  392. char *mtpt;
  393. Ofile *f;
  394. Odir d;
  395. mtpt = "/mnt/doc";
  396. ARGBEGIN{
  397. case 'm':
  398. mtpt = ARGF();
  399. break;
  400. default:
  401. goto Usage;
  402. }ARGEND
  403. if(argc != 1) {
  404. Usage:
  405. fprint(2, "usage: olefs file\n");
  406. exits("usage");
  407. }
  408. f = oleopen(argv[0]);
  409. if(f == nil) {
  410. print("error: %r\n");
  411. exits("open");
  412. }
  413. // dumpdir(f, 0);
  414. if(oreaddir(f, 0, &d) != 1) {
  415. fprint(2, "oreaddir error: %r\n");
  416. exits("oreaddir");
  417. }
  418. olesrv.tree = alloctree(nil, nil, DMDIR|0777, nil);
  419. filldir(olesrv.tree->root, f, d.dir, 0);
  420. postmountsrv(&olesrv, nil, mtpt, MREPL);
  421. exits(0);
  422. }