bundle.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638
  1. /*++
  2. Copyright (c) 2016 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. bundle.c
  9. Abstract:
  10. This module implements the Chalk bundle module, which allows for creation
  11. of a specialized application based on a Chalk environment.
  12. Author:
  13. Evan Green 19-Oct-2016
  14. Environment:
  15. C
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #define _LARGEFILE64_SOURCE 1
  21. #include <minoca/lib/types.h>
  22. #include <minoca/lib/chalk.h>
  23. #include <minoca/lib/chalk/app.h>
  24. #include <errno.h>
  25. #include <libgen.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <sys/stat.h>
  30. #include <time.h>
  31. #include <unistd.h>
  32. //
  33. // --------------------------------------------------------------------- Macros
  34. //
  35. #ifdef _WIN32
  36. #define mkdir(_Path, _Permissions) mkdir(_Path)
  37. #define S_IXGRP 0
  38. #define S_IXOTH 0
  39. #endif
  40. #if defined(__APPLE__) || defined(__CYGWIN__) || defined(__FreeBSD__)
  41. #define fseeko64 fseeko
  42. #define ftello64 ftello
  43. #endif
  44. //
  45. // ---------------------------------------------------------------- Definitions
  46. //
  47. //
  48. // Define the magic value to search for to indicate the presence of a bundle.
  49. //
  50. #define CK_BUNDLE_MAGIC 0x7F6C646E75426B43ULL
  51. //
  52. // Define the size of the temporary file name.
  53. //
  54. #define CK_BUNDLE_NAME_SIZE 256
  55. //
  56. // ------------------------------------------------------ Data Type Definitions
  57. //
  58. //
  59. // ----------------------------------------------- Internal Function Prototypes
  60. //
  61. VOID
  62. CkpBundleModuleInit (
  63. PCK_VM Vm
  64. );
  65. VOID
  66. CkpBundleCreate (
  67. PCK_VM Vm
  68. );
  69. INT
  70. CkpBundleFreezeInteger (
  71. FILE *File,
  72. LONGLONG Value
  73. );
  74. INT
  75. CkpBundleFreezeBuffer (
  76. FILE *File,
  77. PCVOID Buffer,
  78. UINTN Size
  79. );
  80. ULONG
  81. CkpBundleChecksum (
  82. PVOID Buffer,
  83. UINTN Size
  84. );
  85. PSTR
  86. CkpFindBundle (
  87. PSTR Buffer,
  88. PSTR End,
  89. PUINTN Size
  90. );
  91. INT
  92. CkpLoadBundle (
  93. PCK_VM Vm,
  94. PSTR Bundle,
  95. UINTN BundleSize
  96. );
  97. INT
  98. CkpBundleLoadModules (
  99. PCK_VM Vm,
  100. PSTR *Bundle,
  101. PUINTN BundleSize
  102. );
  103. INT
  104. CkpBundleLoadModule (
  105. PCK_VM Vm,
  106. PSTR *Bundle,
  107. PUINTN BundleSize
  108. );
  109. PSTR
  110. CkpBundleThawElement (
  111. PSTR *Contents,
  112. PUINTN Size,
  113. PUINTN NameSize
  114. );
  115. PSTR
  116. CkpBundleThawString (
  117. PCK_VM Vm,
  118. PSTR *Contents,
  119. PUINTN Size,
  120. PUINTN StringSize
  121. );
  122. BOOL
  123. CkpBundleThawInteger (
  124. PSTR *Contents,
  125. PUINTN Size,
  126. PCK_INTEGER Integer
  127. );
  128. PSTR
  129. CkpBundleGetTemporaryDirectory (
  130. VOID
  131. );
  132. //
  133. // -------------------------------------------------------------------- Globals
  134. //
  135. CK_VARIABLE_DESCRIPTION CkBundleModuleValues[] = {
  136. {CkTypeFunction, "create", CkpBundleCreate, 3},
  137. {CkTypeInvalid, NULL, NULL, 0}
  138. };
  139. //
  140. // Define the temporary directory name where modules are stored.
  141. //
  142. CHAR CkBundleDirectory[256];
  143. //
  144. // ------------------------------------------------------------------ Functions
  145. //
  146. BOOL
  147. CkPreloadBundleModule (
  148. PCK_VM Vm
  149. )
  150. /*++
  151. Routine Description:
  152. This routine preloads the bundle module. It is called to make the presence
  153. of the module known in cases where the module is statically linked.
  154. Arguments:
  155. Vm - Supplies a pointer to the virtual machine.
  156. Return Value:
  157. TRUE on success.
  158. FALSE on failure.
  159. --*/
  160. {
  161. BOOL Result;
  162. Result = CkPreloadForeignModule(Vm,
  163. "bundle",
  164. NULL,
  165. NULL,
  166. CkpBundleModuleInit);
  167. return Result;
  168. }
  169. INT
  170. CkBundleThaw (
  171. PCK_VM Vm
  172. )
  173. /*++
  174. Routine Description:
  175. This routine reloads the modules previously saved in a bundle. The exec
  176. name global should be set before calling this function.
  177. Arguments:
  178. Vm - Supplies a pointer to the virtual machine.
  179. Return Value:
  180. Returns 0 if the bundle was loaded successfully.
  181. Returns -1 if no bundle could be found.
  182. Returns other values on error.
  183. --*/
  184. {
  185. PSTR Buffer;
  186. UINTN BundleSize;
  187. PSTR Current;
  188. PSTR End;
  189. FILE *File;
  190. struct stat Stat;
  191. INT Status;
  192. Buffer = NULL;
  193. if (stat(CkAppExecName, &Stat) != 0) {
  194. Status = errno;
  195. goto BundleThawEnd;
  196. }
  197. Buffer = malloc(Stat.st_size + 1);
  198. if (Buffer == NULL) {
  199. Status = errno;
  200. goto BundleThawEnd;
  201. }
  202. File = fopen(CkAppExecName, "rb");
  203. if (File == NULL) {
  204. Status = errno;
  205. goto BundleThawEnd;
  206. }
  207. if (fread(Buffer, 1, Stat.st_size, File) != Stat.st_size) {
  208. fclose(File);
  209. Status = errno;
  210. goto BundleThawEnd;
  211. }
  212. fclose(File);
  213. Buffer[Stat.st_size] = '\0';
  214. End = Buffer + Stat.st_size;
  215. Current = Buffer;
  216. Status = -1;
  217. while (TRUE) {
  218. Current = CkpFindBundle(Current, End, &BundleSize);
  219. if (Current == NULL) {
  220. break;
  221. }
  222. Status = CkpLoadBundle(Vm, Current, BundleSize);
  223. if (Status != 0) {
  224. goto BundleThawEnd;
  225. }
  226. Current += BundleSize;
  227. }
  228. BundleThawEnd:
  229. if (Buffer != NULL) {
  230. free(Buffer);
  231. }
  232. return Status;
  233. }
  234. //
  235. // --------------------------------------------------------- Internal Functions
  236. //
  237. VOID
  238. CkpBundleModuleInit (
  239. PCK_VM Vm
  240. )
  241. /*++
  242. Routine Description:
  243. This routine populates the OS module namespace.
  244. Arguments:
  245. Vm - Supplies a pointer to the virtual machine.
  246. Return Value:
  247. None.
  248. --*/
  249. {
  250. CkDeclareVariables(Vm, 0, CkBundleModuleValues);
  251. return;
  252. }
  253. VOID
  254. CkpBundleCreate (
  255. PCK_VM Vm
  256. )
  257. /*++
  258. Routine Description:
  259. This routine creates a new application bundle.
  260. Arguments:
  261. Vm - Supplies a pointer to the virtual machine.
  262. Return Value:
  263. None.
  264. --*/
  265. {
  266. PVOID Buffer;
  267. size_t BufferSize;
  268. off_t ChecksumOffset;
  269. off_t EndOffset;
  270. FILE *Executable;
  271. FILE *File;
  272. FILE *ForeignFile;
  273. CK_INTEGER IsForeign;
  274. CK_INTEGER ModuleCount;
  275. UINTN ModuleIndex;
  276. PCSTR OutputName;
  277. PCSTR Path;
  278. UINTN PathSize;
  279. struct stat Stat;
  280. INT Status;
  281. PCSTR String;
  282. UINTN StringSize;
  283. off_t TotalSize;
  284. ULONG Value32;
  285. ULONGLONG Value64;
  286. Buffer = NULL;
  287. Executable = NULL;
  288. File = NULL;
  289. ForeignFile = NULL;
  290. OutputName = NULL;
  291. if (!CkCheckArguments(Vm, 3, CkTypeString, CkTypeList, CkTypeString)) {
  292. Status = -1;
  293. goto BundleCreateEnd;
  294. }
  295. //
  296. // This routine takes 3 arguments:
  297. // 1) The output file name.
  298. // 2) The set of modules to add.
  299. // 3) The expression to execute once all modules are preloaded.
  300. //
  301. if ((CkAppExecName == NULL) || (*CkAppExecName == '\0')) {
  302. Status = EINVAL;
  303. goto BundleCreateEnd;
  304. }
  305. //
  306. // Create the output file, and copy the executable to it.
  307. //
  308. OutputName = CkGetString(Vm, 1, NULL);
  309. File = fopen(OutputName, "wb+");
  310. if (File == NULL) {
  311. Status = errno;
  312. goto BundleCreateEnd;
  313. }
  314. if (stat(CkAppExecName, &Stat) != 0) {
  315. Status = errno;
  316. goto BundleCreateEnd;
  317. }
  318. Executable = fopen(CkAppExecName, "rb");
  319. if (Executable == NULL) {
  320. Status = errno;
  321. goto BundleCreateEnd;
  322. }
  323. BufferSize = Stat.st_size;
  324. Buffer = malloc(Stat.st_size);
  325. if (Buffer == NULL) {
  326. Status = errno;
  327. goto BundleCreateEnd;
  328. }
  329. if ((fread(Buffer, 1, Stat.st_size, Executable) != Stat.st_size) ||
  330. (fwrite(Buffer, 1, Stat.st_size, File) != Stat.st_size)) {
  331. Status = errno;
  332. if (Status == 0) {
  333. Status = EIO;
  334. }
  335. goto BundleCreateEnd;
  336. }
  337. //
  338. // Write out the magic value, and save room for the length and checksum.
  339. //
  340. Value64 = CK_BUNDLE_MAGIC;
  341. if (fwrite(&Value64, 1, sizeof(Value64), File) != sizeof(Value64)) {
  342. Status = errno;
  343. goto BundleCreateEnd;
  344. }
  345. ChecksumOffset = ftello64(File);
  346. if (ChecksumOffset == -1) {
  347. Status = errno;
  348. goto BundleCreateEnd;
  349. }
  350. Value32 = 0;
  351. if (fwrite(&Value32, 1, sizeof(Value32), File) != sizeof(Value32)) {
  352. Status = errno;
  353. goto BundleCreateEnd;
  354. }
  355. Value64 = 0;
  356. if (fwrite(&Value64, 1, sizeof(Value64), File) != sizeof(Value64)) {
  357. Status = errno;
  358. goto BundleCreateEnd;
  359. }
  360. if (fprintf(File, "{\nExpression: ") < 0) {
  361. Status = errno;
  362. goto BundleCreateEnd;
  363. }
  364. String = CkGetString(Vm, 3, &StringSize);
  365. if (String == NULL) {
  366. Status = EINVAL;
  367. goto BundleCreateEnd;
  368. }
  369. Status = CkpBundleFreezeBuffer(File, String, StringSize);
  370. if (Status != 0) {
  371. goto BundleCreateEnd;
  372. }
  373. if (fprintf(File, "\nModules: [\n") < 0) {
  374. Status = errno;
  375. goto BundleCreateEnd;
  376. }
  377. //
  378. // Now emit all the modules.
  379. //
  380. if (!CkGetLength(Vm, 2, &ModuleCount)) {
  381. Status = -1;
  382. goto BundleCreateEnd;
  383. }
  384. for (ModuleIndex = 0; ModuleIndex < ModuleCount; ModuleIndex += 1) {
  385. Path = NULL;
  386. PathSize = 0;
  387. CkListGet(Vm, 2, ModuleIndex);
  388. if (!CkCallMethod(Vm, "isForeign", 0)) {
  389. Status = -1;
  390. goto BundleCreateEnd;
  391. }
  392. IsForeign = CkGetInteger(Vm, -1);
  393. CkStackPop(Vm);
  394. //
  395. // If the module is foreign, attempt to get its path. If it has no
  396. // path, then skip it.
  397. //
  398. if (IsForeign != 0) {
  399. CkListGet(Vm, 2, ModuleIndex);
  400. if (!CkCallMethod(Vm, "path", 0)) {
  401. Status = -1;
  402. goto BundleCreateEnd;
  403. }
  404. Path = CkGetString(Vm, -1, &PathSize);
  405. if ((Path == NULL) || (PathSize == 0)) {
  406. CkStackPop(Vm);
  407. continue;
  408. }
  409. }
  410. //
  411. // Write the foreign boolean.
  412. //
  413. if (fprintf(File, "{\nForeign: ") < 0) {
  414. Status = errno;
  415. goto BundleCreateEnd;
  416. }
  417. Status = CkpBundleFreezeInteger(File, IsForeign);
  418. if (Status != 0) {
  419. goto BundleCreateEnd;
  420. }
  421. //
  422. // Write the module name.
  423. //
  424. if (fprintf(File, "\nName: ") < 0) {
  425. Status = errno;
  426. goto BundleCreateEnd;
  427. }
  428. CkListGet(Vm, 2, ModuleIndex);
  429. if (!CkCallMethod(Vm, "name", 0)) {
  430. Status = -1;
  431. goto BundleCreateEnd;
  432. }
  433. String = CkGetString(Vm, -1, &StringSize);
  434. Status = CkpBundleFreezeBuffer(File, String, StringSize);
  435. if (Status != 0) {
  436. goto BundleCreateEnd;
  437. }
  438. CkStackPop(Vm);
  439. //
  440. // Write out the path if there is one.
  441. //
  442. if (IsForeign != FALSE) {
  443. if (fprintf(File, "\nPath: ") < 0) {
  444. Status = errno;
  445. goto BundleCreateEnd;
  446. }
  447. Status = CkpBundleFreezeBuffer(File, Path, PathSize);
  448. if (Status != 0) {
  449. goto BundleCreateEnd;
  450. }
  451. //
  452. // Read in the shared object file.
  453. //
  454. if (stat(Path, &Stat) != 0) {
  455. Status = errno;
  456. goto BundleCreateEnd;
  457. }
  458. ForeignFile = fopen(Path, "rb");
  459. CkStackPop(Vm);
  460. Path = NULL;
  461. if (ForeignFile == NULL) {
  462. Status = errno;
  463. goto BundleCreateEnd;
  464. }
  465. if (BufferSize < Stat.st_size) {
  466. BufferSize = Stat.st_size;
  467. free(Buffer);
  468. Buffer = malloc(BufferSize);
  469. if (Buffer == NULL) {
  470. Status = errno;
  471. goto BundleCreateEnd;
  472. }
  473. }
  474. if (fread(Buffer, 1, Stat.st_size, ForeignFile) != Stat.st_size) {
  475. Status = errno;
  476. goto BundleCreateEnd;
  477. }
  478. fclose(ForeignFile);
  479. ForeignFile = NULL;
  480. if (fprintf(File, "\nData: ") < 0) {
  481. Status = -1;
  482. goto BundleCreateEnd;
  483. }
  484. Status = CkpBundleFreezeBuffer(File, Buffer, Stat.st_size);
  485. if (Status != 0) {
  486. goto BundleCreateEnd;
  487. }
  488. //
  489. // This is not a foreign module. Freeze it and write out the frozen
  490. // contents.
  491. //
  492. } else {
  493. if (fprintf(File, "\nData: ") < 0) {
  494. Status = -1;
  495. goto BundleCreateEnd;
  496. }
  497. CkListGet(Vm, 2, ModuleIndex);
  498. if (!CkCallMethod(Vm, "freeze", 0)) {
  499. Status = -1;
  500. goto BundleCreateEnd;
  501. }
  502. String = CkGetString(Vm, -1, &StringSize);
  503. Status = CkpBundleFreezeBuffer(File, String, StringSize);
  504. if (Status != 0) {
  505. goto BundleCreateEnd;
  506. }
  507. CkStackPop(Vm);
  508. }
  509. //
  510. // Print the module terminator.
  511. //
  512. if (fprintf(File, "\n}") < 0) {
  513. Status = errno;
  514. goto BundleCreateEnd;
  515. }
  516. //
  517. // Print the list separator.
  518. //
  519. if (ModuleIndex != ModuleCount - 1) {
  520. if (fprintf(File, ", \n") < 0) {
  521. Status = errno;
  522. goto BundleCreateEnd;
  523. }
  524. }
  525. }
  526. if (fprintf(File, "]\n}\n") < 0) {
  527. Status = errno;
  528. goto BundleCreateEnd;
  529. }
  530. //
  531. // Write the length in its final place place.
  532. //
  533. EndOffset = ftello64(File);
  534. if ((EndOffset == -1) || (EndOffset < ChecksumOffset)) {
  535. Status = EINVAL;
  536. goto BundleCreateEnd;
  537. }
  538. TotalSize = EndOffset - ChecksumOffset;
  539. Value64 = TotalSize;
  540. if ((fseeko64(File, ChecksumOffset + sizeof(Value32), SEEK_SET) < 0) ||
  541. (fwrite(&Value64, 1, sizeof(Value64), File) != sizeof(Value64))) {
  542. Status = errno;
  543. goto BundleCreateEnd;
  544. }
  545. //
  546. // Read in the full contents.
  547. //
  548. if (BufferSize < TotalSize) {
  549. free(Buffer);
  550. BufferSize = TotalSize;
  551. Buffer = malloc(BufferSize);
  552. if (Buffer == NULL) {
  553. Status = errno;
  554. goto BundleCreateEnd;
  555. }
  556. }
  557. if ((fseeko64(File, ChecksumOffset, SEEK_SET) < 0) ||
  558. (fread(Buffer, 1, TotalSize, File) != TotalSize)) {
  559. Status = errno;
  560. goto BundleCreateEnd;
  561. }
  562. //
  563. // Write out the checksum.
  564. //
  565. Value32 = CkpBundleChecksum(Buffer, TotalSize);
  566. if ((fseeko64(File, ChecksumOffset, SEEK_SET) < 0) ||
  567. (fwrite(&Value32, 1, sizeof(Value32), File) != sizeof(Value32))) {
  568. Status = errno;
  569. goto BundleCreateEnd;
  570. }
  571. Status = 0;
  572. BundleCreateEnd:
  573. if (Executable != NULL) {
  574. fclose(Executable);
  575. }
  576. if (File != NULL) {
  577. fclose(File);
  578. if (stat(OutputName, &Stat) == 0) {
  579. chmod(OutputName, Stat.st_mode | S_IXUSR | S_IXOTH | S_IXGRP);
  580. }
  581. }
  582. if (ForeignFile != NULL) {
  583. fclose(ForeignFile);
  584. }
  585. if (Buffer != NULL) {
  586. free(Buffer);
  587. }
  588. if (Status != 0) {
  589. if (Status != -1) {
  590. CkRaiseBasicException(Vm,
  591. "RuntimeError",
  592. "Error during bundle creation: %s",
  593. strerror(Status));
  594. }
  595. }
  596. return;
  597. }
  598. INT
  599. CkpBundleFreezeInteger (
  600. FILE *File,
  601. LONGLONG Value
  602. )
  603. /*++
  604. Routine Description:
  605. This routine prints an integer to the frozen data.
  606. Arguments:
  607. File - Supplies a pointer to the file to write out to.
  608. Value - Supplies the value to write.
  609. Return Value:
  610. 0 on success.
  611. Returns an error number on failure.
  612. --*/
  613. {
  614. if (fprintf(File, "i%lld ", Value) < 0) {
  615. return errno;
  616. }
  617. return 0;
  618. }
  619. INT
  620. CkpBundleFreezeBuffer (
  621. FILE *File,
  622. PCVOID Buffer,
  623. UINTN Size
  624. )
  625. /*++
  626. Routine Description:
  627. This routine prints a string or raw byte buffer to the file.
  628. Arguments:
  629. File - Supplies a pointer to the file to write out to.
  630. Buffer - Supplies a pointer to the buffer to write.
  631. Size - Supplies the size of the buffer in bytes.
  632. Return Value:
  633. 0 on success.
  634. Returns an error number on failure.
  635. --*/
  636. {
  637. if (fprintf(File, "s%ld\"", Size) < 0) {
  638. return errno;
  639. }
  640. if (fwrite(Buffer, 1, Size, File) != Size) {
  641. if (errno == 0) {
  642. return -1;
  643. }
  644. return errno;
  645. }
  646. if (fprintf(File, "\"") < 0) {
  647. return errno;
  648. }
  649. return 0;
  650. }
  651. ULONG
  652. CkpBundleChecksum (
  653. PVOID Buffer,
  654. UINTN Size
  655. )
  656. /*++
  657. Routine Description:
  658. This routine performs a simple checksum on the given buffer.
  659. Arguments:
  660. Buffer - Supplies a pointer to the buffer to sum.
  661. Size - Supplies the number of bytes to sum.
  662. Return Value:
  663. Returns the sum of all the bytes.
  664. --*/
  665. {
  666. PUCHAR Bytes;
  667. PUCHAR End;
  668. ULONG Sum;
  669. Bytes = Buffer;
  670. End = Buffer + Size;
  671. Sum = 0;
  672. while (Bytes < End) {
  673. Sum += *Bytes;
  674. Bytes += 1;
  675. }
  676. return Sum;
  677. }
  678. PSTR
  679. CkpFindBundle (
  680. PSTR Buffer,
  681. PSTR End,
  682. PUINTN Size
  683. )
  684. /*++
  685. Routine Description:
  686. This routine locates a valid bundle within the given bundle.
  687. Arguments:
  688. Buffer - Supplies a pointer to the buffer to search.
  689. End - Supplies a pointer one beyond the end of the buffer.
  690. Size - Supplies a pointer where the size of the bundle will be returned.
  691. Return Value:
  692. Returns a pointer to the bundle just beyond the magic, checksum, and length.
  693. NULL if no valid bundle could be found.
  694. --*/
  695. {
  696. ULONG Checksum;
  697. ULONG ComputedChecksum;
  698. ULONGLONG Length;
  699. ULONGLONG Magic;
  700. Magic = CK_BUNDLE_MAGIC;
  701. while (Buffer + sizeof(Magic) + sizeof(Checksum) + sizeof(Length) <= End) {
  702. //
  703. // Get excited if the magic value is here.
  704. //
  705. if ((*Buffer == (CHAR)Magic) &&
  706. (memcmp(Buffer, &Magic, sizeof(Magic)) == 0)) {
  707. Buffer += sizeof(Magic);
  708. //
  709. // Grab the checksum and length, and validate them.
  710. //
  711. memcpy(&Checksum, Buffer, sizeof(Checksum));
  712. memcpy(&Length, Buffer + sizeof(Checksum), sizeof(Length));
  713. if ((Length > sizeof(Checksum) + sizeof(Length)) &&
  714. (Buffer + Length <= End)) {
  715. ComputedChecksum = CkpBundleChecksum(Buffer + sizeof(Checksum),
  716. Length - sizeof(Checksum));
  717. if (Checksum == ComputedChecksum) {
  718. *Size = Length - (sizeof(Checksum) + sizeof(Length));
  719. return Buffer + sizeof(Checksum) + sizeof(Length);
  720. }
  721. }
  722. } else {
  723. Buffer += 1;
  724. }
  725. }
  726. return NULL;
  727. }
  728. INT
  729. CkpLoadBundle (
  730. PCK_VM Vm,
  731. PSTR Bundle,
  732. UINTN BundleSize
  733. )
  734. /*++
  735. Routine Description:
  736. This routine loads up the modules in a bundle.
  737. Arguments:
  738. Vm - Supplies a pointer to the virtual machine.
  739. Bundle - Supplies a pointer to the bundle.
  740. BundleSize - Supplies the size of the bundle in bytes.
  741. Return Value:
  742. 0 on success.
  743. Returns an error number on failure.
  744. --*/
  745. {
  746. PCSTR Expression;
  747. UINTN ExpressionSize;
  748. PCSTR Name;
  749. UINTN NameSize;
  750. INT Status;
  751. Expression = NULL;
  752. if ((BundleSize < 2) || (*Bundle != '{')) {
  753. Status = EINVAL;
  754. goto LoadBundleEnd;
  755. }
  756. Bundle += 1;
  757. BundleSize -= 1;
  758. //
  759. // Loop pulling elements out of the outer dictionary.
  760. //
  761. while (TRUE) {
  762. Name = CkpBundleThawElement(&Bundle, &BundleSize, &NameSize);
  763. if (Name == NULL) {
  764. break;
  765. }
  766. if ((NameSize == 10) && (memcmp(Name, "Expression", 10) == 0)) {
  767. Expression = CkpBundleThawString(Vm,
  768. &Bundle,
  769. &BundleSize,
  770. &ExpressionSize);
  771. if (Expression == NULL) {
  772. Status = EINVAL;
  773. goto LoadBundleEnd;
  774. }
  775. } else if ((NameSize == 7) && (memcmp(Name, "Modules", 7) == 0)) {
  776. Status = CkpBundleLoadModules(Vm, &Bundle, &BundleSize);
  777. if (Status != 0) {
  778. goto LoadBundleEnd;
  779. }
  780. }
  781. }
  782. if (*Bundle != '}') {
  783. Status = EINVAL;
  784. goto LoadBundleEnd;
  785. }
  786. Status = 0;
  787. LoadBundleEnd:
  788. if (Status != 0) {
  789. fprintf(stderr, "Failed to load bundle: %s\n", strerror(Status));
  790. } else {
  791. //
  792. // The bundle's all loaded, execute the expression.
  793. //
  794. if (Expression != NULL) {
  795. Status = CkInterpret(Vm,
  796. NULL,
  797. Expression,
  798. ExpressionSize,
  799. 1,
  800. FALSE);
  801. }
  802. }
  803. return Status;
  804. }
  805. INT
  806. CkpBundleLoadModules (
  807. PCK_VM Vm,
  808. PSTR *Bundle,
  809. PUINTN BundleSize
  810. )
  811. /*++
  812. Routine Description:
  813. This routine loads up the modules portion of a bundle.
  814. Arguments:
  815. Vm - Supplies a pointer to the virtual machine.
  816. Bundle - Supplies a pointer to the bundle.
  817. BundleSize - Supplies the size of the bundle in bytes.
  818. Return Value:
  819. 0 on success.
  820. Returns an error number on failure.
  821. --*/
  822. {
  823. INT Status;
  824. if ((*BundleSize < 2) || (**Bundle != '[')) {
  825. return EINVAL;
  826. }
  827. *Bundle += 2;
  828. *BundleSize -= 2;
  829. while (TRUE) {
  830. Status = CkpBundleLoadModule(Vm, Bundle, BundleSize);
  831. if (Status != 0) {
  832. return Status;
  833. }
  834. if (*BundleSize < 1) {
  835. return EINVAL;
  836. }
  837. if (**Bundle != ',') {
  838. break;
  839. }
  840. *Bundle += 1;
  841. *BundleSize -= 1;
  842. while ((*BundleSize > 0) &&
  843. ((**Bundle == ' ') || (**Bundle == '\n'))) {
  844. *Bundle += 1;
  845. *BundleSize -= 1;
  846. }
  847. }
  848. if ((*BundleSize < 1) || (**Bundle != ']')) {
  849. return EINVAL;
  850. }
  851. *Bundle += 1;
  852. *BundleSize -= 1;
  853. Status = 0;
  854. return Status;
  855. }
  856. INT
  857. CkpBundleLoadModule (
  858. PCK_VM Vm,
  859. PSTR *Bundle,
  860. PUINTN BundleSize
  861. )
  862. /*++
  863. Routine Description:
  864. This routine loads a single module within a bundle.
  865. Arguments:
  866. Vm - Supplies a pointer to the virtual machine.
  867. Bundle - Supplies a pointer to the bundle.
  868. BundleSize - Supplies the size of the bundle in bytes.
  869. Return Value:
  870. 0 on success.
  871. Returns an error number on failure.
  872. --*/
  873. {
  874. PSTR BaseName;
  875. PSTR Current;
  876. PCSTR Data;
  877. UINTN DataSize;
  878. PSTR Directory;
  879. FILE *File;
  880. CHAR FileName[CK_BUNDLE_NAME_SIZE];
  881. CK_INTEGER Foreign;
  882. PCSTR ModuleName;
  883. UINTN ModuleNameSize;
  884. PCSTR Name;
  885. UINTN NameSize;
  886. PSTR Path;
  887. UINTN PathSize;
  888. UINTN Size;
  889. INT Status;
  890. Data = NULL;
  891. Directory = NULL;
  892. File = NULL;
  893. Path = NULL;
  894. ModuleName = NULL;
  895. Foreign = FALSE;
  896. Current = *Bundle;
  897. Size = *BundleSize;
  898. //
  899. // Parse out all the elements.
  900. //
  901. if ((Size == 0) || (*Current != '{')) {
  902. Status = EINVAL;
  903. goto BundleLoadModuleEnd;
  904. }
  905. Current += 1;
  906. Size -= 1;
  907. while (TRUE) {
  908. Name = CkpBundleThawElement(&Current, &Size, &NameSize);
  909. if (Name == NULL) {
  910. break;
  911. }
  912. if ((NameSize == 7) && (memcmp(Name, "Foreign", 7) == 0)) {
  913. if (!CkpBundleThawInteger(&Current, &Size, &Foreign)) {
  914. Status = EINVAL;
  915. goto BundleLoadModuleEnd;
  916. }
  917. } else if ((NameSize == 4) && (memcmp(Name, "Path", 4) == 0)) {
  918. Path = CkpBundleThawString(Vm, &Current, &Size, &PathSize);
  919. } else if ((NameSize == 4) && (memcmp(Name, "Name", 4) == 0)) {
  920. ModuleName = CkpBundleThawString(Vm,
  921. &Current,
  922. &Size,
  923. &ModuleNameSize);
  924. } else if ((NameSize == 4) && (memcmp(Name, "Data", 4) == 0)) {
  925. Data = CkpBundleThawString(Vm, &Current, &Size, &DataSize);
  926. } else {
  927. Status = EILSEQ;
  928. goto BundleLoadModuleEnd;
  929. }
  930. }
  931. if ((Size == 0) || (*Current != '}')) {
  932. Status = EINVAL;
  933. goto BundleLoadModuleEnd;
  934. }
  935. Current += 1;
  936. Size -= 1;
  937. if ((Data == NULL) || (ModuleName == NULL)) {
  938. Status = EINVAL;
  939. goto BundleLoadModuleEnd;
  940. }
  941. if (Directory == NULL) {
  942. Directory = CkpBundleGetTemporaryDirectory();
  943. if (Directory == NULL) {
  944. Status = errno;
  945. goto BundleLoadModuleEnd;
  946. }
  947. }
  948. //
  949. // Get the module file name.
  950. //
  951. if (Foreign != FALSE) {
  952. if (Path == NULL) {
  953. Status = EINVAL;
  954. goto BundleLoadModuleEnd;
  955. }
  956. BaseName = basename(Path);
  957. snprintf(FileName, CK_BUNDLE_NAME_SIZE, "%s/%s", Directory, BaseName);
  958. FileName[CK_BUNDLE_NAME_SIZE - 1] = '\0';
  959. } else {
  960. snprintf(FileName,
  961. CK_BUNDLE_NAME_SIZE,
  962. "%s/%s.%s",
  963. Directory,
  964. ModuleName,
  965. CK_SOURCE_EXTENSION);
  966. }
  967. //
  968. // Write out the module.
  969. //
  970. File = fopen(FileName, "wb");
  971. if (File == NULL) {
  972. Status = errno;
  973. goto BundleLoadModuleEnd;
  974. }
  975. if (fwrite(Data, 1, DataSize, File) != DataSize) {
  976. fclose(File);
  977. Status = errno;
  978. goto BundleLoadModuleEnd;
  979. }
  980. fclose(File);
  981. //
  982. // Fire up the library.
  983. //
  984. if (!CkLoadModule(Vm, ModuleName, FileName)) {
  985. Status = EILSEQ;
  986. goto BundleLoadModuleEnd;
  987. }
  988. Status = 0;
  989. BundleLoadModuleEnd:
  990. if (Status != 0) {
  991. fprintf(stderr, "Failed to load bundle: %s\n", strerror(errno));
  992. }
  993. *BundleSize = Size;
  994. *Bundle = Current;
  995. return Status;
  996. }
  997. PSTR
  998. CkpBundleThawElement (
  999. PSTR *Contents,
  1000. PUINTN Size,
  1001. PUINTN NameSize
  1002. )
  1003. /*++
  1004. Routine Description:
  1005. This routine reads a dictionary entry.
  1006. Arguments:
  1007. Contents - Supplies a pointer that on input points to the element to read.
  1008. This is updated on output.
  1009. Size - Supplies a pointer to the remaining size, not including a null
  1010. terminator which may not exist.
  1011. NameSize - Supplies a pointer where the size of the element name/key will
  1012. be returned.
  1013. Return Value:
  1014. Returns the address of the element key, if found.
  1015. NULL on failure.
  1016. --*/
  1017. {
  1018. PSTR Current;
  1019. PSTR End;
  1020. PSTR Name;
  1021. Current = *Contents;
  1022. End = Current + *Size;
  1023. //
  1024. // Skip any space.
  1025. //
  1026. while ((Current < End) &&
  1027. ((*Current == ' ') ||
  1028. (*Current == '\t') ||
  1029. (*Current == '\r') ||
  1030. (*Current == '\n'))) {
  1031. Current += 1;
  1032. }
  1033. Name = Current;
  1034. //
  1035. // Find a colon.
  1036. //
  1037. while ((Current < End) && (*Current != ':')) {
  1038. Current += 1;
  1039. }
  1040. //
  1041. // If there was no colon, just return the advance past the spaces. If the
  1042. // name is a closing brace, exit.
  1043. //
  1044. if ((Current == End) || ((Name < End) && (*Name == '}'))) {
  1045. *Size = End - Name;
  1046. *Contents = Name;
  1047. return NULL;
  1048. }
  1049. *NameSize = Current - Name;
  1050. //
  1051. // Get past the colon and any additional space.
  1052. //
  1053. Current += 1;
  1054. while ((Current < End) &&
  1055. ((*Current == ' ') ||
  1056. (*Current == '\t') ||
  1057. (*Current == '\r') ||
  1058. (*Current == '\n'))) {
  1059. Current += 1;
  1060. }
  1061. *Size = End - Current;
  1062. *Contents = Current;
  1063. return Name;
  1064. }
  1065. PSTR
  1066. CkpBundleThawString (
  1067. PCK_VM Vm,
  1068. PSTR *Contents,
  1069. PUINTN Size,
  1070. PUINTN StringSize
  1071. )
  1072. /*++
  1073. Routine Description:
  1074. This routine reads a string.
  1075. Arguments:
  1076. Vm - Supplies a pointer to the virtual machine.
  1077. Contents - Supplies a pointer that on input points to the element to read.
  1078. This is updated on output.
  1079. Size - Supplies a pointer to the remaining size, not including a null
  1080. terminator which may not exist.
  1081. StringSize - Supplies a pointer where the size of the string in bytes
  1082. will be returned, not including a null terminator (which won't be
  1083. there).
  1084. Return Value:
  1085. Returns a pointer to the string on success.
  1086. NULL on failure.
  1087. --*/
  1088. {
  1089. PSTR AfterScan;
  1090. PSTR String;
  1091. if ((*Size < 2) || (**Contents != 's')) {
  1092. return NULL;
  1093. }
  1094. *StringSize = strtoull(*Contents + 1, &AfterScan, 10);
  1095. if ((AfterScan == NULL) || (AfterScan == *Contents) ||
  1096. (AfterScan >= *Contents + *Size) ||
  1097. (AfterScan + 2 + *StringSize > *Contents + *Size)) {
  1098. return NULL;
  1099. }
  1100. AfterScan += 1;
  1101. String = AfterScan;
  1102. AfterScan += *StringSize;
  1103. if (*AfterScan != '"') {
  1104. return NULL;
  1105. }
  1106. //
  1107. // Terminate the string inline.
  1108. //
  1109. *AfterScan = '\0';
  1110. AfterScan += 1;
  1111. *Size -= AfterScan - *Contents;
  1112. *Contents = AfterScan;
  1113. return String;
  1114. }
  1115. BOOL
  1116. CkpBundleThawInteger (
  1117. PSTR *Contents,
  1118. PUINTN Size,
  1119. PCK_INTEGER Integer
  1120. )
  1121. /*++
  1122. Routine Description:
  1123. This routine reads an integer.
  1124. Arguments:
  1125. Contents - Supplies a pointer that on input points to the element to read.
  1126. This is updated on output.
  1127. Size - Supplies a pointer to the remaining size, not including a null
  1128. terminator which may not exist.
  1129. Integer - Supplies a pointer to the integer to read.
  1130. Return Value:
  1131. TRUE on success.
  1132. FALSE on failure.
  1133. --*/
  1134. {
  1135. PSTR AfterScan;
  1136. if ((*Size < 2) || (**Contents != 'i')) {
  1137. return FALSE;
  1138. }
  1139. AfterScan = NULL;
  1140. *Integer = strtoll(*Contents + 1, &AfterScan, 10);
  1141. if ((AfterScan == NULL) || (AfterScan == *Contents) ||
  1142. (AfterScan >= *Contents + *Size)) {
  1143. return FALSE;
  1144. }
  1145. *Size = (*Contents + *Size) - AfterScan;
  1146. *Contents = AfterScan;
  1147. return TRUE;
  1148. }
  1149. PSTR
  1150. CkpBundleGetTemporaryDirectory (
  1151. VOID
  1152. )
  1153. /*++
  1154. Routine Description:
  1155. This routine creates a temporary directory.
  1156. Arguments:
  1157. None.
  1158. Return Value:
  1159. Returns a pointer to the temporary directory path on success.
  1160. NULL on failure.
  1161. --*/
  1162. {
  1163. INT Digit;
  1164. INT Index;
  1165. INT Length;
  1166. PSTR Temp;
  1167. INT Try;
  1168. if (*CkBundleDirectory != '\0') {
  1169. return CkBundleDirectory;
  1170. }
  1171. Temp = getenv("TMPDIR");
  1172. if (Temp == NULL) {
  1173. Temp = getenv("TEMP");
  1174. if (Temp == NULL) {
  1175. Temp = getenv("TMP");
  1176. }
  1177. }
  1178. if (Temp == NULL) {
  1179. Temp = "/tmp";
  1180. }
  1181. Length = snprintf(CkBundleDirectory,
  1182. sizeof(CkBundleDirectory),
  1183. "%s/ck",
  1184. Temp);
  1185. if ((Length < 0) || (Length + 9 > sizeof(CkBundleDirectory))) {
  1186. return NULL;
  1187. }
  1188. srand(time(NULL) ^ getpid());
  1189. for (Try = 0; Try < 100; Try += 1) {
  1190. for (Index = 0; Index < 8; Index += 1) {
  1191. Digit = rand() % 36;
  1192. if (Digit < 10) {
  1193. Digit += '0';
  1194. } else {
  1195. Digit = Digit - 10 + 'A';
  1196. }
  1197. CkBundleDirectory[Length + Index] = Digit;
  1198. }
  1199. CkBundleDirectory[Length + Index] = '\0';
  1200. if (mkdir(CkBundleDirectory, 0755) == 0) {
  1201. return CkBundleDirectory;
  1202. }
  1203. if (errno != EEXIST) {
  1204. return NULL;
  1205. }
  1206. }
  1207. return NULL;
  1208. }