You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TestPOIFSStream.java 118KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.poifs.filesystem;
  16. import static org.apache.poi.POIDataSamples.writeOutAndReadBack;
  17. import static org.hamcrest.CoreMatchers.hasItem;
  18. import static org.hamcrest.MatcherAssert.assertThat;
  19. import static org.hamcrest.core.IsEqual.equalTo;
  20. import static org.junit.jupiter.api.Assertions.assertEquals;
  21. import static org.junit.jupiter.api.Assertions.assertFalse;
  22. import static org.junit.jupiter.api.Assertions.assertNotNull;
  23. import static org.junit.jupiter.api.Assertions.assertNull;
  24. import static org.junit.jupiter.api.Assertions.assertThrows;
  25. import static org.junit.jupiter.api.Assertions.assertTrue;
  26. import static org.junit.jupiter.api.Assertions.fail;
  27. import static org.junit.jupiter.api.Assumptions.assumeTrue;
  28. import java.io.ByteArrayInputStream;
  29. import java.io.File;
  30. import java.io.FileOutputStream;
  31. import java.io.IOException;
  32. import java.io.InputStream;
  33. import java.io.OutputStream;
  34. import java.nio.ByteBuffer;
  35. import java.util.Arrays;
  36. import java.util.Collection;
  37. import java.util.Iterator;
  38. import java.util.List;
  39. import java.util.NoSuchElementException;
  40. import java.util.function.Function;
  41. import org.apache.commons.collections4.CollectionUtils;
  42. import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
  43. import org.apache.poi.POIDataSamples;
  44. import org.apache.poi.hpsf.DocumentSummaryInformation;
  45. import org.apache.poi.hpsf.NoPropertySetStreamException;
  46. import org.apache.poi.hpsf.PropertySet;
  47. import org.apache.poi.hpsf.PropertySetFactory;
  48. import org.apache.poi.hpsf.SummaryInformation;
  49. import org.apache.poi.poifs.common.POIFSConstants;
  50. import org.apache.poi.poifs.property.DirectoryProperty;
  51. import org.apache.poi.poifs.property.Property;
  52. import org.apache.poi.poifs.property.PropertyTable;
  53. import org.apache.poi.poifs.property.RootProperty;
  54. import org.apache.poi.poifs.storage.BATBlock;
  55. import org.apache.poi.poifs.storage.HeaderBlock;
  56. import org.apache.poi.util.IOUtils;
  57. import org.apache.poi.util.TempFile;
  58. import org.junit.jupiter.api.Disabled;
  59. import org.junit.jupiter.api.Test;
  60. import org.junit.jupiter.params.ParameterizedTest;
  61. import org.junit.jupiter.params.provider.Arguments;
  62. import org.junit.jupiter.params.provider.MethodSource;
  63. /**
  64. * Tests {@link POIFSStream}
  65. */
  66. final class TestPOIFSStream {
  67. private static final POIDataSamples _inst = POIDataSamples.getPOIFSInstance();
  68. /**
  69. * Read a single block stream
  70. */
  71. @Test
  72. void testReadTinyStream() throws Exception {
  73. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.getFile("BlockSize512.zvi"))) {
  74. // 98 is actually the last block in a two block stream...
  75. POIFSStream stream = new POIFSStream(fs, 98);
  76. Iterator<ByteBuffer> i = stream.getBlockIterator();
  77. assertTrue(i.hasNext());
  78. ByteBuffer b = i.next();
  79. assertFalse(i.hasNext());
  80. // Check the contents
  81. assertEquals((byte) 0x81, b.get());
  82. assertEquals((byte) 0x00, b.get());
  83. assertEquals((byte) 0x00, b.get());
  84. assertEquals((byte) 0x00, b.get());
  85. assertEquals((byte) 0x82, b.get());
  86. assertEquals((byte) 0x00, b.get());
  87. assertEquals((byte) 0x00, b.get());
  88. assertEquals((byte) 0x00, b.get());
  89. }
  90. }
  91. /**
  92. * Read a stream with only two blocks in it
  93. */
  94. @Test
  95. void testReadShortStream() throws Exception {
  96. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.getFile("BlockSize512.zvi"))) {
  97. // 97 -> 98 -> end
  98. POIFSStream stream = new POIFSStream(fs, 97);
  99. Iterator<ByteBuffer> i = stream.getBlockIterator();
  100. assertTrue(i.hasNext());
  101. ByteBuffer b97 = i.next();
  102. assertTrue(i.hasNext());
  103. ByteBuffer b98 = i.next();
  104. assertFalse(i.hasNext());
  105. // Check the contents of the 1st block
  106. assertEquals((byte) 0x01, b97.get());
  107. assertEquals((byte) 0x00, b97.get());
  108. assertEquals((byte) 0x00, b97.get());
  109. assertEquals((byte) 0x00, b97.get());
  110. assertEquals((byte) 0x02, b97.get());
  111. assertEquals((byte) 0x00, b97.get());
  112. assertEquals((byte) 0x00, b97.get());
  113. assertEquals((byte) 0x00, b97.get());
  114. // Check the contents of the 2nd block
  115. assertEquals((byte) 0x81, b98.get());
  116. assertEquals((byte) 0x00, b98.get());
  117. assertEquals((byte) 0x00, b98.get());
  118. assertEquals((byte) 0x00, b98.get());
  119. assertEquals((byte) 0x82, b98.get());
  120. assertEquals((byte) 0x00, b98.get());
  121. assertEquals((byte) 0x00, b98.get());
  122. assertEquals((byte) 0x00, b98.get());
  123. }
  124. }
  125. /**
  126. * Read a stream with many blocks
  127. */
  128. @Test
  129. void testReadLongerStream() throws Exception {
  130. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.getFile("BlockSize512.zvi"))) {
  131. ByteBuffer b0 = null;
  132. ByteBuffer b1 = null;
  133. ByteBuffer b22 = null;
  134. // The stream at 0 has 23 blocks in it
  135. POIFSStream stream = new POIFSStream(fs, 0);
  136. Iterator<ByteBuffer> i = stream.getBlockIterator();
  137. int count = 0;
  138. while (i.hasNext()) {
  139. ByteBuffer b = i.next();
  140. if (count == 0) {
  141. b0 = b;
  142. }
  143. if (count == 1) {
  144. b1 = b;
  145. }
  146. if (count == 22) {
  147. b22 = b;
  148. }
  149. count++;
  150. }
  151. assertEquals(23, count);
  152. // Check the contents
  153. // 1st block is at 0
  154. assertNotNull(b0);
  155. assertEquals((byte) 0x9e, b0.get());
  156. assertEquals((byte) 0x75, b0.get());
  157. assertEquals((byte) 0x97, b0.get());
  158. assertEquals((byte) 0xf6, b0.get());
  159. // 2nd block is at 1
  160. assertNotNull(b1);
  161. assertEquals((byte) 0x86, b1.get());
  162. assertEquals((byte) 0x09, b1.get());
  163. assertEquals((byte) 0x22, b1.get());
  164. assertEquals((byte) 0xfb, b1.get());
  165. // last block is at 89
  166. assertNotNull(b22);
  167. assertEquals((byte) 0xfe, b22.get());
  168. assertEquals((byte) 0xff, b22.get());
  169. assertEquals((byte) 0x00, b22.get());
  170. assertEquals((byte) 0x00, b22.get());
  171. assertEquals((byte) 0x05, b22.get());
  172. assertEquals((byte) 0x01, b22.get());
  173. assertEquals((byte) 0x02, b22.get());
  174. assertEquals((byte) 0x00, b22.get());
  175. }
  176. }
  177. /**
  178. * Read a stream with several blocks in a 4096 byte block file
  179. */
  180. @Test
  181. void testReadStream4096() throws Exception {
  182. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.getFile("BlockSize4096.zvi"))) {
  183. // 0 -> 1 -> 2 -> end
  184. POIFSStream stream = new POIFSStream(fs, 0);
  185. Iterator<ByteBuffer> i = stream.getBlockIterator();
  186. assertTrue(i.hasNext());
  187. ByteBuffer b0 = i.next();
  188. assertTrue(i.hasNext());
  189. ByteBuffer b1 = i.next();
  190. assertTrue(i.hasNext());
  191. ByteBuffer b2 = i.next();
  192. assertFalse(i.hasNext());
  193. // Check the contents of the 1st block
  194. assertEquals((byte) 0x9E, b0.get());
  195. assertEquals((byte) 0x75, b0.get());
  196. assertEquals((byte) 0x97, b0.get());
  197. assertEquals((byte) 0xF6, b0.get());
  198. assertEquals((byte) 0xFF, b0.get());
  199. assertEquals((byte) 0x21, b0.get());
  200. assertEquals((byte) 0xD2, b0.get());
  201. assertEquals((byte) 0x11, b0.get());
  202. // Check the contents of the 2nd block
  203. assertEquals((byte) 0x00, b1.get());
  204. assertEquals((byte) 0x00, b1.get());
  205. assertEquals((byte) 0x03, b1.get());
  206. assertEquals((byte) 0x00, b1.get());
  207. assertEquals((byte) 0x00, b1.get());
  208. assertEquals((byte) 0x00, b1.get());
  209. assertEquals((byte) 0x00, b1.get());
  210. assertEquals((byte) 0x00, b1.get());
  211. // Check the contents of the 3rd block
  212. assertEquals((byte) 0x6D, b2.get());
  213. assertEquals((byte) 0x00, b2.get());
  214. assertEquals((byte) 0x00, b2.get());
  215. assertEquals((byte) 0x00, b2.get());
  216. assertEquals((byte) 0x03, b2.get());
  217. assertEquals((byte) 0x00, b2.get());
  218. assertEquals((byte) 0x46, b2.get());
  219. assertEquals((byte) 0x00, b2.get());
  220. }
  221. }
  222. /**
  223. * Craft a nasty file with a loop, and ensure we don't get stuck
  224. */
  225. @Test
  226. void testReadFailsOnLoop() throws Exception {
  227. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.getFile("BlockSize512.zvi"))) {
  228. // Hack the FAT so that it goes 0->1->2->0
  229. fs.setNextBlock(0, 1);
  230. fs.setNextBlock(1, 2);
  231. fs.setNextBlock(2, 0);
  232. // Now try to read
  233. POIFSStream stream = new POIFSStream(fs, 0);
  234. Iterator<ByteBuffer> i = stream.getBlockIterator();
  235. assertTrue(i.hasNext());
  236. // 1st read works
  237. i.next();
  238. assertTrue(i.hasNext());
  239. // 2nd read works
  240. i.next();
  241. assertTrue(i.hasNext());
  242. // 3rd read works
  243. i.next();
  244. assertTrue(i.hasNext());
  245. // 4th read blows up as it loops back to 0
  246. assertThrows(RuntimeException.class, i::next, "Loop should have been detected but wasn't!");
  247. assertTrue(i.hasNext());
  248. }
  249. }
  250. /**
  251. * Tests that we can load some streams that are
  252. * stored in the mini stream.
  253. */
  254. @Test
  255. void testReadMiniStreams() throws Exception {
  256. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"))) {
  257. POIFSMiniStore ministore = fs.getMiniStore();
  258. // 178 -> 179 -> 180 -> end
  259. POIFSStream stream = new POIFSStream(ministore, 178);
  260. Iterator<ByteBuffer> i = stream.getBlockIterator();
  261. assertTrue(i.hasNext());
  262. ByteBuffer b178 = i.next();
  263. assertTrue(i.hasNext());
  264. ByteBuffer b179 = i.next();
  265. assertTrue(i.hasNext());
  266. ByteBuffer b180 = i.next();
  267. assertFalse(i.hasNext());
  268. // Check the contents of the 1st block
  269. assertEquals((byte) 0xfe, b178.get());
  270. assertEquals((byte) 0xff, b178.get());
  271. assertEquals((byte) 0x00, b178.get());
  272. assertEquals((byte) 0x00, b178.get());
  273. assertEquals((byte) 0x05, b178.get());
  274. assertEquals((byte) 0x01, b178.get());
  275. assertEquals((byte) 0x02, b178.get());
  276. assertEquals((byte) 0x00, b178.get());
  277. // And the 2nd
  278. assertEquals((byte) 0x6c, b179.get());
  279. assertEquals((byte) 0x00, b179.get());
  280. assertEquals((byte) 0x00, b179.get());
  281. assertEquals((byte) 0x00, b179.get());
  282. assertEquals((byte) 0x28, b179.get());
  283. assertEquals((byte) 0x00, b179.get());
  284. assertEquals((byte) 0x00, b179.get());
  285. assertEquals((byte) 0x00, b179.get());
  286. // And the 3rd
  287. assertEquals((byte) 0x30, b180.get());
  288. assertEquals((byte) 0x00, b180.get());
  289. assertEquals((byte) 0x00, b180.get());
  290. assertEquals((byte) 0x00, b180.get());
  291. assertEquals((byte) 0x00, b180.get());
  292. assertEquals((byte) 0x00, b180.get());
  293. assertEquals((byte) 0x00, b180.get());
  294. assertEquals((byte) 0x80, b180.get());
  295. }
  296. }
  297. /**
  298. * Writing the same amount of data as before
  299. */
  300. @Test
  301. void testReplaceStream() throws Exception {
  302. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"))) {
  303. byte[] data = new byte[512];
  304. for (int i = 0; i < data.length; i++) {
  305. data[i] = (byte) (i % 256);
  306. }
  307. // 98 is actually the last block in a two block stream...
  308. POIFSStream stream = new POIFSStream(fs, 98);
  309. stream.updateContents(data);
  310. // Check the reading of blocks
  311. Iterator<ByteBuffer> it = stream.getBlockIterator();
  312. assertTrue(it.hasNext());
  313. ByteBuffer b = it.next();
  314. assertFalse(it.hasNext());
  315. // Now check the contents
  316. data = new byte[512];
  317. b.get(data);
  318. for (int i = 0; i < data.length; i++) {
  319. byte exp = (byte) (i % 256);
  320. assertEquals(exp, data[i]);
  321. }
  322. }
  323. }
  324. /**
  325. * Writes less data than before, some blocks will need
  326. * to be freed
  327. */
  328. @Test
  329. void testReplaceStreamWithLess() throws Exception {
  330. try (InputStream is = _inst.openResourceAsStream("BlockSize512.zvi");
  331. POIFSFileSystem fs = new POIFSFileSystem(is)) {
  332. byte[] data = new byte[512];
  333. for (int i = 0; i < data.length; i++) {
  334. data[i] = (byte) (i % 256);
  335. }
  336. // 97 -> 98 -> end
  337. assertEquals(98, fs.getNextBlock(97));
  338. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(98));
  339. // Create a 2 block stream, will become a 1 block one
  340. POIFSStream stream = new POIFSStream(fs, 97);
  341. stream.updateContents(data);
  342. // 97 should now be the end, and 98 free
  343. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(97));
  344. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(98));
  345. // Check the reading of blocks
  346. Iterator<ByteBuffer> it = stream.getBlockIterator();
  347. assertTrue(it.hasNext());
  348. ByteBuffer b = it.next();
  349. assertFalse(it.hasNext());
  350. // Now check the contents
  351. data = new byte[512];
  352. b.get(data);
  353. for (int i = 0; i < data.length; i++) {
  354. byte exp = (byte) (i % 256);
  355. assertEquals(exp, data[i]);
  356. }
  357. }
  358. }
  359. /**
  360. * Writes more data than before, new blocks will be needed
  361. */
  362. @Test
  363. void testReplaceStreamWithMore() throws Exception {
  364. try (InputStream is = _inst.openResourceAsStream("BlockSize512.zvi");
  365. POIFSFileSystem fs = new POIFSFileSystem(is)) {
  366. byte[] data = new byte[512 * 3];
  367. for (int i = 0; i < data.length; i++) {
  368. data[i] = (byte) (i % 256);
  369. }
  370. // 97 -> 98 -> end
  371. assertEquals(98, fs.getNextBlock(97));
  372. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(98));
  373. // 100 is our first free one
  374. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99));
  375. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(100));
  376. // Create a 2 block stream, will become a 3 block one
  377. POIFSStream stream = new POIFSStream(fs, 97);
  378. stream.updateContents(data);
  379. // 97 -> 98 -> 100 -> end
  380. assertEquals(98, fs.getNextBlock(97));
  381. assertEquals(100, fs.getNextBlock(98));
  382. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(100));
  383. // Check the reading of blocks
  384. Iterator<ByteBuffer> it = stream.getBlockIterator();
  385. int count = 0;
  386. while (it.hasNext()) {
  387. ByteBuffer b = it.next();
  388. data = new byte[512];
  389. b.get(data);
  390. for (int i = 0; i < data.length; i++) {
  391. byte exp = (byte) (i % 256);
  392. assertEquals(exp, data[i]);
  393. }
  394. count++;
  395. }
  396. assertEquals(3, count);
  397. }
  398. }
  399. /**
  400. * Writes to a new stream in the file
  401. */
  402. @Test
  403. void testWriteNewStream() throws Exception {
  404. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"))) {
  405. // 100 is our first free one
  406. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99));
  407. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(100));
  408. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(101));
  409. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(102));
  410. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(103));
  411. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(104));
  412. // Add a single block one
  413. byte[] data = new byte[512];
  414. for (int i = 0; i < data.length; i++) {
  415. data[i] = (byte) (i % 256);
  416. }
  417. POIFSStream stream = new POIFSStream(fs);
  418. stream.updateContents(data);
  419. // Check it was allocated properly
  420. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99));
  421. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(100));
  422. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(101));
  423. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(102));
  424. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(103));
  425. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(104));
  426. // And check the contents
  427. Iterator<ByteBuffer> it = stream.getBlockIterator();
  428. int count = 0;
  429. while (it.hasNext()) {
  430. ByteBuffer b = it.next();
  431. data = new byte[512];
  432. b.get(data);
  433. for (int i = 0; i < data.length; i++) {
  434. byte exp = (byte) (i % 256);
  435. assertEquals(exp, data[i]);
  436. }
  437. count++;
  438. }
  439. assertEquals(1, count);
  440. // And a multi block one
  441. data = new byte[512 * 3];
  442. for (int i = 0; i < data.length; i++) {
  443. data[i] = (byte) (i % 256);
  444. }
  445. stream = new POIFSStream(fs);
  446. stream.updateContents(data);
  447. // Check it was allocated properly
  448. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99));
  449. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(100));
  450. assertEquals(102, fs.getNextBlock(101));
  451. assertEquals(103, fs.getNextBlock(102));
  452. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(103));
  453. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(104));
  454. // And check the contents
  455. it = stream.getBlockIterator();
  456. count = 0;
  457. while (it.hasNext()) {
  458. ByteBuffer b = it.next();
  459. data = new byte[512];
  460. b.get(data);
  461. for (int i = 0; i < data.length; i++) {
  462. byte exp = (byte) (i % 256);
  463. assertEquals(exp, data[i]);
  464. }
  465. count++;
  466. }
  467. assertEquals(3, count);
  468. // Free it
  469. stream.free();
  470. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99));
  471. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(100));
  472. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(101));
  473. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(102));
  474. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(103));
  475. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(104));
  476. }
  477. }
  478. /**
  479. * Writes to a new stream in the file, where we've not enough
  480. * free blocks so new FAT segments will need to be allocated
  481. * to support this
  482. */
  483. @Test
  484. void testWriteNewStreamExtraFATs() throws Exception {
  485. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"))) {
  486. // Allocate almost all the blocks
  487. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99));
  488. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(100));
  489. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(127));
  490. for (int i = 100; i < 127; i++) {
  491. fs.setNextBlock(i, POIFSConstants.END_OF_CHAIN);
  492. }
  493. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(127));
  494. assertTrue(fs.getBATBlockAndIndex(0).getBlock().hasFreeSectors());
  495. // Write a 3 block stream
  496. byte[] data = new byte[512 * 3];
  497. for (int i = 0; i < data.length; i++) {
  498. data[i] = (byte) (i % 256);
  499. }
  500. POIFSStream stream = new POIFSStream(fs);
  501. stream.updateContents(data);
  502. // Check we got another BAT
  503. assertFalse(fs.getBATBlockAndIndex(0).getBlock().hasFreeSectors());
  504. assertTrue(fs.getBATBlockAndIndex(128).getBlock().hasFreeSectors());
  505. // the BAT will be in the first spot of the new block
  506. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(126));
  507. assertEquals(129, fs.getNextBlock(127));
  508. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(128));
  509. assertEquals(130, fs.getNextBlock(129));
  510. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(130));
  511. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(131));
  512. }
  513. }
  514. /**
  515. * Replaces data in an existing stream, with a bit
  516. * more data than before, in a 4096 byte block file
  517. */
  518. @Test
  519. void testWriteStream4096() throws Exception {
  520. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.openResourceAsStream("BlockSize4096.zvi"))) {
  521. // 0 -> 1 -> 2 -> end
  522. assertEquals(1, fs.getNextBlock(0));
  523. assertEquals(2, fs.getNextBlock(1));
  524. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(2));
  525. assertEquals(4, fs.getNextBlock(3));
  526. // First free one is at 15
  527. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(14));
  528. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(15));
  529. // Write a 5 block file
  530. byte[] data = new byte[4096 * 5];
  531. for (int i = 0; i < data.length; i++) {
  532. data[i] = (byte) (i % 256);
  533. }
  534. POIFSStream stream = new POIFSStream(fs, 0);
  535. stream.updateContents(data);
  536. // Check it
  537. assertEquals(1, fs.getNextBlock(0));
  538. assertEquals(2, fs.getNextBlock(1));
  539. assertEquals(15, fs.getNextBlock(2)); // Jumps
  540. assertEquals(4, fs.getNextBlock(3)); // Next stream
  541. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(14));
  542. assertEquals(16, fs.getNextBlock(15)); // Continues
  543. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(16)); // Ends
  544. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(17)); // Free
  545. // Check the contents too
  546. Iterator<ByteBuffer> it = stream.getBlockIterator();
  547. int count = 0;
  548. while (it.hasNext()) {
  549. ByteBuffer b = it.next();
  550. data = new byte[512];
  551. b.get(data);
  552. for (int i = 0; i < data.length; i++) {
  553. byte exp = (byte) (i % 256);
  554. assertEquals(exp, data[i]);
  555. }
  556. count++;
  557. }
  558. assertEquals(5, count);
  559. }
  560. }
  561. /**
  562. * Tests that we can write into the mini stream
  563. */
  564. @Test
  565. void testWriteMiniStreams() throws Exception {
  566. try (InputStream is = _inst.openResourceAsStream("BlockSize512.zvi");
  567. POIFSFileSystem fs = new POIFSFileSystem(is)) {
  568. POIFSMiniStore ministore = fs.getMiniStore();
  569. // 178 -> 179 -> 180 -> end
  570. assertEquals(179, ministore.getNextBlock(178));
  571. assertEquals(180, ministore.getNextBlock(179));
  572. assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(180));
  573. // Try writing 3 full blocks worth
  574. byte[] data = new byte[64 * 3];
  575. for (int i = 0; i < data.length; i++) {
  576. data[i] = (byte) i;
  577. }
  578. POIFSStream stream = new POIFSStream(ministore, 178);
  579. stream.updateContents(data);
  580. // Check
  581. assertEquals(179, ministore.getNextBlock(178));
  582. assertEquals(180, ministore.getNextBlock(179));
  583. assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(180));
  584. stream = new POIFSStream(ministore, 178);
  585. Iterator<ByteBuffer> it = stream.getBlockIterator();
  586. ByteBuffer b178 = it.next();
  587. ByteBuffer b179 = it.next();
  588. ByteBuffer b180 = it.next();
  589. assertFalse(it.hasNext());
  590. assertEquals((byte) 0x00, b178.get());
  591. assertEquals((byte) 0x01, b178.get());
  592. assertEquals((byte) 0x40, b179.get());
  593. assertEquals((byte) 0x41, b179.get());
  594. assertEquals((byte) 0x80, b180.get());
  595. assertEquals((byte) 0x81, b180.get());
  596. // Try writing just into 3 blocks worth
  597. data = new byte[64 * 2 + 12];
  598. for (int i = 0; i < data.length; i++) {
  599. data[i] = (byte) (i + 4);
  600. }
  601. stream = new POIFSStream(ministore, 178);
  602. stream.updateContents(data);
  603. // Check
  604. assertEquals(179, ministore.getNextBlock(178));
  605. assertEquals(180, ministore.getNextBlock(179));
  606. assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(180));
  607. stream = new POIFSStream(ministore, 178);
  608. it = stream.getBlockIterator();
  609. b178 = it.next();
  610. b179 = it.next();
  611. b180 = it.next();
  612. assertFalse(it.hasNext());
  613. assertEquals((byte) 0x04, b178.get(0));
  614. assertEquals((byte) 0x05, b178.get(1));
  615. assertEquals((byte) 0x44, b179.get(0));
  616. assertEquals((byte) 0x45, b179.get(1));
  617. assertEquals((byte) 0x84, b180.get(0));
  618. assertEquals((byte) 0x85, b180.get(1));
  619. // Try writing 1, should truncate
  620. data = new byte[12];
  621. for (int i = 0; i < data.length; i++) {
  622. data[i] = (byte) (i + 9);
  623. }
  624. stream = new POIFSStream(ministore, 178);
  625. stream.updateContents(data);
  626. assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(178));
  627. assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(179));
  628. assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(180));
  629. stream = new POIFSStream(ministore, 178);
  630. it = stream.getBlockIterator();
  631. b178 = it.next();
  632. assertFalse(it.hasNext());
  633. assertEquals((byte) 0x09, b178.get(0));
  634. assertEquals((byte) 0x0a, b178.get(1));
  635. // Try writing 5, should extend
  636. assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(178));
  637. assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(179));
  638. assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(180));
  639. assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(181));
  640. assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(182));
  641. assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(183));
  642. data = new byte[64 * 4 + 12];
  643. for (int i = 0; i < data.length; i++) {
  644. data[i] = (byte) (i + 3);
  645. }
  646. stream = new POIFSStream(ministore, 178);
  647. stream.updateContents(data);
  648. assertEquals(179, ministore.getNextBlock(178));
  649. assertEquals(180, ministore.getNextBlock(179));
  650. assertEquals(181, ministore.getNextBlock(180));
  651. assertEquals(182, ministore.getNextBlock(181));
  652. assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(182));
  653. stream = new POIFSStream(ministore, 178);
  654. it = stream.getBlockIterator();
  655. b178 = it.next();
  656. b179 = it.next();
  657. b180 = it.next();
  658. ByteBuffer b181 = it.next();
  659. ByteBuffer b182 = it.next();
  660. assertFalse(it.hasNext());
  661. assertEquals((byte) 0x03, b178.get(0));
  662. assertEquals((byte) 0x04, b178.get(1));
  663. assertEquals((byte) 0x43, b179.get(0));
  664. assertEquals((byte) 0x44, b179.get(1));
  665. assertEquals((byte) 0x83, b180.get(0));
  666. assertEquals((byte) 0x84, b180.get(1));
  667. assertEquals((byte) 0xc3, b181.get(0));
  668. assertEquals((byte) 0xc4, b181.get(1));
  669. assertEquals((byte) 0x03, b182.get(0));
  670. assertEquals((byte) 0x04, b182.get(1));
  671. // Write lots, so it needs another big block
  672. ministore.getBlockAt(183);
  673. assertThrows(NoSuchElementException.class, () -> ministore.getBlockAt(184), "Block 184 should be off the end of the list");
  674. data = new byte[64 * 6 + 12];
  675. for (int i = 0; i < data.length; i++) {
  676. data[i] = (byte) (i + 1);
  677. }
  678. stream = new POIFSStream(ministore, 178);
  679. stream.updateContents(data);
  680. // Should have added 2 more blocks to the chain
  681. assertEquals(179, ministore.getNextBlock(178));
  682. assertEquals(180, ministore.getNextBlock(179));
  683. assertEquals(181, ministore.getNextBlock(180));
  684. assertEquals(182, ministore.getNextBlock(181));
  685. assertEquals(183, ministore.getNextBlock(182));
  686. assertEquals(184, ministore.getNextBlock(183));
  687. assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(184));
  688. assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(185));
  689. // Block 184 should exist
  690. ministore.getBlockAt(183);
  691. ministore.getBlockAt(184);
  692. ministore.getBlockAt(185);
  693. // Check contents
  694. stream = new POIFSStream(ministore, 178);
  695. it = stream.getBlockIterator();
  696. b178 = it.next();
  697. b179 = it.next();
  698. b180 = it.next();
  699. b181 = it.next();
  700. b182 = it.next();
  701. ByteBuffer b183 = it.next();
  702. ByteBuffer b184 = it.next();
  703. assertFalse(it.hasNext());
  704. assertEquals((byte) 0x01, b178.get(0));
  705. assertEquals((byte) 0x02, b178.get(1));
  706. assertEquals((byte) 0x41, b179.get(0));
  707. assertEquals((byte) 0x42, b179.get(1));
  708. assertEquals((byte) 0x81, b180.get(0));
  709. assertEquals((byte) 0x82, b180.get(1));
  710. assertEquals((byte) 0xc1, b181.get(0));
  711. assertEquals((byte) 0xc2, b181.get(1));
  712. assertEquals((byte) 0x01, b182.get(0));
  713. assertEquals((byte) 0x02, b182.get(1));
  714. assertEquals((byte) 0x41, b183.get(0));
  715. assertEquals((byte) 0x42, b183.get(1));
  716. assertEquals((byte) 0x81, b184.get(0));
  717. assertEquals((byte) 0x82, b184.get(1));
  718. }
  719. }
  720. /**
  721. * Craft a nasty file with a loop, and ensure we don't get stuck
  722. */
  723. @Test
  724. void testWriteFailsOnLoop() throws Exception {
  725. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.getFile("BlockSize512.zvi"))) {
  726. // Hack the FAT so that it goes 0->1->2->0
  727. fs.setNextBlock(0, 1);
  728. fs.setNextBlock(1, 2);
  729. fs.setNextBlock(2, 0);
  730. // Try to write a large amount, should fail on the write
  731. POIFSStream stream1 = new POIFSStream(fs, 0);
  732. assertThrows(IllegalStateException.class,
  733. () -> stream1.updateContents(new byte[512 * 4]), "Loop should have been detected but wasn't!");
  734. // Now reset, and try on a small bit
  735. // Should fail during the freeing set
  736. fs.setNextBlock(0, 1);
  737. fs.setNextBlock(1, 2);
  738. fs.setNextBlock(2, 0);
  739. POIFSStream stream2 = new POIFSStream(fs, 0);
  740. assertThrows(IllegalStateException.class,
  741. () -> stream2.updateContents(new byte[512]), "Loop should have been detected but wasn't!");
  742. }
  743. }
  744. /**
  745. * Tests adding a new stream, writing and reading it.
  746. */
  747. @Test
  748. void testReadWriteNewStream() throws Exception {
  749. try (POIFSFileSystem fs = new POIFSFileSystem()) {
  750. POIFSStream stream = new POIFSStream(fs);
  751. // Check our filesystem has Properties then BAT
  752. assertEquals(2, fs.getFreeBlock());
  753. BATBlock bat = fs.getBATBlockAndIndex(0).getBlock();
  754. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0));
  755. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1));
  756. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(2));
  757. // Check the stream as-is
  758. assertEquals(POIFSConstants.END_OF_CHAIN, stream.getStartBlock());
  759. assertThrows(IllegalStateException.class, stream::getBlockIterator,
  760. "Shouldn't be able to get an iterator before writing");
  761. // Write in two blocks
  762. byte[] data = new byte[512 + 20];
  763. for (int i = 0; i < 512; i++) {
  764. data[i] = (byte) (i % 256);
  765. }
  766. for (int i = 512; i < data.length; i++) {
  767. data[i] = (byte) (i % 256 + 100);
  768. }
  769. stream.updateContents(data);
  770. // Check now
  771. assertEquals(4, fs.getFreeBlock());
  772. bat = fs.getBATBlockAndIndex(0).getBlock();
  773. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0));
  774. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1));
  775. assertEquals(3, bat.getValueAt(2));
  776. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(3));
  777. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(4));
  778. Iterator<ByteBuffer> it = stream.getBlockIterator();
  779. assertTrue(it.hasNext());
  780. ByteBuffer b = it.next();
  781. byte[] read = new byte[512];
  782. b.get(read);
  783. for (int i = 0; i < read.length; i++) {
  784. assertEquals(data[i], read[i], "Wrong value at " + i);
  785. }
  786. assertTrue(it.hasNext());
  787. b = it.next();
  788. read = new byte[512];
  789. b.get(read);
  790. for (int i = 0; i < 20; i++) {
  791. assertEquals(data[i + 512], read[i]);
  792. }
  793. for (int i = 20; i < read.length; i++) {
  794. assertEquals(0, read[i]);
  795. }
  796. assertFalse(it.hasNext());
  797. }
  798. }
  799. /**
  800. * Writes a stream, then replaces it
  801. */
  802. @Test
  803. void testWriteThenReplace() throws Exception {
  804. try (POIFSFileSystem fs1 = new POIFSFileSystem()) {
  805. // Starts empty, other that Properties and BAT
  806. BATBlock bat = fs1.getBATBlockAndIndex(0).getBlock();
  807. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0));
  808. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1));
  809. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(2));
  810. // Write something that uses a main stream
  811. byte[] main4106 = new byte[4106];
  812. main4106[0] = -10;
  813. main4106[4105] = -11;
  814. fs1.getRoot().createDocument("Normal", new ByteArrayInputStream(main4106));
  815. // Should have used 9 blocks
  816. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0));
  817. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1));
  818. assertEquals(3, bat.getValueAt(2));
  819. assertEquals(4, bat.getValueAt(3));
  820. assertEquals(5, bat.getValueAt(4));
  821. assertEquals(6, bat.getValueAt(5));
  822. assertEquals(7, bat.getValueAt(6));
  823. assertEquals(8, bat.getValueAt(7));
  824. assertEquals(9, bat.getValueAt(8));
  825. assertEquals(10, bat.getValueAt(9));
  826. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(10));
  827. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(11));
  828. DocumentEntry normal = (DocumentEntry) fs1.getRoot().getEntry("Normal");
  829. assertEquals(4106, normal.getSize());
  830. assertEquals(4106, ((DocumentNode) normal).getProperty().getSize());
  831. // Replace with one still big enough for a main stream, but one block smaller
  832. byte[] main4096 = new byte[4096];
  833. main4096[0] = -10;
  834. main4096[4095] = -11;
  835. try (DocumentOutputStream nout = new DocumentOutputStream(normal)) {
  836. nout.write(main4096);
  837. }
  838. // Will have dropped to 8
  839. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0));
  840. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1));
  841. assertEquals(3, bat.getValueAt(2));
  842. assertEquals(4, bat.getValueAt(3));
  843. assertEquals(5, bat.getValueAt(4));
  844. assertEquals(6, bat.getValueAt(5));
  845. assertEquals(7, bat.getValueAt(6));
  846. assertEquals(8, bat.getValueAt(7));
  847. assertEquals(9, bat.getValueAt(8));
  848. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(9));
  849. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(10));
  850. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(11));
  851. normal = (DocumentEntry) fs1.getRoot().getEntry("Normal");
  852. assertEquals(4096, normal.getSize());
  853. assertEquals(4096, ((DocumentNode) normal).getProperty().getSize());
  854. // Write and check
  855. try (POIFSFileSystem fs2 = writeOutAndReadBack(fs1)) {
  856. bat = fs2.getBATBlockAndIndex(0).getBlock();
  857. // No change after write
  858. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); // Properties
  859. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1));
  860. assertEquals(3, bat.getValueAt(2));
  861. assertEquals(4, bat.getValueAt(3));
  862. assertEquals(5, bat.getValueAt(4));
  863. assertEquals(6, bat.getValueAt(5));
  864. assertEquals(7, bat.getValueAt(6));
  865. assertEquals(8, bat.getValueAt(7));
  866. assertEquals(9, bat.getValueAt(8));
  867. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(9)); // End of Normal
  868. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(10));
  869. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(11));
  870. normal = (DocumentEntry) fs2.getRoot().getEntry("Normal");
  871. assertEquals(4096, normal.getSize());
  872. assertEquals(4096, ((DocumentNode) normal).getProperty().getSize());
  873. // Make longer, take 1 block at the end
  874. normal = (DocumentEntry) fs2.getRoot().getEntry("Normal");
  875. try (DocumentOutputStream nout = new DocumentOutputStream(normal)) {
  876. nout.write(main4106);
  877. }
  878. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0));
  879. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1));
  880. assertEquals(3, bat.getValueAt(2));
  881. assertEquals(4, bat.getValueAt(3));
  882. assertEquals(5, bat.getValueAt(4));
  883. assertEquals(6, bat.getValueAt(5));
  884. assertEquals(7, bat.getValueAt(6));
  885. assertEquals(8, bat.getValueAt(7));
  886. assertEquals(9, bat.getValueAt(8));
  887. assertEquals(10, bat.getValueAt(9));
  888. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(10)); // Normal
  889. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(11));
  890. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(12));
  891. normal = (DocumentEntry) fs2.getRoot().getEntry("Normal");
  892. assertEquals(4106, normal.getSize());
  893. assertEquals(4106, ((DocumentNode) normal).getProperty().getSize());
  894. // Make it small, will trigger the SBAT stream and free lots up
  895. byte[] mini = new byte[]{42, 0, 1, 2, 3, 4, 42};
  896. normal = (DocumentEntry) fs2.getRoot().getEntry("Normal");
  897. try (DocumentOutputStream nout = new DocumentOutputStream(normal)) {
  898. nout.write(mini);
  899. }
  900. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0));
  901. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1));
  902. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(2)); // SBAT
  903. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(3)); // Mini Stream
  904. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(4));
  905. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(5));
  906. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(6));
  907. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(7));
  908. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(8));
  909. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(9));
  910. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(10));
  911. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(11));
  912. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(12));
  913. normal = (DocumentEntry) fs2.getRoot().getEntry("Normal");
  914. assertEquals(7, normal.getSize());
  915. assertEquals(7, ((DocumentNode) normal).getProperty().getSize());
  916. // Finally back to big again
  917. try (DocumentOutputStream nout = new DocumentOutputStream(normal)) {
  918. nout.write(main4096);
  919. }
  920. // Will keep the mini stream, now empty
  921. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0));
  922. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1));
  923. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(2)); // SBAT
  924. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(3)); // Mini Stream
  925. assertEquals(5, bat.getValueAt(4));
  926. assertEquals(6, bat.getValueAt(5));
  927. assertEquals(7, bat.getValueAt(6));
  928. assertEquals(8, bat.getValueAt(7));
  929. assertEquals(9, bat.getValueAt(8));
  930. assertEquals(10, bat.getValueAt(9));
  931. assertEquals(11, bat.getValueAt(10));
  932. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(11));
  933. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(12));
  934. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(13));
  935. normal = (DocumentEntry) fs2.getRoot().getEntry("Normal");
  936. assertEquals(4096, normal.getSize());
  937. assertEquals(4096, ((DocumentNode) normal).getProperty().getSize());
  938. // Save, re-load, re-check
  939. try (POIFSFileSystem fs3 = writeOutAndReadBack(fs2)) {
  940. bat = fs3.getBATBlockAndIndex(0).getBlock();
  941. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0));
  942. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1));
  943. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(2)); // SBAT
  944. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(3)); // Mini Stream
  945. assertEquals(5, bat.getValueAt(4));
  946. assertEquals(6, bat.getValueAt(5));
  947. assertEquals(7, bat.getValueAt(6));
  948. assertEquals(8, bat.getValueAt(7));
  949. assertEquals(9, bat.getValueAt(8));
  950. assertEquals(10, bat.getValueAt(9));
  951. assertEquals(11, bat.getValueAt(10));
  952. assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(11));
  953. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(12));
  954. assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(13));
  955. normal = (DocumentEntry) fs3.getRoot().getEntry("Normal");
  956. assertEquals(4096, normal.getSize());
  957. assertEquals(4096, ((DocumentNode) normal).getProperty().getSize());
  958. }
  959. }
  960. }
  961. }
  962. /**
  963. * Returns test files with 512 byte and 4k block sizes, loaded
  964. * both from InputStreams and Files
  965. */
  966. public static Collection<Arguments> get512and4kFileAndInput() {
  967. return CollectionUtils.union(get512FileAndInput(), get4kFileAndInput());
  968. }
  969. public static List<Arguments> get512FileAndInput() {
  970. return Arrays.asList(
  971. Arguments.of("BlockSize512.zvi", (Function<String,POIFSFileSystem>)TestPOIFSStream::openAsFile),
  972. Arguments.of("BlockSize512.zvi", (Function<String,POIFSFileSystem>)TestPOIFSStream::openAsStream)
  973. );
  974. }
  975. public static List<Arguments> get4kFileAndInput() {
  976. return Arrays.asList(
  977. Arguments.of("BlockSize4096.zvi", (Function<String,POIFSFileSystem>)TestPOIFSStream::openAsFile),
  978. Arguments.of("BlockSize4096.zvi", (Function<String,POIFSFileSystem>)TestPOIFSStream::openAsStream)
  979. );
  980. }
  981. private static POIFSFileSystem openAsFile(String fileName) {
  982. try {
  983. return new POIFSFileSystem(_inst.getFile(fileName));
  984. } catch (IOException e) {
  985. fail(e);
  986. return null;
  987. }
  988. }
  989. private static POIFSFileSystem openAsStream(String fileName) {
  990. try {
  991. return new POIFSFileSystem(_inst.openResourceAsStream(fileName));
  992. } catch (IOException e) {
  993. fail(e);
  994. return null;
  995. }
  996. }
  997. private static void assertBATCount(POIFSFileSystem fs, int expectedBAT, int expectedXBAT) throws IOException {
  998. int foundBAT = 0;
  999. int foundXBAT = 0;
  1000. int sz = (int) (fs.size() / fs.getBigBlockSize());
  1001. for (int i = 0; i < sz; i++) {
  1002. if (fs.getNextBlock(i) == POIFSConstants.FAT_SECTOR_BLOCK) {
  1003. foundBAT++;
  1004. }
  1005. if (fs.getNextBlock(i) == POIFSConstants.DIFAT_SECTOR_BLOCK) {
  1006. foundXBAT++;
  1007. }
  1008. }
  1009. assertEquals(expectedBAT, foundBAT, "Wrong number of BATs");
  1010. assertEquals(expectedXBAT, foundXBAT, "Wrong number of XBATs with " + expectedBAT + " BATs");
  1011. }
  1012. private void assertContentsMatches(byte[] expected, DocumentEntry doc) throws IOException {
  1013. DocumentInputStream inp = new DocumentInputStream(doc);
  1014. byte[] contents = new byte[doc.getSize()];
  1015. assertEquals(doc.getSize(), inp.read(contents));
  1016. inp.close();
  1017. if (expected != null) {
  1018. assertThat(expected, equalTo(contents));
  1019. }
  1020. }
  1021. private static HeaderBlock writeOutAndReadHeader(POIFSFileSystem fs) throws IOException {
  1022. UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
  1023. fs.writeFilesystem(baos);
  1024. return new HeaderBlock(baos.toInputStream());
  1025. }
  1026. private static POIFSFileSystem writeOutFileAndReadBack(POIFSFileSystem original) throws IOException {
  1027. final File file = TempFile.createTempFile("TestPOIFS", ".ole2");
  1028. try (OutputStream fout = new FileOutputStream(file)) {
  1029. original.writeFilesystem(fout);
  1030. }
  1031. return new POIFSFileSystem(file, false);
  1032. }
  1033. @ParameterizedTest()
  1034. @MethodSource("get512FileAndInput")
  1035. void basicOpen512(String file, Function<String,POIFSFileSystem> opener) throws IOException {
  1036. // With a simple 512 block file
  1037. try (POIFSFileSystem fs = opener.apply(file)) {
  1038. assertEquals(512, fs.getBigBlockSize());
  1039. }
  1040. }
  1041. @ParameterizedTest()
  1042. @MethodSource("get4kFileAndInput")
  1043. void basicOpen4k(String file, Function<String,POIFSFileSystem> opener) throws IOException {
  1044. // Now with a simple 4096 block file
  1045. try (POIFSFileSystem fs = opener.apply(file)) {
  1046. assertEquals(4096, fs.getBigBlockSize());
  1047. }
  1048. }
  1049. @ParameterizedTest()
  1050. @MethodSource("get512FileAndInput")
  1051. void propertiesAndFatOnRead512(String file, Function<String,POIFSFileSystem> opener) throws IOException {
  1052. // With a simple 512 block file
  1053. try (POIFSFileSystem fs = opener.apply(file)) {
  1054. // Check the FAT was properly processed:
  1055. // Verify we only got one block
  1056. fs.getBATBlockAndIndex(0);
  1057. fs.getBATBlockAndIndex(1);
  1058. assertThrows(IndexOutOfBoundsException.class, () -> fs.getBATBlockAndIndex(140),
  1059. "Should only be one BAT, but a 2nd was found");
  1060. // Verify a few next offsets
  1061. // 97 -> 98 -> END
  1062. assertEquals(98, fs.getNextBlock(97));
  1063. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(98));
  1064. // Check the properties
  1065. PropertyTable props = fs._get_property_table();
  1066. assertEquals(90, props.getStartBlock());
  1067. assertEquals(7, props.countBlocks());
  1068. // Root property tells us about the Mini Stream
  1069. RootProperty root = props.getRoot();
  1070. assertEquals("Root Entry", root.getName());
  1071. assertEquals(11564, root.getSize());
  1072. assertEquals(0, root.getStartBlock());
  1073. // Check its children too
  1074. Property prop;
  1075. Iterator<Property> pi = root.getChildren();
  1076. prop = pi.next();
  1077. assertEquals("Thumbnail", prop.getName());
  1078. prop = pi.next();
  1079. assertEquals("\u0005DocumentSummaryInformation", prop.getName());
  1080. prop = pi.next();
  1081. assertEquals("\u0005SummaryInformation", prop.getName());
  1082. prop = pi.next();
  1083. assertEquals("Image", prop.getName());
  1084. prop = pi.next();
  1085. assertEquals("Tags", prop.getName());
  1086. assertFalse(pi.hasNext());
  1087. // Check the SBAT (Small Blocks FAT) was properly processed
  1088. POIFSMiniStore ministore = fs.getMiniStore();
  1089. // Verify we only got two SBAT blocks
  1090. ministore.getBATBlockAndIndex(0);
  1091. ministore.getBATBlockAndIndex(128);
  1092. assertThrows(IndexOutOfBoundsException.class, () -> ministore.getBATBlockAndIndex(256),
  1093. "Should only be two SBATs, but a 3rd was found");
  1094. // Verify a few offsets: 0->50 is a stream
  1095. for (int i = 0; i < 50; i++) {
  1096. assertEquals(i + 1, ministore.getNextBlock(i));
  1097. }
  1098. assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(50));
  1099. }
  1100. }
  1101. @ParameterizedTest()
  1102. @MethodSource("get4kFileAndInput")
  1103. void propertiesAndFatOnRead4k(String file, Function<String,POIFSFileSystem> opener) throws IOException {
  1104. // Now with a simple 4096 block file
  1105. try (POIFSFileSystem fs = opener.apply(file)) {
  1106. // Check the FAT was properly processed
  1107. // Verify we only got one block
  1108. fs.getBATBlockAndIndex(0);
  1109. fs.getBATBlockAndIndex(1);
  1110. assertThrows(IndexOutOfBoundsException.class, () -> fs.getBATBlockAndIndex(1040),
  1111. "Should only be one BAT, but a 2nd was found");
  1112. // Verify a few next offsets
  1113. // 0 -> 1 -> 2 -> END
  1114. assertEquals(1, fs.getNextBlock(0));
  1115. assertEquals(2, fs.getNextBlock(1));
  1116. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(2));
  1117. // Check the properties
  1118. PropertyTable props = fs._get_property_table();
  1119. assertEquals(12, props.getStartBlock());
  1120. assertEquals(1, props.countBlocks());
  1121. // Root property tells us about the Mini Stream
  1122. RootProperty root = props.getRoot();
  1123. assertEquals("Root Entry", root.getName());
  1124. assertEquals(11564, root.getSize());
  1125. assertEquals(0, root.getStartBlock());
  1126. // Check its children too
  1127. Property prop;
  1128. Iterator<Property> pi = root.getChildren();
  1129. prop = pi.next();
  1130. assertEquals("Thumbnail", prop.getName());
  1131. prop = pi.next();
  1132. assertEquals("\u0005DocumentSummaryInformation", prop.getName());
  1133. prop = pi.next();
  1134. assertEquals("\u0005SummaryInformation", prop.getName());
  1135. prop = pi.next();
  1136. assertEquals("Image", prop.getName());
  1137. prop = pi.next();
  1138. assertEquals("Tags", prop.getName());
  1139. assertFalse(pi.hasNext());
  1140. // Check the SBAT (Small Blocks FAT) was properly processed
  1141. POIFSMiniStore ministore = fs.getMiniStore();
  1142. // Verify we only got one SBAT block
  1143. ministore.getBATBlockAndIndex(0);
  1144. ministore.getBATBlockAndIndex(128);
  1145. ministore.getBATBlockAndIndex(1023);
  1146. assertThrows(IndexOutOfBoundsException.class, () -> ministore.getBATBlockAndIndex(1024),
  1147. "Should only be one SBAT, but a 2nd was found");
  1148. // Verify a few offsets: 0->50 is a stream
  1149. for (int i = 0; i < 50; i++) {
  1150. assertEquals(i + 1, ministore.getNextBlock(i));
  1151. }
  1152. assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(50));
  1153. }
  1154. }
  1155. /**
  1156. * Check that for a given block, we can correctly figure
  1157. * out what the next one is
  1158. */
  1159. @ParameterizedTest()
  1160. @MethodSource("get512FileAndInput")
  1161. void nextBlock512(String file, Function<String,POIFSFileSystem> opener) throws IOException {
  1162. try (POIFSFileSystem fs = opener.apply(file)) {
  1163. // 0 -> 21 are simple
  1164. for (int i = 0; i < 21; i++) {
  1165. assertEquals(i + 1, fs.getNextBlock(i));
  1166. }
  1167. // 21 jumps to 89, then ends
  1168. assertEquals(89, fs.getNextBlock(21));
  1169. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(89));
  1170. // 22 -> 88 simple sequential stream
  1171. for (int i = 22; i < 88; i++) {
  1172. assertEquals(i + 1, fs.getNextBlock(i));
  1173. }
  1174. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(88));
  1175. // 90 -> 96 is another stream
  1176. for (int i = 90; i < 96; i++) {
  1177. assertEquals(i + 1, fs.getNextBlock(i));
  1178. }
  1179. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(96));
  1180. // 97+98 is another
  1181. assertEquals(98, fs.getNextBlock(97));
  1182. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(98));
  1183. // 99 is our FAT block
  1184. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99));
  1185. // 100 onwards is free
  1186. for (int i = 100; i < fs.getBigBlockSizeDetails().getBATEntriesPerBlock(); i++) {
  1187. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(i));
  1188. }
  1189. }
  1190. }
  1191. @ParameterizedTest()
  1192. @MethodSource("get4kFileAndInput")
  1193. void nextBlock4k(String file, Function<String,POIFSFileSystem> opener) throws IOException {
  1194. // Quick check on 4096 byte blocks too
  1195. try (POIFSFileSystem fs = opener.apply(file)) {
  1196. // 0 -> 1 -> 2 -> end
  1197. assertEquals(1, fs.getNextBlock(0));
  1198. assertEquals(2, fs.getNextBlock(1));
  1199. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(2));
  1200. // 4 -> 11 then end
  1201. for (int i = 4; i < 11; i++) {
  1202. assertEquals(i + 1, fs.getNextBlock(i));
  1203. }
  1204. assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(11));
  1205. }
  1206. }
  1207. /**
  1208. * Check we get the right data back for each block
  1209. */
  1210. @ParameterizedTest()
  1211. @MethodSource("get512FileAndInput")
  1212. void getBlock512(String file, Function<String,POIFSFileSystem> opener) throws IOException {
  1213. try (POIFSFileSystem fs = opener.apply(file)) {
  1214. // The 0th block is the first data block
  1215. ByteBuffer b = fs.getBlockAt(0);
  1216. assertEquals((byte) 0x9e, b.get());
  1217. assertEquals((byte) 0x75, b.get());
  1218. assertEquals((byte) 0x97, b.get());
  1219. assertEquals((byte) 0xf6, b.get());
  1220. // And the next block
  1221. b = fs.getBlockAt(1);
  1222. assertEquals((byte) 0x86, b.get());
  1223. assertEquals((byte) 0x09, b.get());
  1224. assertEquals((byte) 0x22, b.get());
  1225. assertEquals((byte) 0xfb, b.get());
  1226. // Check the final block too
  1227. b = fs.getBlockAt(99);
  1228. assertEquals((byte) 0x01, b.get());
  1229. assertEquals((byte) 0x00, b.get());
  1230. assertEquals((byte) 0x00, b.get());
  1231. assertEquals((byte) 0x00, b.get());
  1232. assertEquals((byte) 0x02, b.get());
  1233. assertEquals((byte) 0x00, b.get());
  1234. assertEquals((byte) 0x00, b.get());
  1235. assertEquals((byte) 0x00, b.get());
  1236. }
  1237. }
  1238. @ParameterizedTest()
  1239. @MethodSource("get4kFileAndInput")
  1240. void getBlock4k(String file, Function<String,POIFSFileSystem> opener) throws IOException {
  1241. // Quick check on 4096 byte blocks too
  1242. try (POIFSFileSystem fs = opener.apply(file)) {
  1243. // The 0th block is the first data block
  1244. ByteBuffer b = fs.getBlockAt(0);
  1245. assertEquals((byte) 0x9e, b.get());
  1246. assertEquals((byte) 0x75, b.get());
  1247. assertEquals((byte) 0x97, b.get());
  1248. assertEquals((byte) 0xf6, b.get());
  1249. // And the next block
  1250. b = fs.getBlockAt(1);
  1251. assertEquals((byte) 0x00, b.get());
  1252. assertEquals((byte) 0x00, b.get());
  1253. assertEquals((byte) 0x03, b.get());
  1254. assertEquals((byte) 0x00, b.get());
  1255. // The 14th block is the FAT
  1256. b = fs.getBlockAt(14);
  1257. assertEquals((byte) 0x01, b.get());
  1258. assertEquals((byte) 0x00, b.get());
  1259. assertEquals((byte) 0x00, b.get());
  1260. assertEquals((byte) 0x00, b.get());
  1261. assertEquals((byte) 0x02, b.get());
  1262. assertEquals((byte) 0x00, b.get());
  1263. assertEquals((byte) 0x00, b.get());
  1264. assertEquals((byte) 0x00, b.get());
  1265. }
  1266. }
  1267. /**
  1268. * Ask for free blocks where there are some already
  1269. * to be had from the FAT
  1270. */
  1271. @Test
  1272. void getFreeBlockWithSpare() throws IOException {
  1273. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.getFile("BlockSize512.zvi"))) {
  1274. // Our first BAT block has spares
  1275. assertTrue(fs.getBATBlockAndIndex(0).getBlock().hasFreeSectors());
  1276. // First free one is 100
  1277. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(100));
  1278. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(101));
  1279. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(102));
  1280. assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(103));
  1281. // Ask, will get 100
  1282. assertEquals(100, fs.getFreeBlock());
  1283. // Ask again, will still get 100 as not written to
  1284. assertEquals(100, fs.getFreeBlock());
  1285. // Allocate it, then ask again
  1286. fs.setNextBlock(100, POIFSConstants.END_OF_CHAIN);
  1287. assertEquals(101, fs.getFreeBlock());
  1288. }
  1289. }
  1290. /**
  1291. * Ask for free blocks where no free ones exist, and so the
  1292. * file needs to be extended and another BAT/XBAT added
  1293. */
  1294. @Test
  1295. void getFreeBlockWithNoneSpare() throws IOException {
  1296. try (POIFSFileSystem fs1 = new POIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"))) {
  1297. int free;
  1298. // We have one BAT at block 99
  1299. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs1.getNextBlock(99));
  1300. assertBATCount(fs1, 1, 0);
  1301. // We've spare ones from 100 to 128
  1302. for (int i = 100; i < 128; i++) {
  1303. assertEquals(POIFSConstants.UNUSED_BLOCK, fs1.getNextBlock(i));
  1304. }
  1305. // Check our BAT knows it's free
  1306. assertTrue(fs1.getBATBlockAndIndex(0).getBlock().hasFreeSectors());
  1307. // Allocate all the spare ones
  1308. for (int i = 100; i < 128; i++) {
  1309. fs1.setNextBlock(i, POIFSConstants.END_OF_CHAIN);
  1310. }
  1311. // BAT is now full, but there's only the one
  1312. assertFalse(fs1.getBATBlockAndIndex(0).getBlock().hasFreeSectors());
  1313. assertThrows(IndexOutOfBoundsException.class, () -> fs1.getBATBlockAndIndex(128), "Should only be one BAT");
  1314. assertBATCount(fs1, 1, 0);
  1315. // Now ask for a free one, will need to extend the file
  1316. assertEquals(129, fs1.getFreeBlock());
  1317. assertFalse(fs1.getBATBlockAndIndex(0).getBlock().hasFreeSectors());
  1318. assertTrue(fs1.getBATBlockAndIndex(128).getBlock().hasFreeSectors());
  1319. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs1.getNextBlock(128));
  1320. assertEquals(POIFSConstants.UNUSED_BLOCK, fs1.getNextBlock(129));
  1321. // We now have 2 BATs, but no XBATs
  1322. assertBATCount(fs1, 2, 0);
  1323. // Fill up to hold 109 BAT blocks
  1324. for (int i = 0; i < 109; i++) {
  1325. fs1.getFreeBlock();
  1326. int startOffset = i * 128;
  1327. while (fs1.getBATBlockAndIndex(startOffset).getBlock().hasFreeSectors()) {
  1328. free = fs1.getFreeBlock();
  1329. fs1.setNextBlock(free, POIFSConstants.END_OF_CHAIN);
  1330. }
  1331. }
  1332. assertFalse(fs1.getBATBlockAndIndex(109 * 128 - 1).getBlock().hasFreeSectors());
  1333. assertThrows(IndexOutOfBoundsException.class, () -> fs1.getBATBlockAndIndex(109 * 128), "Should only be 109 BATs");
  1334. // We now have 109 BATs, but no XBATs
  1335. assertBATCount(fs1, 109, 0);
  1336. // Ask for it to be written out, and check the header
  1337. HeaderBlock header = writeOutAndReadHeader(fs1);
  1338. assertEquals(109, header.getBATCount());
  1339. assertEquals(0, header.getXBATCount());
  1340. // Ask for another, will get our first XBAT
  1341. free = fs1.getFreeBlock();
  1342. assertTrue(free > 0, "Had: " + free);
  1343. assertFalse(fs1.getBATBlockAndIndex(109 * 128 - 1).getBlock().hasFreeSectors());
  1344. assertTrue(fs1.getBATBlockAndIndex(110 * 128 - 1).getBlock().hasFreeSectors());
  1345. assertThrows(IndexOutOfBoundsException.class, () -> fs1.getBATBlockAndIndex(110 * 128), "Should only be 110 BATs");
  1346. assertBATCount(fs1, 110, 1);
  1347. header = writeOutAndReadHeader(fs1);
  1348. assertEquals(110, header.getBATCount());
  1349. assertEquals(1, header.getXBATCount());
  1350. // Fill the XBAT, which means filling 127 BATs
  1351. for (int i = 109; i < 109 + 127; i++) {
  1352. fs1.getFreeBlock();
  1353. int startOffset = i * 128;
  1354. while (fs1.getBATBlockAndIndex(startOffset).getBlock().hasFreeSectors()) {
  1355. free = fs1.getFreeBlock();
  1356. fs1.setNextBlock(free, POIFSConstants.END_OF_CHAIN);
  1357. }
  1358. assertBATCount(fs1, i + 1, 1);
  1359. }
  1360. // Should now have 109+127 = 236 BATs
  1361. assertFalse(fs1.getBATBlockAndIndex(236 * 128 - 1).getBlock().hasFreeSectors());
  1362. assertThrows(IndexOutOfBoundsException.class, () -> fs1.getBATBlockAndIndex(236 * 128), "Should only be 236 BATs");
  1363. assertBATCount(fs1, 236, 1);
  1364. // Ask for another, will get our 2nd XBAT
  1365. free = fs1.getFreeBlock();
  1366. assertTrue(free > 0, "Had: " + free);
  1367. assertFalse(fs1.getBATBlockAndIndex(236 * 128 - 1).getBlock().hasFreeSectors());
  1368. assertTrue(fs1.getBATBlockAndIndex(237 * 128 - 1).getBlock().hasFreeSectors());
  1369. assertThrows(IndexOutOfBoundsException.class, () -> fs1.getBATBlockAndIndex(237 * 128), "Should only be 237 BATs");
  1370. // Check the counts now
  1371. assertBATCount(fs1, 237, 2);
  1372. // Check the header
  1373. header = writeOutAndReadHeader(fs1);
  1374. assertNotNull(header);
  1375. // Now, write it out, and read it back in again fully
  1376. try (POIFSFileSystem fs2 = writeOutAndReadBack(fs1)) {
  1377. // Check that it is seen correctly
  1378. assertBATCount(fs2, 237, 2);
  1379. assertFalse(fs2.getBATBlockAndIndex(236 * 128 - 1).getBlock().hasFreeSectors());
  1380. assertTrue(fs2.getBATBlockAndIndex(237 * 128 - 1).getBlock().hasFreeSectors());
  1381. assertThrows(IndexOutOfBoundsException.class, () -> fs2.getBATBlockAndIndex(237 * 128), "Should only be 237 BATs");
  1382. }
  1383. }
  1384. }
  1385. /**
  1386. * Test that we can correctly get the list of directory
  1387. * entries, and the details on the files in them
  1388. */
  1389. @ParameterizedTest
  1390. @MethodSource("get512and4kFileAndInput")
  1391. void listEntries(String file, Function<String,POIFSFileSystem> opener) throws IOException {
  1392. try (POIFSFileSystem fs = opener.apply(file)) {
  1393. DirectoryEntry root = fs.getRoot();
  1394. assertEquals(5, root.getEntryCount());
  1395. // Check by the names
  1396. Entry thumbnail = root.getEntry("Thumbnail");
  1397. Entry dsi = root.getEntry("\u0005DocumentSummaryInformation");
  1398. Entry si = root.getEntry("\u0005SummaryInformation");
  1399. Entry image = root.getEntry("Image");
  1400. Entry tags = root.getEntry("Tags");
  1401. assertFalse(thumbnail.isDirectoryEntry());
  1402. assertFalse(dsi.isDirectoryEntry());
  1403. assertFalse(si.isDirectoryEntry());
  1404. assertTrue(image.isDirectoryEntry());
  1405. assertFalse(tags.isDirectoryEntry());
  1406. // Check via the iterator
  1407. Iterator<Entry> it = root.getEntries();
  1408. assertEquals(thumbnail.getName(), it.next().getName());
  1409. assertEquals(dsi.getName(), it.next().getName());
  1410. assertEquals(si.getName(), it.next().getName());
  1411. assertEquals(image.getName(), it.next().getName());
  1412. assertEquals(tags.getName(), it.next().getName());
  1413. // Look inside another
  1414. DirectoryEntry imageD = (DirectoryEntry) image;
  1415. assertEquals(7, imageD.getEntryCount());
  1416. }
  1417. }
  1418. /**
  1419. * Tests that we can get the correct contents for
  1420. * a document in the filesystem
  1421. */
  1422. @ParameterizedTest
  1423. @MethodSource("get512and4kFileAndInput")
  1424. void getDocumentEntry(String file, Function<String,POIFSFileSystem> opener)
  1425. throws IOException, NoPropertySetStreamException {
  1426. try (POIFSFileSystem fs = opener.apply(file)) {
  1427. DirectoryEntry root = fs.getRoot();
  1428. Entry si = root.getEntry("\u0005SummaryInformation");
  1429. assertTrue(si.isDocumentEntry());
  1430. DocumentNode doc = (DocumentNode) si;
  1431. // Check we can read it
  1432. assertContentsMatches(null, doc);
  1433. // Now try to build the property set
  1434. try (DocumentInputStream inp = new DocumentInputStream(doc)) {
  1435. PropertySet ps = PropertySetFactory.create(inp);
  1436. SummaryInformation inf = (SummaryInformation) ps;
  1437. // Check some bits in it
  1438. assertNull(inf.getApplicationName());
  1439. assertNull(inf.getAuthor());
  1440. assertNull(inf.getSubject());
  1441. assertEquals(131333, inf.getOSVersion());
  1442. }
  1443. // Try the other summary information
  1444. si = root.getEntry("\u0005DocumentSummaryInformation");
  1445. assertTrue(si.isDocumentEntry());
  1446. doc = (DocumentNode) si;
  1447. assertContentsMatches(null, doc);
  1448. try (DocumentInputStream inp = new DocumentInputStream(doc)) {
  1449. PropertySet ps = PropertySetFactory.create(inp);
  1450. DocumentSummaryInformation dinf = (DocumentSummaryInformation) ps;
  1451. assertEquals(131333, dinf.getOSVersion());
  1452. }
  1453. }
  1454. }
  1455. /**
  1456. * Read a file, write it and read it again.
  1457. * Then, alter+add some streams, write and read
  1458. */
  1459. @ParameterizedTest
  1460. @MethodSource("get512and4kFileAndInput")
  1461. void readWriteRead(String file, Function<String,POIFSFileSystem> opener) throws IOException, NoPropertySetStreamException {
  1462. SummaryInformation sinf;
  1463. DocumentSummaryInformation dinf;
  1464. DirectoryEntry root, testDir;
  1465. try (POIFSFileSystem fs1 = opener.apply(file)) {
  1466. // Check we can find the entries we expect
  1467. root = fs1.getRoot();
  1468. assertEquals(5, root.getEntryCount());
  1469. assertThat(root.getEntryNames(), hasItem("Thumbnail"));
  1470. assertThat(root.getEntryNames(), hasItem("Image"));
  1471. assertThat(root.getEntryNames(), hasItem("Tags"));
  1472. assertThat(root.getEntryNames(), hasItem("\u0005DocumentSummaryInformation"));
  1473. assertThat(root.getEntryNames(), hasItem("\u0005SummaryInformation"));
  1474. // Write out, re-load
  1475. try (POIFSFileSystem fs2 = writeOutAndReadBack(fs1)) {
  1476. // Check they're still there
  1477. root = fs2.getRoot();
  1478. assertEquals(5, root.getEntryCount());
  1479. assertThat(root.getEntryNames(), hasItem("Thumbnail"));
  1480. assertThat(root.getEntryNames(), hasItem("Image"));
  1481. assertThat(root.getEntryNames(), hasItem("Tags"));
  1482. assertThat(root.getEntryNames(), hasItem("\u0005DocumentSummaryInformation"));
  1483. assertThat(root.getEntryNames(), hasItem("\u0005SummaryInformation"));
  1484. // Check the contents of them - parse the summary block and check
  1485. sinf = (SummaryInformation) PropertySetFactory.create(new DocumentInputStream(
  1486. (DocumentEntry) root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME)));
  1487. assertEquals(131333, sinf.getOSVersion());
  1488. dinf = (DocumentSummaryInformation) PropertySetFactory.create(new DocumentInputStream(
  1489. (DocumentEntry) root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME)));
  1490. assertEquals(131333, dinf.getOSVersion());
  1491. // Add a test mini stream
  1492. testDir = root.createDirectory("Testing 123");
  1493. testDir.createDirectory("Testing 456");
  1494. testDir.createDirectory("Testing 789");
  1495. byte[] mini = new byte[]{42, 0, 1, 2, 3, 4, 42};
  1496. testDir.createDocument("Mini", new ByteArrayInputStream(mini));
  1497. // Write out, re-load
  1498. try (POIFSFileSystem fs3 = writeOutAndReadBack(fs2)) {
  1499. root = fs3.getRoot();
  1500. testDir = (DirectoryEntry) root.getEntry("Testing 123");
  1501. assertEquals(6, root.getEntryCount());
  1502. assertThat(root.getEntryNames(), hasItem("Thumbnail"));
  1503. assertThat(root.getEntryNames(), hasItem("Image"));
  1504. assertThat(root.getEntryNames(), hasItem("Tags"));
  1505. assertThat(root.getEntryNames(), hasItem("Testing 123"));
  1506. assertThat(root.getEntryNames(), hasItem("\u0005DocumentSummaryInformation"));
  1507. assertThat(root.getEntryNames(), hasItem("\u0005SummaryInformation"));
  1508. // Check old and new are there
  1509. sinf = (SummaryInformation) PropertySetFactory.create(new DocumentInputStream(
  1510. (DocumentEntry) root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME)));
  1511. assertEquals(131333, sinf.getOSVersion());
  1512. dinf = (DocumentSummaryInformation) PropertySetFactory.create(new DocumentInputStream(
  1513. (DocumentEntry) root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME)));
  1514. assertEquals(131333, dinf.getOSVersion());
  1515. assertContentsMatches(mini, (DocumentEntry) testDir.getEntry("Mini"));
  1516. // Write out and read once more, just to be sure
  1517. try (POIFSFileSystem fs4 = writeOutAndReadBack(fs3)) {
  1518. root = fs4.getRoot();
  1519. testDir = (DirectoryEntry) root.getEntry("Testing 123");
  1520. assertEquals(6, root.getEntryCount());
  1521. assertThat(root.getEntryNames(), hasItem("Thumbnail"));
  1522. assertThat(root.getEntryNames(), hasItem("Image"));
  1523. assertThat(root.getEntryNames(), hasItem("Tags"));
  1524. assertThat(root.getEntryNames(), hasItem("Testing 123"));
  1525. assertThat(root.getEntryNames(), hasItem("\u0005DocumentSummaryInformation"));
  1526. assertThat(root.getEntryNames(), hasItem("\u0005SummaryInformation"));
  1527. sinf = (SummaryInformation) PropertySetFactory.create(new DocumentInputStream(
  1528. (DocumentEntry) root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME)));
  1529. assertEquals(131333, sinf.getOSVersion());
  1530. dinf = (DocumentSummaryInformation) PropertySetFactory.create(new DocumentInputStream(
  1531. (DocumentEntry) root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME)));
  1532. assertEquals(131333, dinf.getOSVersion());
  1533. assertContentsMatches(mini, (DocumentEntry) testDir.getEntry("Mini"));
  1534. // Add a full stream, delete a full stream
  1535. byte[] main4096 = new byte[4096];
  1536. main4096[0] = -10;
  1537. main4096[4095] = -11;
  1538. testDir.createDocument("Normal4096", new ByteArrayInputStream(main4096));
  1539. root.getEntry("Tags").delete();
  1540. // Write out, re-load
  1541. try (POIFSFileSystem fs5 = writeOutAndReadBack(fs4)) {
  1542. // Check it's all there
  1543. root = fs5.getRoot();
  1544. testDir = (DirectoryEntry) root.getEntry("Testing 123");
  1545. assertEquals(5, root.getEntryCount());
  1546. assertThat(root.getEntryNames(), hasItem("Thumbnail"));
  1547. assertThat(root.getEntryNames(), hasItem("Image"));
  1548. assertThat(root.getEntryNames(), hasItem("Testing 123"));
  1549. assertThat(root.getEntryNames(), hasItem("\u0005DocumentSummaryInformation"));
  1550. assertThat(root.getEntryNames(), hasItem("\u0005SummaryInformation"));
  1551. // Check old and new are there
  1552. sinf = (SummaryInformation) PropertySetFactory.create(new DocumentInputStream(
  1553. (DocumentEntry) root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME)));
  1554. assertEquals(131333, sinf.getOSVersion());
  1555. dinf = (DocumentSummaryInformation) PropertySetFactory.create(new DocumentInputStream(
  1556. (DocumentEntry) root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME)));
  1557. assertEquals(131333, dinf.getOSVersion());
  1558. assertContentsMatches(mini, (DocumentEntry) testDir.getEntry("Mini"));
  1559. assertContentsMatches(main4096, (DocumentEntry) testDir.getEntry("Normal4096"));
  1560. // Delete a directory, and add one more
  1561. testDir.getEntry("Testing 456").delete();
  1562. testDir.createDirectory("Testing ABC");
  1563. // Save
  1564. try (POIFSFileSystem fs6 = writeOutAndReadBack(fs5)) {
  1565. // Check
  1566. root = fs6.getRoot();
  1567. testDir = (DirectoryEntry) root.getEntry("Testing 123");
  1568. assertEquals(5, root.getEntryCount());
  1569. assertThat(root.getEntryNames(), hasItem("Thumbnail"));
  1570. assertThat(root.getEntryNames(), hasItem("Image"));
  1571. assertThat(root.getEntryNames(), hasItem("Testing 123"));
  1572. assertThat(root.getEntryNames(), hasItem("\u0005DocumentSummaryInformation"));
  1573. assertThat(root.getEntryNames(), hasItem("\u0005SummaryInformation"));
  1574. assertEquals(4, testDir.getEntryCount());
  1575. assertThat(testDir.getEntryNames(), hasItem("Mini"));
  1576. assertThat(testDir.getEntryNames(), hasItem("Normal4096"));
  1577. assertThat(testDir.getEntryNames(), hasItem("Testing 789"));
  1578. assertThat(testDir.getEntryNames(), hasItem("Testing ABC"));
  1579. // Add another mini stream
  1580. byte[] mini2 = new byte[]{-42, 0, -1, -2, -3, -4, -42};
  1581. testDir.createDocument("Mini2", new ByteArrayInputStream(mini2));
  1582. // Save, load, check
  1583. try (POIFSFileSystem fs7 = writeOutAndReadBack(fs6)) {
  1584. root = fs7.getRoot();
  1585. testDir = (DirectoryEntry) root.getEntry("Testing 123");
  1586. assertEquals(5, root.getEntryCount());
  1587. assertThat(root.getEntryNames(), hasItem("Thumbnail"));
  1588. assertThat(root.getEntryNames(), hasItem("Image"));
  1589. assertThat(root.getEntryNames(), hasItem("Testing 123"));
  1590. assertThat(root.getEntryNames(), hasItem("\u0005DocumentSummaryInformation"));
  1591. assertThat(root.getEntryNames(), hasItem("\u0005SummaryInformation"));
  1592. assertEquals(5, testDir.getEntryCount());
  1593. assertThat(testDir.getEntryNames(), hasItem("Mini"));
  1594. assertThat(testDir.getEntryNames(), hasItem("Mini2"));
  1595. assertThat(testDir.getEntryNames(), hasItem("Normal4096"));
  1596. assertThat(testDir.getEntryNames(), hasItem("Testing 789"));
  1597. assertThat(testDir.getEntryNames(), hasItem("Testing ABC"));
  1598. assertContentsMatches(mini, (DocumentEntry) testDir.getEntry("Mini"));
  1599. assertContentsMatches(mini2, (DocumentEntry) testDir.getEntry("Mini2"));
  1600. assertContentsMatches(main4096, (DocumentEntry) testDir.getEntry("Normal4096"));
  1601. // Delete a mini stream, add one more
  1602. testDir.getEntry("Mini").delete();
  1603. byte[] mini3 = new byte[]{42, 0, 42, 0, 42, 0, 42};
  1604. testDir.createDocument("Mini3", new ByteArrayInputStream(mini3));
  1605. // Save, load, check
  1606. try (POIFSFileSystem fs8 = writeOutAndReadBack(fs7)) {
  1607. root = fs8.getRoot();
  1608. testDir = (DirectoryEntry) root.getEntry("Testing 123");
  1609. assertEquals(5, root.getEntryCount());
  1610. assertThat(root.getEntryNames(), hasItem("Thumbnail"));
  1611. assertThat(root.getEntryNames(), hasItem("Image"));
  1612. assertThat(root.getEntryNames(), hasItem("Testing 123"));
  1613. assertThat(root.getEntryNames(), hasItem("\u0005DocumentSummaryInformation"));
  1614. assertThat(root.getEntryNames(), hasItem("\u0005SummaryInformation"));
  1615. assertEquals(5, testDir.getEntryCount());
  1616. assertThat(testDir.getEntryNames(), hasItem("Mini2"));
  1617. assertThat(testDir.getEntryNames(), hasItem("Mini3"));
  1618. assertThat(testDir.getEntryNames(), hasItem("Normal4096"));
  1619. assertThat(testDir.getEntryNames(), hasItem("Testing 789"));
  1620. assertThat(testDir.getEntryNames(), hasItem("Testing ABC"));
  1621. assertContentsMatches(mini2, (DocumentEntry) testDir.getEntry("Mini2"));
  1622. assertContentsMatches(mini3, (DocumentEntry) testDir.getEntry("Mini3"));
  1623. assertContentsMatches(main4096, (DocumentEntry) testDir.getEntry("Normal4096"));
  1624. // Change some existing streams
  1625. POIFSDocument mini2Doc = new POIFSDocument((DocumentNode) testDir.getEntry("Mini2"));
  1626. mini2Doc.replaceContents(new ByteArrayInputStream(mini));
  1627. byte[] main4106 = new byte[4106];
  1628. main4106[0] = 41;
  1629. main4106[4105] = 42;
  1630. POIFSDocument mainDoc = new POIFSDocument((DocumentNode) testDir.getEntry("Normal4096"));
  1631. mainDoc.replaceContents(new ByteArrayInputStream(main4106));
  1632. // Re-check
  1633. try (POIFSFileSystem fs9 = writeOutAndReadBack(fs8)) {
  1634. root = fs9.getRoot();
  1635. testDir = (DirectoryEntry) root.getEntry("Testing 123");
  1636. assertEquals(5, root.getEntryCount());
  1637. assertThat(root.getEntryNames(), hasItem("Thumbnail"));
  1638. assertThat(root.getEntryNames(), hasItem("Image"));
  1639. assertThat(root.getEntryNames(), hasItem("Testing 123"));
  1640. assertThat(root.getEntryNames(), hasItem("\u0005DocumentSummaryInformation"));
  1641. assertThat(root.getEntryNames(), hasItem("\u0005SummaryInformation"));
  1642. assertEquals(5, testDir.getEntryCount());
  1643. assertThat(testDir.getEntryNames(), hasItem("Mini2"));
  1644. assertThat(testDir.getEntryNames(), hasItem("Mini3"));
  1645. assertThat(testDir.getEntryNames(), hasItem("Normal4096"));
  1646. assertThat(testDir.getEntryNames(), hasItem("Testing 789"));
  1647. assertThat(testDir.getEntryNames(), hasItem("Testing ABC"));
  1648. assertContentsMatches(mini, (DocumentEntry) testDir.getEntry("Mini2"));
  1649. assertContentsMatches(mini3, (DocumentEntry) testDir.getEntry("Mini3"));
  1650. assertContentsMatches(main4106, (DocumentEntry) testDir.getEntry("Normal4096"));
  1651. }
  1652. }
  1653. }
  1654. }
  1655. }
  1656. }
  1657. }
  1658. }
  1659. }
  1660. }
  1661. /**
  1662. * Create a new file, write it and read it again
  1663. * Then, add some streams, write and read
  1664. */
  1665. @Test
  1666. void createWriteRead() throws IOException {
  1667. try (POIFSFileSystem fs1 = new POIFSFileSystem()) {
  1668. // Initially has Properties + BAT but not SBAT
  1669. assertEquals(POIFSConstants.END_OF_CHAIN, fs1.getNextBlock(0));
  1670. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs1.getNextBlock(1));
  1671. assertEquals(POIFSConstants.UNUSED_BLOCK, fs1.getNextBlock(2));
  1672. // Check that the SBAT is empty
  1673. assertEquals(POIFSConstants.END_OF_CHAIN, fs1.getRoot().getProperty().getStartBlock());
  1674. // Check that properties table was given block 0
  1675. assertEquals(0, fs1._get_property_table().getStartBlock());
  1676. // Write and read it
  1677. try (POIFSFileSystem fs2 = writeOutAndReadBack(fs1)) {
  1678. // No change, SBAT remains empty
  1679. assertEquals(POIFSConstants.END_OF_CHAIN, fs2.getNextBlock(0));
  1680. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs2.getNextBlock(1));
  1681. assertEquals(POIFSConstants.UNUSED_BLOCK, fs2.getNextBlock(2));
  1682. assertEquals(POIFSConstants.UNUSED_BLOCK, fs2.getNextBlock(3));
  1683. assertEquals(POIFSConstants.END_OF_CHAIN, fs2.getRoot().getProperty().getStartBlock());
  1684. assertEquals(0, fs2._get_property_table().getStartBlock());
  1685. }
  1686. }
  1687. // Check the same but with saving to a file
  1688. try (POIFSFileSystem fs3 = new POIFSFileSystem();
  1689. POIFSFileSystem fs4 = writeOutFileAndReadBack(fs3)) {
  1690. // Same, no change, SBAT remains empty
  1691. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(0));
  1692. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs4.getNextBlock(1));
  1693. assertEquals(POIFSConstants.UNUSED_BLOCK, fs4.getNextBlock(2));
  1694. assertEquals(POIFSConstants.UNUSED_BLOCK, fs4.getNextBlock(3));
  1695. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getRoot().getProperty().getStartBlock());
  1696. assertEquals(0, fs4._get_property_table().getStartBlock());
  1697. // Put everything within a new directory
  1698. DirectoryEntry testDir = fs4.createDirectory("Test Directory");
  1699. // Add a new Normal Stream (Normal Streams minimum 4096 bytes)
  1700. byte[] main4096 = new byte[4096];
  1701. main4096[0] = -10;
  1702. main4096[4095] = -11;
  1703. testDir.createDocument("Normal4096", new ByteArrayInputStream(main4096));
  1704. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(0));
  1705. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs4.getNextBlock(1));
  1706. assertEquals(3, fs4.getNextBlock(2));
  1707. assertEquals(4, fs4.getNextBlock(3));
  1708. assertEquals(5, fs4.getNextBlock(4));
  1709. assertEquals(6, fs4.getNextBlock(5));
  1710. assertEquals(7, fs4.getNextBlock(6));
  1711. assertEquals(8, fs4.getNextBlock(7));
  1712. assertEquals(9, fs4.getNextBlock(8));
  1713. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(9));
  1714. assertEquals(POIFSConstants.UNUSED_BLOCK, fs4.getNextBlock(10));
  1715. assertEquals(POIFSConstants.UNUSED_BLOCK, fs4.getNextBlock(11));
  1716. // SBAT still unused
  1717. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getRoot().getProperty().getStartBlock());
  1718. // Add a bigger Normal Stream
  1719. byte[] main5124 = new byte[5124];
  1720. main5124[0] = -22;
  1721. main5124[5123] = -33;
  1722. testDir.createDocument("Normal5124", new ByteArrayInputStream(main5124));
  1723. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(0));
  1724. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs4.getNextBlock(1));
  1725. assertEquals(3, fs4.getNextBlock(2));
  1726. assertEquals(4, fs4.getNextBlock(3));
  1727. assertEquals(5, fs4.getNextBlock(4));
  1728. assertEquals(6, fs4.getNextBlock(5));
  1729. assertEquals(7, fs4.getNextBlock(6));
  1730. assertEquals(8, fs4.getNextBlock(7));
  1731. assertEquals(9, fs4.getNextBlock(8));
  1732. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(9));
  1733. assertEquals(11, fs4.getNextBlock(10));
  1734. assertEquals(12, fs4.getNextBlock(11));
  1735. assertEquals(13, fs4.getNextBlock(12));
  1736. assertEquals(14, fs4.getNextBlock(13));
  1737. assertEquals(15, fs4.getNextBlock(14));
  1738. assertEquals(16, fs4.getNextBlock(15));
  1739. assertEquals(17, fs4.getNextBlock(16));
  1740. assertEquals(18, fs4.getNextBlock(17));
  1741. assertEquals(19, fs4.getNextBlock(18));
  1742. assertEquals(20, fs4.getNextBlock(19));
  1743. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(20));
  1744. assertEquals(POIFSConstants.UNUSED_BLOCK, fs4.getNextBlock(21));
  1745. assertEquals(POIFSConstants.UNUSED_BLOCK, fs4.getNextBlock(22));
  1746. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getRoot().getProperty().getStartBlock());
  1747. // Now Add a mini stream
  1748. byte[] mini = new byte[]{42, 0, 1, 2, 3, 4, 42};
  1749. testDir.createDocument("Mini", new ByteArrayInputStream(mini));
  1750. // Mini stream will get one block for fat + one block for data
  1751. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(0));
  1752. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs4.getNextBlock(1));
  1753. assertEquals(3, fs4.getNextBlock(2));
  1754. assertEquals(4, fs4.getNextBlock(3));
  1755. assertEquals(5, fs4.getNextBlock(4));
  1756. assertEquals(6, fs4.getNextBlock(5));
  1757. assertEquals(7, fs4.getNextBlock(6));
  1758. assertEquals(8, fs4.getNextBlock(7));
  1759. assertEquals(9, fs4.getNextBlock(8));
  1760. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(9));
  1761. assertEquals(11, fs4.getNextBlock(10));
  1762. assertEquals(12, fs4.getNextBlock(11));
  1763. assertEquals(13, fs4.getNextBlock(12));
  1764. assertEquals(14, fs4.getNextBlock(13));
  1765. assertEquals(15, fs4.getNextBlock(14));
  1766. assertEquals(16, fs4.getNextBlock(15));
  1767. assertEquals(17, fs4.getNextBlock(16));
  1768. assertEquals(18, fs4.getNextBlock(17));
  1769. assertEquals(19, fs4.getNextBlock(18));
  1770. assertEquals(20, fs4.getNextBlock(19));
  1771. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(20));
  1772. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(21));
  1773. assertEquals(POIFSConstants.END_OF_CHAIN, fs4.getNextBlock(22));
  1774. assertEquals(POIFSConstants.UNUSED_BLOCK, fs4.getNextBlock(23));
  1775. // Check the mini stream location was set
  1776. // (21 is mini fat, 22 is first mini stream block)
  1777. assertEquals(22, fs4.getRoot().getProperty().getStartBlock());
  1778. // Write and read back
  1779. try (POIFSFileSystem fs5 = writeOutAndReadBack(fs4)) {
  1780. HeaderBlock header = writeOutAndReadHeader(fs5);
  1781. // Check the header has the right points in it
  1782. assertEquals(1, header.getBATCount());
  1783. assertEquals(1, header.getBATArray()[0]);
  1784. assertEquals(2, header.getPropertyCount());
  1785. assertEquals(0, header.getPropertyStart());
  1786. assertEquals(1, header.getSBATCount());
  1787. assertEquals(21, header.getSBATStart());
  1788. assertEquals(22, fs5._get_property_table().getRoot().getStartBlock());
  1789. // Block use should be almost the same, except the properties
  1790. // stream will have grown out to cover 2 blocks
  1791. // Check the block use is all unchanged
  1792. assertEquals(23, fs5.getNextBlock(0)); // Properties now extends over 2 blocks
  1793. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs5.getNextBlock(1));
  1794. assertEquals(3, fs5.getNextBlock(2));
  1795. assertEquals(4, fs5.getNextBlock(3));
  1796. assertEquals(5, fs5.getNextBlock(4));
  1797. assertEquals(6, fs5.getNextBlock(5));
  1798. assertEquals(7, fs5.getNextBlock(6));
  1799. assertEquals(8, fs5.getNextBlock(7));
  1800. assertEquals(9, fs5.getNextBlock(8));
  1801. assertEquals(POIFSConstants.END_OF_CHAIN, fs5.getNextBlock(9)); // End of normal4096
  1802. assertEquals(11, fs5.getNextBlock(10));
  1803. assertEquals(12, fs5.getNextBlock(11));
  1804. assertEquals(13, fs5.getNextBlock(12));
  1805. assertEquals(14, fs5.getNextBlock(13));
  1806. assertEquals(15, fs5.getNextBlock(14));
  1807. assertEquals(16, fs5.getNextBlock(15));
  1808. assertEquals(17, fs5.getNextBlock(16));
  1809. assertEquals(18, fs5.getNextBlock(17));
  1810. assertEquals(19, fs5.getNextBlock(18));
  1811. assertEquals(20, fs5.getNextBlock(19));
  1812. assertEquals(POIFSConstants.END_OF_CHAIN, fs5.getNextBlock(20)); // End of normal5124
  1813. assertEquals(POIFSConstants.END_OF_CHAIN, fs5.getNextBlock(21)); // Mini Stream FAT
  1814. assertEquals(POIFSConstants.END_OF_CHAIN, fs5.getNextBlock(22)); // Mini Stream data
  1815. assertEquals(POIFSConstants.END_OF_CHAIN, fs5.getNextBlock(23)); // Properties #2
  1816. assertEquals(POIFSConstants.UNUSED_BLOCK, fs5.getNextBlock(24));
  1817. // Check some data
  1818. assertEquals(1, fs5.getRoot().getEntryCount());
  1819. testDir = (DirectoryEntry) fs5.getRoot().getEntry("Test Directory");
  1820. assertEquals(3, testDir.getEntryCount());
  1821. DocumentEntry miniDoc = (DocumentEntry) testDir.getEntry("Mini");
  1822. assertContentsMatches(mini, miniDoc);
  1823. DocumentEntry normDoc = (DocumentEntry) testDir.getEntry("Normal4096");
  1824. assertContentsMatches(main4096, normDoc);
  1825. normDoc = (DocumentEntry) testDir.getEntry("Normal5124");
  1826. assertContentsMatches(main5124, normDoc);
  1827. // Delete a couple of streams
  1828. miniDoc.delete();
  1829. normDoc.delete();
  1830. // Check - will have un-used sectors now
  1831. try (POIFSFileSystem fs6 = writeOutAndReadBack(fs5)) {
  1832. assertEquals(POIFSConstants.END_OF_CHAIN, fs6.getNextBlock(0)); // Props back in 1 block
  1833. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs6.getNextBlock(1));
  1834. assertEquals(3, fs6.getNextBlock(2));
  1835. assertEquals(4, fs6.getNextBlock(3));
  1836. assertEquals(5, fs6.getNextBlock(4));
  1837. assertEquals(6, fs6.getNextBlock(5));
  1838. assertEquals(7, fs6.getNextBlock(6));
  1839. assertEquals(8, fs6.getNextBlock(7));
  1840. assertEquals(9, fs6.getNextBlock(8));
  1841. assertEquals(POIFSConstants.END_OF_CHAIN, fs6.getNextBlock(9)); // End of normal4096
  1842. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(10));
  1843. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(11));
  1844. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(12));
  1845. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(13));
  1846. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(14));
  1847. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(15));
  1848. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(16));
  1849. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(17));
  1850. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(18));
  1851. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(19));
  1852. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(20));
  1853. assertEquals(POIFSConstants.END_OF_CHAIN, fs6.getNextBlock(21)); // Mini Stream FAT
  1854. assertEquals(POIFSConstants.END_OF_CHAIN, fs6.getNextBlock(22)); // Mini Stream data
  1855. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(23)); // Properties gone
  1856. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(24));
  1857. assertEquals(POIFSConstants.UNUSED_BLOCK, fs6.getNextBlock(25));
  1858. }
  1859. }
  1860. }
  1861. }
  1862. @Test
  1863. void addBeforeWrite() throws IOException {
  1864. try (POIFSFileSystem fs1 = new POIFSFileSystem()) {
  1865. // Initially has Properties + BAT but nothing else
  1866. assertEquals(POIFSConstants.END_OF_CHAIN, fs1.getNextBlock(0));
  1867. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs1.getNextBlock(1));
  1868. assertEquals(POIFSConstants.UNUSED_BLOCK, fs1.getNextBlock(2));
  1869. HeaderBlock hdr = writeOutAndReadHeader(fs1);
  1870. // No mini stream, and no xbats
  1871. // Will have fat then properties stream
  1872. assertEquals(1, hdr.getBATCount());
  1873. assertEquals(1, hdr.getBATArray()[0]);
  1874. assertEquals(1, hdr.getPropertyCount());
  1875. assertEquals(0, hdr.getPropertyStart());
  1876. assertEquals(POIFSConstants.END_OF_CHAIN, hdr.getSBATStart());
  1877. assertEquals(POIFSConstants.END_OF_CHAIN, hdr.getXBATIndex());
  1878. assertEquals(POIFSConstants.SMALLER_BIG_BLOCK_SIZE * 3, fs1.size());
  1879. }
  1880. // Get a clean filesystem to start with
  1881. try (POIFSFileSystem fs1 = new POIFSFileSystem()) {
  1882. // Put our test files in a non-standard place
  1883. DirectoryEntry parentDir = fs1.createDirectory("Parent Directory");
  1884. DirectoryEntry testDir = parentDir.createDirectory("Test Directory");
  1885. // Add to the mini stream
  1886. byte[] mini = new byte[]{42, 0, 1, 2, 3, 4, 42};
  1887. testDir.createDocument("Mini", new ByteArrayInputStream(mini));
  1888. // Add to the main stream
  1889. byte[] main4096 = new byte[4096];
  1890. main4096[0] = -10;
  1891. main4096[4095] = -11;
  1892. testDir.createDocument("Normal4096", new ByteArrayInputStream(main4096));
  1893. // Check the mini stream was added, then the main stream
  1894. assertEquals(POIFSConstants.END_OF_CHAIN, fs1.getNextBlock(0));
  1895. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs1.getNextBlock(1));
  1896. assertEquals(POIFSConstants.END_OF_CHAIN, fs1.getNextBlock(2)); // Mini Fat
  1897. assertEquals(POIFSConstants.END_OF_CHAIN, fs1.getNextBlock(3)); // Mini Stream
  1898. assertEquals(5, fs1.getNextBlock(4)); // Main Stream
  1899. assertEquals(6, fs1.getNextBlock(5));
  1900. assertEquals(7, fs1.getNextBlock(6));
  1901. assertEquals(8, fs1.getNextBlock(7));
  1902. assertEquals(9, fs1.getNextBlock(8));
  1903. assertEquals(10, fs1.getNextBlock(9));
  1904. assertEquals(11, fs1.getNextBlock(10));
  1905. assertEquals(POIFSConstants.END_OF_CHAIN, fs1.getNextBlock(11));
  1906. assertEquals(POIFSConstants.UNUSED_BLOCK, fs1.getNextBlock(12));
  1907. assertEquals(POIFSConstants.SMALLER_BIG_BLOCK_SIZE * 13, fs1.size());
  1908. // Check that we can read the right data pre-write
  1909. DocumentEntry miniDoc = (DocumentEntry) testDir.getEntry("Mini");
  1910. assertContentsMatches(mini, miniDoc);
  1911. DocumentEntry normDoc = (DocumentEntry) testDir.getEntry("Normal4096");
  1912. assertContentsMatches(main4096, normDoc);
  1913. // Write, read, check
  1914. HeaderBlock hdr = writeOutAndReadHeader(fs1);
  1915. try (POIFSFileSystem fs2 = writeOutAndReadBack(fs1)) {
  1916. // Check the header details - will have the sbat near the start,
  1917. // then the properties at the end
  1918. assertEquals(1, hdr.getBATCount());
  1919. assertEquals(1, hdr.getBATArray()[0]);
  1920. assertEquals(2, hdr.getSBATStart());
  1921. assertEquals(2, hdr.getPropertyCount());
  1922. assertEquals(0, hdr.getPropertyStart());
  1923. assertEquals(POIFSConstants.END_OF_CHAIN, hdr.getXBATIndex());
  1924. // Check the block allocation is unchanged, other than
  1925. // the properties stream going in at the end
  1926. assertEquals(12, fs2.getNextBlock(0)); // Properties
  1927. assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs2.getNextBlock(1));
  1928. assertEquals(POIFSConstants.END_OF_CHAIN, fs2.getNextBlock(2));
  1929. assertEquals(POIFSConstants.END_OF_CHAIN, fs2.getNextBlock(3));
  1930. assertEquals(5, fs2.getNextBlock(4));
  1931. assertEquals(6, fs2.getNextBlock(5));
  1932. assertEquals(7, fs2.getNextBlock(6));
  1933. assertEquals(8, fs2.getNextBlock(7));
  1934. assertEquals(9, fs2.getNextBlock(8));
  1935. assertEquals(10, fs2.getNextBlock(9));
  1936. assertEquals(11, fs2.getNextBlock(10));
  1937. assertEquals(POIFSConstants.END_OF_CHAIN, fs2.getNextBlock(11));
  1938. assertEquals(POIFSConstants.END_OF_CHAIN, fs2.getNextBlock(12));
  1939. assertEquals(POIFSConstants.UNUSED_BLOCK, fs2.getNextBlock(13));
  1940. assertEquals(POIFSConstants.SMALLER_BIG_BLOCK_SIZE * 14, fs2.size());
  1941. // Check the data
  1942. DirectoryEntry fsRoot = fs2.getRoot();
  1943. assertEquals(1, fsRoot.getEntryCount());
  1944. parentDir = (DirectoryEntry) fsRoot.getEntry("Parent Directory");
  1945. assertEquals(1, parentDir.getEntryCount());
  1946. testDir = (DirectoryEntry) parentDir.getEntry("Test Directory");
  1947. assertEquals(2, testDir.getEntryCount());
  1948. miniDoc = (DocumentEntry) testDir.getEntry("Mini");
  1949. assertContentsMatches(mini, miniDoc);
  1950. normDoc = (DocumentEntry) testDir.getEntry("Normal4096");
  1951. assertContentsMatches(main4096, normDoc);
  1952. // Add one more stream to each, then save and re-load
  1953. byte[] mini2 = new byte[]{-42, 0, -1, -2, -3, -4, -42};
  1954. testDir.createDocument("Mini2", new ByteArrayInputStream(mini2));
  1955. // Add to the main stream
  1956. byte[] main4106 = new byte[4106];
  1957. main4106[0] = 41;
  1958. main4106[4105] = 42;
  1959. testDir.createDocument("Normal4106", new ByteArrayInputStream(main4106));
  1960. // Recheck the data in all 4 streams
  1961. try (POIFSFileSystem fs3 = writeOutAndReadBack(fs2)) {
  1962. fsRoot = fs3.getRoot();
  1963. assertEquals(1, fsRoot.getEntryCount());
  1964. parentDir = (DirectoryEntry) fsRoot.getEntry("Parent Directory");
  1965. assertEquals(1, parentDir.getEntryCount());
  1966. testDir = (DirectoryEntry) parentDir.getEntry("Test Directory");
  1967. assertEquals(4, testDir.getEntryCount());
  1968. miniDoc = (DocumentEntry) testDir.getEntry("Mini");
  1969. assertContentsMatches(mini, miniDoc);
  1970. miniDoc = (DocumentEntry) testDir.getEntry("Mini2");
  1971. assertContentsMatches(mini2, miniDoc);
  1972. normDoc = (DocumentEntry) testDir.getEntry("Normal4106");
  1973. assertContentsMatches(main4106, normDoc);
  1974. }
  1975. }
  1976. }
  1977. }
  1978. @Test
  1979. void readZeroLengthEntries() throws IOException {
  1980. try (POIFSFileSystem fs = new POIFSFileSystem(_inst.getFile("only-zero-byte-streams.ole2"))) {
  1981. DirectoryNode testDir = fs.getRoot();
  1982. assertEquals(3, testDir.getEntryCount());
  1983. DocumentEntry entry = (DocumentEntry) testDir.getEntry("test-zero-1");
  1984. assertNotNull(entry);
  1985. assertEquals(0, entry.getSize());
  1986. entry = (DocumentEntry) testDir.getEntry("test-zero-2");
  1987. assertNotNull(entry);
  1988. assertEquals(0, entry.getSize());
  1989. entry = (DocumentEntry) testDir.getEntry("test-zero-3");
  1990. assertNotNull(entry);
  1991. assertEquals(0, entry.getSize());
  1992. // Check properties, all have zero length, no blocks
  1993. PropertyTable props = fs._get_property_table();
  1994. assertEquals(POIFSConstants.END_OF_CHAIN, props.getRoot().getStartBlock());
  1995. for (Property prop : props.getRoot()) {
  1996. assertEquals("test-zero-", prop.getName().substring(0, 10));
  1997. assertEquals(POIFSConstants.END_OF_CHAIN, prop.getStartBlock());
  1998. }
  1999. }
  2000. }
  2001. @Test
  2002. void writeZeroLengthEntries() throws IOException {
  2003. try (POIFSFileSystem fs1 = new POIFSFileSystem()) {
  2004. DirectoryNode testDir = fs1.getRoot();
  2005. DocumentEntry miniDoc;
  2006. DocumentEntry normDoc;
  2007. DocumentEntry emptyDoc;
  2008. // Add mini and normal sized entries to start
  2009. byte[] mini2 = new byte[]{-42, 0, -1, -2, -3, -4, -42};
  2010. testDir.createDocument("Mini2", new ByteArrayInputStream(mini2));
  2011. // Add to the main stream
  2012. byte[] main4106 = new byte[4106];
  2013. main4106[0] = 41;
  2014. main4106[4105] = 42;
  2015. testDir.createDocument("Normal4106", new ByteArrayInputStream(main4106));
  2016. // Now add some empty ones
  2017. byte[] empty = new byte[0];
  2018. testDir.createDocument("empty-1", new ByteArrayInputStream(empty));
  2019. testDir.createDocument("empty-2", new ByteArrayInputStream(empty));
  2020. testDir.createDocument("empty-3", new ByteArrayInputStream(empty));
  2021. // Check
  2022. miniDoc = (DocumentEntry) testDir.getEntry("Mini2");
  2023. assertContentsMatches(mini2, miniDoc);
  2024. normDoc = (DocumentEntry) testDir.getEntry("Normal4106");
  2025. assertContentsMatches(main4106, normDoc);
  2026. emptyDoc = (DocumentEntry) testDir.getEntry("empty-1");
  2027. assertContentsMatches(empty, emptyDoc);
  2028. emptyDoc = (DocumentEntry) testDir.getEntry("empty-2");
  2029. assertContentsMatches(empty, emptyDoc);
  2030. emptyDoc = (DocumentEntry) testDir.getEntry("empty-3");
  2031. assertContentsMatches(empty, emptyDoc);
  2032. // Look at the properties entry, and check the empty ones
  2033. // have zero size and no start block
  2034. PropertyTable props = fs1._get_property_table();
  2035. Iterator<Property> propsIt = props.getRoot().getChildren();
  2036. Property prop = propsIt.next();
  2037. assertEquals("Mini2", prop.getName());
  2038. assertEquals(0, prop.getStartBlock());
  2039. assertEquals(7, prop.getSize());
  2040. prop = propsIt.next();
  2041. assertEquals("Normal4106", prop.getName());
  2042. assertEquals(4, prop.getStartBlock()); // BAT, Props, SBAT, MIni
  2043. assertEquals(4106, prop.getSize());
  2044. prop = propsIt.next();
  2045. assertEquals("empty-1", prop.getName());
  2046. assertEquals(POIFSConstants.END_OF_CHAIN, prop.getStartBlock());
  2047. assertEquals(0, prop.getSize());
  2048. prop = propsIt.next();
  2049. assertEquals("empty-2", prop.getName());
  2050. assertEquals(POIFSConstants.END_OF_CHAIN, prop.getStartBlock());
  2051. assertEquals(0, prop.getSize());
  2052. prop = propsIt.next();
  2053. assertEquals("empty-3", prop.getName());
  2054. assertEquals(POIFSConstants.END_OF_CHAIN, prop.getStartBlock());
  2055. assertEquals(0, prop.getSize());
  2056. // Save and re-check
  2057. try (POIFSFileSystem fs2 = writeOutAndReadBack(fs1)) {
  2058. testDir = fs2.getRoot();
  2059. miniDoc = (DocumentEntry) testDir.getEntry("Mini2");
  2060. assertContentsMatches(mini2, miniDoc);
  2061. normDoc = (DocumentEntry) testDir.getEntry("Normal4106");
  2062. assertContentsMatches(main4106, normDoc);
  2063. emptyDoc = (DocumentEntry) testDir.getEntry("empty-1");
  2064. assertContentsMatches(empty, emptyDoc);
  2065. emptyDoc = (DocumentEntry) testDir.getEntry("empty-2");
  2066. assertContentsMatches(empty, emptyDoc);
  2067. emptyDoc = (DocumentEntry) testDir.getEntry("empty-3");
  2068. assertContentsMatches(empty, emptyDoc);
  2069. // Check that a mini-stream was assigned, with one block used
  2070. assertEquals(3, testDir.getProperty().getStartBlock());
  2071. assertEquals(64, testDir.getProperty().getSize());
  2072. }
  2073. }
  2074. }
  2075. /**
  2076. * Test that we can read a file with POIFS, create a new POIFS instance,
  2077. * write it out, read it with POIFS, and see the original data
  2078. */
  2079. @Test
  2080. void POIFSReadCopyWritePOIFSRead() throws IOException {
  2081. File testFile = POIDataSamples.getSpreadSheetInstance().getFile("Simple.xls");
  2082. try (POIFSFileSystem src = new POIFSFileSystem(testFile);
  2083. POIFSFileSystem nfs = new POIFSFileSystem()) {
  2084. byte[] wbDataExp = IOUtils.toByteArray(src.createDocumentInputStream("Workbook"));
  2085. EntryUtils.copyNodes(src.getRoot(), nfs.getRoot());
  2086. try (POIFSFileSystem pfs = writeOutFileAndReadBack(nfs)) {
  2087. byte[] wbDataAct = IOUtils.toByteArray(pfs.createDocumentInputStream("Workbook"));
  2088. assertThat(wbDataExp, equalTo(wbDataAct));
  2089. }
  2090. }
  2091. }
  2092. /**
  2093. * Ensure that you can recursively delete directories and their
  2094. * contents
  2095. */
  2096. @Test
  2097. void RecursiveDelete() throws IOException {
  2098. File testFile = POIDataSamples.getSpreadSheetInstance().getFile("SimpleMacro.xls");
  2099. try (POIFSFileSystem src = new POIFSFileSystem(testFile)) {
  2100. // Starts out with 5 entries:
  2101. // _VBA_PROJECT_CUR
  2102. // SummaryInformation <(0x05)SummaryInformation>
  2103. // DocumentSummaryInformation <(0x05)DocumentSummaryInformation>
  2104. // Workbook
  2105. // CompObj <(0x01)CompObj>
  2106. assertEquals(5, _countChildren(src._get_property_table().getRoot()));
  2107. assertEquals(5, src.getRoot().getEntryCount());
  2108. // Grab the VBA project root
  2109. DirectoryEntry vbaProj = (DirectoryEntry) src.getRoot().getEntry("_VBA_PROJECT_CUR");
  2110. assertEquals(3, vbaProj.getEntryCount());
  2111. // Can't delete yet, has stuff
  2112. assertFalse(vbaProj.delete());
  2113. // Recursively delete
  2114. _recursiveDeletee(vbaProj);
  2115. // Entries gone
  2116. assertEquals(4, _countChildren(src._get_property_table().getRoot()));
  2117. assertEquals(4, src.getRoot().getEntryCount());
  2118. }
  2119. }
  2120. private void _recursiveDeletee(Entry entry) throws IOException {
  2121. if (entry.isDocumentEntry()) {
  2122. assertTrue(entry.delete());
  2123. return;
  2124. }
  2125. DirectoryEntry dir = (DirectoryEntry) entry;
  2126. String[] names = dir.getEntryNames().toArray(new String[dir.getEntryCount()]);
  2127. for (String name : names) {
  2128. Entry ce = dir.getEntry(name);
  2129. _recursiveDeletee(ce);
  2130. }
  2131. assertTrue(dir.delete());
  2132. }
  2133. @SuppressWarnings("unused")
  2134. private int _countChildren(DirectoryProperty p) {
  2135. int count = 0;
  2136. for (Property cp : p) {
  2137. count++;
  2138. }
  2139. return count;
  2140. }
  2141. /**
  2142. * To ensure we can create a file >2gb in size, as well as to
  2143. * extend existing files past the 2gb boundary.
  2144. * <p>
  2145. * Note that to run this test, you will require 2.5+gb of free
  2146. * space on your TMP/TEMP partition/disk
  2147. * <p>
  2148. * Note that to run this test, you need to be able to mmap 2.5+gb
  2149. * files, which may need bigger kernel.shmmax and vm.max_map_count
  2150. * settings on Linux.
  2151. * <p>
  2152. * TODO Fix this to work...
  2153. */
  2154. @Test
  2155. @Disabled("Work in progress test for #60670")
  2156. void creationAndExtensionPast2GB() throws Exception {
  2157. File big = TempFile.createTempFile("poi-test-", ".ole2");
  2158. assumeTrue(big.getFreeSpace() > 2.5 * 1024 * 1024 * 1024,
  2159. "2.5gb of free space is required on your tmp/temp partition/disk to run large file tests");
  2160. System.out.println("Slow, memory heavy test in progress....");
  2161. int s100mb = 100 * 1024 * 1024;
  2162. int s512mb = 512 * 1024 * 1024;
  2163. long s2gb = 2L * 1024 * 1024 * 1024;
  2164. DocumentEntry entry;
  2165. // Create a just-sub 2gb file
  2166. try (POIFSFileSystem fs = POIFSFileSystem.create(big)) {
  2167. for (int i = 0; i < 19; i++) {
  2168. fs.createDocument(new DummyDataInputStream(s100mb), "Entry" + i);
  2169. }
  2170. fs.writeFilesystem();
  2171. }
  2172. // Extend it past the 2gb mark
  2173. try (POIFSFileSystem fs = new POIFSFileSystem(big, false)) {
  2174. for (int i = 0; i < 19; i++) {
  2175. entry = (DocumentEntry) fs.getRoot().getEntry("Entry" + i);
  2176. assertNotNull(entry);
  2177. assertEquals(s100mb, entry.getSize());
  2178. }
  2179. fs.createDocument(new DummyDataInputStream(s512mb), "Bigger");
  2180. fs.writeFilesystem();
  2181. }
  2182. // Check it still works
  2183. try (POIFSFileSystem fs = new POIFSFileSystem(big, false)) {
  2184. for (int i = 0; i < 19; i++) {
  2185. entry = (DocumentEntry) fs.getRoot().getEntry("Entry" + i);
  2186. assertNotNull(entry);
  2187. assertEquals(s100mb, entry.getSize());
  2188. }
  2189. entry = (DocumentEntry) fs.getRoot().getEntry("Bigger");
  2190. assertNotNull(entry);
  2191. assertEquals(s512mb, entry.getSize());
  2192. }
  2193. // Tidy
  2194. assertTrue(big.delete());
  2195. // Create a >2gb file
  2196. try (POIFSFileSystem fs = POIFSFileSystem.create(big)) {
  2197. for (int i = 0; i < 4; i++) {
  2198. fs.createDocument(new DummyDataInputStream(s512mb), "Entry" + i);
  2199. }
  2200. fs.writeFilesystem();
  2201. }
  2202. // Read it
  2203. try (POIFSFileSystem fs = new POIFSFileSystem(big, false)) {
  2204. for (int i = 0; i < 4; i++) {
  2205. entry = (DocumentEntry) fs.getRoot().getEntry("Entry" + i);
  2206. assertNotNull(entry);
  2207. assertEquals(s512mb, entry.getSize());
  2208. }
  2209. // Extend it
  2210. fs.createDocument(new DummyDataInputStream(s512mb), "Entry4");
  2211. fs.writeFilesystem();
  2212. }
  2213. // Check it worked
  2214. try (POIFSFileSystem fs = new POIFSFileSystem(big, false)) {
  2215. for (int i = 0; i < 5; i++) {
  2216. entry = (DocumentEntry) fs.getRoot().getEntry("Entry" + i);
  2217. assertNotNull(entry);
  2218. assertEquals(s512mb, entry.getSize());
  2219. }
  2220. }
  2221. // Tidy
  2222. assertTrue(big.delete());
  2223. // Create a file with a 2gb entry
  2224. try (POIFSFileSystem fs = POIFSFileSystem.create(big)) {
  2225. fs.createDocument(new DummyDataInputStream(s100mb), "Small");
  2226. // TODO Check we get a helpful error about the max size
  2227. fs.createDocument(new DummyDataInputStream(s2gb), "Big");
  2228. }
  2229. }
  2230. private static final class DummyDataInputStream extends InputStream {
  2231. private final long maxSize;
  2232. private long size;
  2233. private DummyDataInputStream(long maxSize) {
  2234. this.maxSize = maxSize;
  2235. this.size = 0;
  2236. }
  2237. @Override
  2238. public int read() {
  2239. if (size >= maxSize) return -1;
  2240. size++;
  2241. return (int) (size % 128);
  2242. }
  2243. @Override
  2244. public int read(byte[] b) {
  2245. return read(b, 0, b.length);
  2246. }
  2247. @Override
  2248. public int read(byte[] b, int offset, int len) {
  2249. if (size >= maxSize) return -1;
  2250. int sz = (int) Math.min(len, maxSize - size);
  2251. for (int i = 0; i < sz; i++) {
  2252. b[i + offset] = (byte) ((size + i) % 128);
  2253. }
  2254. size += sz;
  2255. return sz;
  2256. }
  2257. }
  2258. @Disabled("Takes a long time to run")
  2259. @Test
  2260. void performance() throws Exception {
  2261. int iterations = 200;//1_000;
  2262. long start = System.currentTimeMillis();
  2263. for (int i = 0; i < iterations; i++) {
  2264. try (InputStream inputStream = POIDataSamples.getHSMFInstance().openResourceAsStream("lots-of-recipients.msg");
  2265. POIFSFileSystem srcFileSystem = new POIFSFileSystem(inputStream);
  2266. POIFSFileSystem destFileSystem = new POIFSFileSystem()) {
  2267. copyAllEntries(srcFileSystem.getRoot(), destFileSystem.getRoot());
  2268. File file = File.createTempFile("npoi", ".dat");
  2269. try (OutputStream outputStream = new FileOutputStream(file)) {
  2270. destFileSystem.writeFilesystem(outputStream);
  2271. }
  2272. assertTrue(file.delete());
  2273. if (i % 10 == 0) System.out.print(".");
  2274. }
  2275. }
  2276. System.out.println("NPOI took: " + (System.currentTimeMillis() - start));
  2277. }
  2278. private static void copyAllEntries(DirectoryEntry srcDirectory, DirectoryEntry destDirectory) throws IOException {
  2279. Iterator<Entry> iterator = srcDirectory.getEntries();
  2280. while (iterator.hasNext()) {
  2281. Entry entry = iterator.next();
  2282. if (entry.isDirectoryEntry()) {
  2283. DirectoryEntry childDest = destDirectory.createDirectory(entry.getName());
  2284. copyAllEntries((DirectoryEntry) entry, childDest);
  2285. } else {
  2286. DocumentEntry srcEntry = (DocumentEntry) entry;
  2287. try (InputStream inputStream = new DocumentInputStream(srcEntry)) {
  2288. destDirectory.createDocument(entry.getName(), inputStream);
  2289. }
  2290. }
  2291. }
  2292. }
  2293. }