/* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ package org.apache.poi.poifs.filesystem; import static org.apache.poi.poifs.filesystem.TestNPOIFSFileSystem.writeOutAndReadBack; import java.io.ByteArrayInputStream; import java.nio.ByteBuffer; import java.util.Iterator; import junit.framework.TestCase; import org.apache.poi.POIDataSamples; import org.apache.poi.poifs.common.POIFSConstants; import org.apache.poi.poifs.storage.BATBlock; /** * Tests {@link NPOIFSStream} */ @SuppressWarnings("resource") public final class TestNPOIFSStream extends TestCase { private static final POIDataSamples _inst = POIDataSamples.getPOIFSInstance(); /** * Read a single block stream */ public void testReadTinyStream() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); // 98 is actually the last block in a two block stream... NPOIFSStream stream = new NPOIFSStream(fs, 98); Iterator<ByteBuffer> i = stream.getBlockIterator(); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); ByteBuffer b = i.next(); assertEquals(false, i.hasNext()); assertEquals(false, i.hasNext()); assertEquals(false, i.hasNext()); // Check the contents assertEquals((byte)0x81, b.get()); assertEquals((byte)0x00, b.get()); assertEquals((byte)0x00, b.get()); assertEquals((byte)0x00, b.get()); assertEquals((byte)0x82, b.get()); assertEquals((byte)0x00, b.get()); assertEquals((byte)0x00, b.get()); assertEquals((byte)0x00, b.get()); fs.close(); } /** * Read a stream with only two blocks in it */ public void testReadShortStream() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); // 97 -> 98 -> end NPOIFSStream stream = new NPOIFSStream(fs, 97); Iterator<ByteBuffer> i = stream.getBlockIterator(); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); ByteBuffer b97 = i.next(); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); ByteBuffer b98 = i.next(); assertEquals(false, i.hasNext()); assertEquals(false, i.hasNext()); assertEquals(false, i.hasNext()); // Check the contents of the 1st block assertEquals((byte)0x01, b97.get()); assertEquals((byte)0x00, b97.get()); assertEquals((byte)0x00, b97.get()); assertEquals((byte)0x00, b97.get()); assertEquals((byte)0x02, b97.get()); assertEquals((byte)0x00, b97.get()); assertEquals((byte)0x00, b97.get()); assertEquals((byte)0x00, b97.get()); // Check the contents of the 2nd block assertEquals((byte)0x81, b98.get()); assertEquals((byte)0x00, b98.get()); assertEquals((byte)0x00, b98.get()); assertEquals((byte)0x00, b98.get()); assertEquals((byte)0x82, b98.get()); assertEquals((byte)0x00, b98.get()); assertEquals((byte)0x00, b98.get()); assertEquals((byte)0x00, b98.get()); fs.close(); } /** * Read a stream with many blocks */ public void testReadLongerStream() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); ByteBuffer b0 = null; ByteBuffer b1 = null; ByteBuffer b22 = null; // The stream at 0 has 23 blocks in it NPOIFSStream stream = new NPOIFSStream(fs, 0); Iterator<ByteBuffer> i = stream.getBlockIterator(); int count = 0; while(i.hasNext()) { ByteBuffer b = i.next(); if(count == 0) { b0 = b; } if(count == 1) { b1 = b; } if(count == 22) { b22 = b; } count++; } assertEquals(23, count); // Check the contents // 1st block is at 0 assertEquals((byte)0x9e, b0.get()); assertEquals((byte)0x75, b0.get()); assertEquals((byte)0x97, b0.get()); assertEquals((byte)0xf6, b0.get()); // 2nd block is at 1 assertEquals((byte)0x86, b1.get()); assertEquals((byte)0x09, b1.get()); assertEquals((byte)0x22, b1.get()); assertEquals((byte)0xfb, b1.get()); // last block is at 89 assertEquals((byte)0xfe, b22.get()); assertEquals((byte)0xff, b22.get()); assertEquals((byte)0x00, b22.get()); assertEquals((byte)0x00, b22.get()); assertEquals((byte)0x05, b22.get()); assertEquals((byte)0x01, b22.get()); assertEquals((byte)0x02, b22.get()); assertEquals((byte)0x00, b22.get()); fs.close(); } /** * Read a stream with several blocks in a 4096 byte block file */ public void testReadStream4096() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize4096.zvi")); // 0 -> 1 -> 2 -> end NPOIFSStream stream = new NPOIFSStream(fs, 0); Iterator<ByteBuffer> i = stream.getBlockIterator(); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); ByteBuffer b0 = i.next(); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); ByteBuffer b1 = i.next(); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); ByteBuffer b2 = i.next(); assertEquals(false, i.hasNext()); assertEquals(false, i.hasNext()); assertEquals(false, i.hasNext()); // Check the contents of the 1st block assertEquals((byte)0x9E, b0.get()); assertEquals((byte)0x75, b0.get()); assertEquals((byte)0x97, b0.get()); assertEquals((byte)0xF6, b0.get()); assertEquals((byte)0xFF, b0.get()); assertEquals((byte)0x21, b0.get()); assertEquals((byte)0xD2, b0.get()); assertEquals((byte)0x11, b0.get()); // Check the contents of the 2nd block assertEquals((byte)0x00, b1.get()); assertEquals((byte)0x00, b1.get()); assertEquals((byte)0x03, b1.get()); assertEquals((byte)0x00, b1.get()); assertEquals((byte)0x00, b1.get()); assertEquals((byte)0x00, b1.get()); assertEquals((byte)0x00, b1.get()); assertEquals((byte)0x00, b1.get()); // Check the contents of the 3rd block assertEquals((byte)0x6D, b2.get()); assertEquals((byte)0x00, b2.get()); assertEquals((byte)0x00, b2.get()); assertEquals((byte)0x00, b2.get()); assertEquals((byte)0x03, b2.get()); assertEquals((byte)0x00, b2.get()); assertEquals((byte)0x46, b2.get()); assertEquals((byte)0x00, b2.get()); fs.close(); } /** * Craft a nasty file with a loop, and ensure we don't get stuck */ public void testReadFailsOnLoop() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); // Hack the FAT so that it goes 0->1->2->0 fs.setNextBlock(0, 1); fs.setNextBlock(1, 2); fs.setNextBlock(2, 0); // Now try to read NPOIFSStream stream = new NPOIFSStream(fs, 0); Iterator<ByteBuffer> i = stream.getBlockIterator(); assertEquals(true, i.hasNext()); // 1st read works i.next(); assertEquals(true, i.hasNext()); // 2nd read works i.next(); assertEquals(true, i.hasNext()); // 3rd read works i.next(); assertEquals(true, i.hasNext()); // 4th read blows up as it loops back to 0 try { i.next(); fail("Loop should have been detected but wasn't!"); } catch(RuntimeException e) { // Good, it was detected } assertEquals(true, i.hasNext()); fs.close(); } /** * Tests that we can load some streams that are * stored in the mini stream. */ public void testReadMiniStreams() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi")); NPOIFSMiniStore ministore = fs.getMiniStore(); // 178 -> 179 -> 180 -> end NPOIFSStream stream = new NPOIFSStream(ministore, 178); Iterator<ByteBuffer> i = stream.getBlockIterator(); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); ByteBuffer b178 = i.next(); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); ByteBuffer b179 = i.next(); assertEquals(true, i.hasNext()); ByteBuffer b180 = i.next(); assertEquals(false, i.hasNext()); assertEquals(false, i.hasNext()); assertEquals(false, i.hasNext()); // Check the contents of the 1st block assertEquals((byte)0xfe, b178.get()); assertEquals((byte)0xff, b178.get()); assertEquals((byte)0x00, b178.get()); assertEquals((byte)0x00, b178.get()); assertEquals((byte)0x05, b178.get()); assertEquals((byte)0x01, b178.get()); assertEquals((byte)0x02, b178.get()); assertEquals((byte)0x00, b178.get()); // And the 2nd assertEquals((byte)0x6c, b179.get()); assertEquals((byte)0x00, b179.get()); assertEquals((byte)0x00, b179.get()); assertEquals((byte)0x00, b179.get()); assertEquals((byte)0x28, b179.get()); assertEquals((byte)0x00, b179.get()); assertEquals((byte)0x00, b179.get()); assertEquals((byte)0x00, b179.get()); // And the 3rd assertEquals((byte)0x30, b180.get()); assertEquals((byte)0x00, b180.get()); assertEquals((byte)0x00, b180.get()); assertEquals((byte)0x00, b180.get()); assertEquals((byte)0x00, b180.get()); assertEquals((byte)0x00, b180.get()); assertEquals((byte)0x00, b180.get()); assertEquals((byte)0x80, b180.get()); fs.close(); } /** * Writing the same amount of data as before */ public void testReplaceStream() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi")); byte[] data = new byte[512]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i%256); } // 98 is actually the last block in a two block stream... NPOIFSStream stream = new NPOIFSStream(fs, 98); stream.updateContents(data); // Check the reading of blocks Iterator<ByteBuffer> it = stream.getBlockIterator(); assertEquals(true, it.hasNext()); ByteBuffer b = it.next(); assertEquals(false, it.hasNext()); // Now check the contents data = new byte[512]; b.get(data); for(int i=0; i<data.length; i++) { byte exp = (byte)(i%256); assertEquals(exp, data[i]); } fs.close(); } /** * Writes less data than before, some blocks will need * to be freed */ public void testReplaceStreamWithLess() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi")); byte[] data = new byte[512]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i%256); } // 97 -> 98 -> end assertEquals(98, fs.getNextBlock(97)); assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(98)); // Create a 2 block stream, will become a 1 block one NPOIFSStream stream = new NPOIFSStream(fs, 97); stream.updateContents(data); // 97 should now be the end, and 98 free assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(97)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(98)); // Check the reading of blocks Iterator<ByteBuffer> it = stream.getBlockIterator(); assertEquals(true, it.hasNext()); ByteBuffer b = it.next(); assertEquals(false, it.hasNext()); // Now check the contents data = new byte[512]; b.get(data); for(int i=0; i<data.length; i++) { byte exp = (byte)(i%256); assertEquals(exp, data[i]); } fs.close(); } /** * Writes more data than before, new blocks will be needed */ public void testReplaceStreamWithMore() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi")); byte[] data = new byte[512*3]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i%256); } // 97 -> 98 -> end assertEquals(98, fs.getNextBlock(97)); assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(98)); // 100 is our first free one assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(100)); // Create a 2 block stream, will become a 3 block one NPOIFSStream stream = new NPOIFSStream(fs, 97); stream.updateContents(data); // 97 -> 98 -> 100 -> end assertEquals(98, fs.getNextBlock(97)); assertEquals(100, fs.getNextBlock(98)); assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(100)); // Check the reading of blocks Iterator<ByteBuffer> it = stream.getBlockIterator(); int count = 0; while(it.hasNext()) { ByteBuffer b = it.next(); data = new byte[512]; b.get(data); for(int i=0; i<data.length; i++) { byte exp = (byte)(i%256); assertEquals(exp, data[i]); } count++; } assertEquals(3, count); fs.close(); } /** * Writes to a new stream in the file */ public void testWriteNewStream() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi")); // 100 is our first free one assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(100)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(101)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(102)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(103)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(104)); // Add a single block one byte[] data = new byte[512]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i%256); } NPOIFSStream stream = new NPOIFSStream(fs); stream.updateContents(data); // Check it was allocated properly assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99)); assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(100)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(101)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(102)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(103)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(104)); // And check the contents Iterator<ByteBuffer> it = stream.getBlockIterator(); int count = 0; while(it.hasNext()) { ByteBuffer b = it.next(); data = new byte[512]; b.get(data); for(int i=0; i<data.length; i++) { byte exp = (byte)(i%256); assertEquals(exp, data[i]); } count++; } assertEquals(1, count); // And a multi block one data = new byte[512*3]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i%256); } stream = new NPOIFSStream(fs); stream.updateContents(data); // Check it was allocated properly assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99)); assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(100)); assertEquals(102, fs.getNextBlock(101)); assertEquals(103, fs.getNextBlock(102)); assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(103)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(104)); // And check the contents it = stream.getBlockIterator(); count = 0; while(it.hasNext()) { ByteBuffer b = it.next(); data = new byte[512]; b.get(data); for(int i=0; i<data.length; i++) { byte exp = (byte)(i%256); assertEquals(exp, data[i]); } count++; } assertEquals(3, count); // Free it stream.free(); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99)); assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(100)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(101)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(102)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(103)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(104)); fs.close(); } /** * Writes to a new stream in the file, where we've not enough * free blocks so new FAT segments will need to be allocated * to support this */ public void testWriteNewStreamExtraFATs() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi")); // Allocate almost all the blocks assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(100)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(127)); for(int i=100; i<127; i++) { fs.setNextBlock(i, POIFSConstants.END_OF_CHAIN); } assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(127)); assertEquals(true, fs.getBATBlockAndIndex(0).getBlock().hasFreeSectors()); // Write a 3 block stream byte[] data = new byte[512*3]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i%256); } NPOIFSStream stream = new NPOIFSStream(fs); stream.updateContents(data); // Check we got another BAT assertEquals(false, fs.getBATBlockAndIndex(0).getBlock().hasFreeSectors()); assertEquals(true, fs.getBATBlockAndIndex(128).getBlock().hasFreeSectors()); // the BAT will be in the first spot of the new block assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(126)); assertEquals(129, fs.getNextBlock(127)); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(128)); assertEquals(130, fs.getNextBlock(129)); assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(130)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(131)); fs.close(); } /** * Replaces data in an existing stream, with a bit * more data than before, in a 4096 byte block file */ public void testWriteStream4096() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize4096.zvi")); // 0 -> 1 -> 2 -> end assertEquals(1, fs.getNextBlock(0)); assertEquals(2, fs.getNextBlock(1)); assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(2)); assertEquals(4, fs.getNextBlock(3)); // First free one is at 15 assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(14)); assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(15)); // Write a 5 block file byte[] data = new byte[4096*5]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i%256); } NPOIFSStream stream = new NPOIFSStream(fs, 0); stream.updateContents(data); // Check it assertEquals(1, fs.getNextBlock(0)); assertEquals(2, fs.getNextBlock(1)); assertEquals(15, fs.getNextBlock(2)); // Jumps assertEquals(4, fs.getNextBlock(3)); // Next stream assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(14)); assertEquals(16, fs.getNextBlock(15)); // Continues assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(16)); // Ends assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(17)); // Free // Check the contents too Iterator<ByteBuffer> it = stream.getBlockIterator(); int count = 0; while(it.hasNext()) { ByteBuffer b = it.next(); data = new byte[512]; b.get(data); for(int i=0; i<data.length; i++) { byte exp = (byte)(i%256); assertEquals(exp, data[i]); } count++; } assertEquals(5, count); fs.close(); } /** * Tests that we can write into the mini stream */ public void testWriteMiniStreams() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi")); NPOIFSMiniStore ministore = fs.getMiniStore(); NPOIFSStream stream = new NPOIFSStream(ministore, 178); // 178 -> 179 -> 180 -> end assertEquals(179, ministore.getNextBlock(178)); assertEquals(180, ministore.getNextBlock(179)); assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(180)); // Try writing 3 full blocks worth byte[] data = new byte[64*3]; for(int i=0; i<data.length; i++) { data[i] = (byte)i; } stream = new NPOIFSStream(ministore, 178); stream.updateContents(data); // Check assertEquals(179, ministore.getNextBlock(178)); assertEquals(180, ministore.getNextBlock(179)); assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(180)); stream = new NPOIFSStream(ministore, 178); Iterator<ByteBuffer> it = stream.getBlockIterator(); ByteBuffer b178 = it.next(); ByteBuffer b179 = it.next(); ByteBuffer b180 = it.next(); assertEquals(false, it.hasNext()); assertEquals((byte)0x00, b178.get()); assertEquals((byte)0x01, b178.get()); assertEquals((byte)0x40, b179.get()); assertEquals((byte)0x41, b179.get()); assertEquals((byte)0x80, b180.get()); assertEquals((byte)0x81, b180.get()); // Try writing just into 3 blocks worth data = new byte[64*2 + 12]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i+4); } stream = new NPOIFSStream(ministore, 178); stream.updateContents(data); // Check assertEquals(179, ministore.getNextBlock(178)); assertEquals(180, ministore.getNextBlock(179)); assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(180)); stream = new NPOIFSStream(ministore, 178); it = stream.getBlockIterator(); b178 = it.next(); b179 = it.next(); b180 = it.next(); assertEquals(false, it.hasNext()); assertEquals((byte)0x04, b178.get(0)); assertEquals((byte)0x05, b178.get(1)); assertEquals((byte)0x44, b179.get(0)); assertEquals((byte)0x45, b179.get(1)); assertEquals((byte)0x84, b180.get(0)); assertEquals((byte)0x85, b180.get(1)); // Try writing 1, should truncate data = new byte[12]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i+9); } stream = new NPOIFSStream(ministore, 178); stream.updateContents(data); assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(178)); assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(179)); assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(180)); stream = new NPOIFSStream(ministore, 178); it = stream.getBlockIterator(); b178 = it.next(); assertEquals(false, it.hasNext()); assertEquals((byte)0x09, b178.get(0)); assertEquals((byte)0x0a, b178.get(1)); // Try writing 5, should extend assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(178)); assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(179)); assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(180)); assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(181)); assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(182)); assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(183)); data = new byte[64*4 + 12]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i+3); } stream = new NPOIFSStream(ministore, 178); stream.updateContents(data); assertEquals(179, ministore.getNextBlock(178)); assertEquals(180, ministore.getNextBlock(179)); assertEquals(181, ministore.getNextBlock(180)); assertEquals(182, ministore.getNextBlock(181)); assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(182)); stream = new NPOIFSStream(ministore, 178); it = stream.getBlockIterator(); b178 = it.next(); b179 = it.next(); b180 = it.next(); ByteBuffer b181 = it.next(); ByteBuffer b182 = it.next(); assertEquals(false, it.hasNext()); assertEquals((byte)0x03, b178.get(0)); assertEquals((byte)0x04, b178.get(1)); assertEquals((byte)0x43, b179.get(0)); assertEquals((byte)0x44, b179.get(1)); assertEquals((byte)0x83, b180.get(0)); assertEquals((byte)0x84, b180.get(1)); assertEquals((byte)0xc3, b181.get(0)); assertEquals((byte)0xc4, b181.get(1)); assertEquals((byte)0x03, b182.get(0)); assertEquals((byte)0x04, b182.get(1)); // Write lots, so it needs another big block ministore.getBlockAt(183); try { ministore.getBlockAt(184); fail("Block 184 should be off the end of the list"); } catch(IndexOutOfBoundsException e) {} data = new byte[64*6 + 12]; for(int i=0; i<data.length; i++) { data[i] = (byte)(i+1); } stream = new NPOIFSStream(ministore, 178); stream.updateContents(data); // Should have added 2 more blocks to the chain assertEquals(179, ministore.getNextBlock(178)); assertEquals(180, ministore.getNextBlock(179)); assertEquals(181, ministore.getNextBlock(180)); assertEquals(182, ministore.getNextBlock(181)); assertEquals(183, ministore.getNextBlock(182)); assertEquals(184, ministore.getNextBlock(183)); assertEquals(POIFSConstants.END_OF_CHAIN, ministore.getNextBlock(184)); assertEquals(POIFSConstants.UNUSED_BLOCK, ministore.getNextBlock(185)); // Block 184 should exist ministore.getBlockAt(183); ministore.getBlockAt(184); ministore.getBlockAt(185); // Check contents stream = new NPOIFSStream(ministore, 178); it = stream.getBlockIterator(); b178 = it.next(); b179 = it.next(); b180 = it.next(); b181 = it.next(); b182 = it.next(); ByteBuffer b183 = it.next(); ByteBuffer b184 = it.next(); assertEquals(false, it.hasNext()); assertEquals((byte)0x01, b178.get(0)); assertEquals((byte)0x02, b178.get(1)); assertEquals((byte)0x41, b179.get(0)); assertEquals((byte)0x42, b179.get(1)); assertEquals((byte)0x81, b180.get(0)); assertEquals((byte)0x82, b180.get(1)); assertEquals((byte)0xc1, b181.get(0)); assertEquals((byte)0xc2, b181.get(1)); assertEquals((byte)0x01, b182.get(0)); assertEquals((byte)0x02, b182.get(1)); assertEquals((byte)0x41, b183.get(0)); assertEquals((byte)0x42, b183.get(1)); assertEquals((byte)0x81, b184.get(0)); assertEquals((byte)0x82, b184.get(1)); fs.close(); } /** * Craft a nasty file with a loop, and ensure we don't get stuck */ public void testWriteFailsOnLoop() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); // Hack the FAT so that it goes 0->1->2->0 fs.setNextBlock(0, 1); fs.setNextBlock(1, 2); fs.setNextBlock(2, 0); // Try to write a large amount, should fail on the write byte[] data = new byte[512*4]; NPOIFSStream stream = new NPOIFSStream(fs, 0); try { stream.updateContents(data); fail("Loop should have been detected but wasn't!"); } catch(IllegalStateException e) {} // Now reset, and try on a small bit // Should fail during the freeing set fs.setNextBlock(0, 1); fs.setNextBlock(1, 2); fs.setNextBlock(2, 0); data = new byte[512]; stream = new NPOIFSStream(fs, 0); try { stream.updateContents(data); fail("Loop should have been detected but wasn't!"); } catch(IllegalStateException e) {} fs.close(); } /** * Tests adding a new stream, writing and reading it. */ public void testReadWriteNewStream() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(); NPOIFSStream stream = new NPOIFSStream(fs); // Check our filesystem has Properties then BAT assertEquals(2, fs.getFreeBlock()); BATBlock bat = fs.getBATBlockAndIndex(0).getBlock(); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK,bat.getValueAt(1)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(2)); // Check the stream as-is assertEquals(POIFSConstants.END_OF_CHAIN, stream.getStartBlock()); try { stream.getBlockIterator(); fail("Shouldn't be able to get an iterator before writing"); } catch(IllegalStateException e) {} // Write in two blocks byte[] data = new byte[512+20]; for(int i=0; i<512; i++) { data[i] = (byte)(i%256); } for(int i=512; i<data.length; i++) { data[i] = (byte)(i%256 + 100); } stream.updateContents(data); // Check now assertEquals(4, fs.getFreeBlock()); bat = fs.getBATBlockAndIndex(0).getBlock(); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK,bat.getValueAt(1)); assertEquals(3, bat.getValueAt(2)); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(3)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(4)); Iterator<ByteBuffer> it = stream.getBlockIterator(); assertEquals(true, it.hasNext()); ByteBuffer b = it.next(); byte[] read = new byte[512]; b.get(read); for(int i=0; i<read.length; i++) { assertEquals("Wrong value at " + i, data[i], read[i]); } assertEquals(true, it.hasNext()); b = it.next(); read = new byte[512]; b.get(read); for(int i=0; i<20; i++) { assertEquals(data[i+512], read[i]); } for(int i=20; i<read.length; i++) { assertEquals(0, read[i]); } assertEquals(false, it.hasNext()); fs.close(); } /** * Writes a stream, then replaces it */ public void testWriteThenReplace() throws Exception { NPOIFSFileSystem fs = new NPOIFSFileSystem(); // Starts empty, other that Properties and BAT BATBlock bat = fs.getBATBlockAndIndex(0).getBlock(); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK,bat.getValueAt(1)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(2)); // Write something that uses a main stream byte[] main4106 = new byte[4106]; main4106[0] = -10; main4106[4105] = -11; DocumentEntry normal = fs.getRoot().createDocument( "Normal", new ByteArrayInputStream(main4106)); // Should have used 9 blocks assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK,bat.getValueAt(1)); assertEquals(3, bat.getValueAt(2)); assertEquals(4, bat.getValueAt(3)); assertEquals(5, bat.getValueAt(4)); assertEquals(6, bat.getValueAt(5)); assertEquals(7, bat.getValueAt(6)); assertEquals(8, bat.getValueAt(7)); assertEquals(9, bat.getValueAt(8)); assertEquals(10, bat.getValueAt(9)); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(10)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(11)); normal = (DocumentEntry)fs.getRoot().getEntry("Normal"); assertEquals(4106, normal.getSize()); assertEquals(4106, ((DocumentNode)normal).getProperty().getSize()); // Replace with one still big enough for a main stream, but one block smaller byte[] main4096 = new byte[4096]; main4096[0] = -10; main4096[4095] = -11; NDocumentOutputStream nout = new NDocumentOutputStream(normal); nout.write(main4096); nout.close(); // Will have dropped to 8 assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK,bat.getValueAt(1)); assertEquals(3, bat.getValueAt(2)); assertEquals(4, bat.getValueAt(3)); assertEquals(5, bat.getValueAt(4)); assertEquals(6, bat.getValueAt(5)); assertEquals(7, bat.getValueAt(6)); assertEquals(8, bat.getValueAt(7)); assertEquals(9, bat.getValueAt(8)); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(9)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(10)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(11)); normal = (DocumentEntry)fs.getRoot().getEntry("Normal"); assertEquals(4096, normal.getSize()); assertEquals(4096, ((DocumentNode)normal).getProperty().getSize()); // Write and check fs = writeOutAndReadBack(fs); bat = fs.getBATBlockAndIndex(0).getBlock(); // No change after write assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); // Properties assertEquals(POIFSConstants.FAT_SECTOR_BLOCK,bat.getValueAt(1)); assertEquals(3, bat.getValueAt(2)); assertEquals(4, bat.getValueAt(3)); assertEquals(5, bat.getValueAt(4)); assertEquals(6, bat.getValueAt(5)); assertEquals(7, bat.getValueAt(6)); assertEquals(8, bat.getValueAt(7)); assertEquals(9, bat.getValueAt(8)); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(9)); // End of Normal assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(10)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(11)); normal = (DocumentEntry)fs.getRoot().getEntry("Normal"); assertEquals(4096, normal.getSize()); assertEquals(4096, ((DocumentNode)normal).getProperty().getSize()); // Make longer, take 1 block at the end normal = (DocumentEntry)fs.getRoot().getEntry("Normal"); nout = new NDocumentOutputStream(normal); nout.write(main4106); nout.close(); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK,bat.getValueAt(1)); assertEquals(3, bat.getValueAt(2)); assertEquals(4, bat.getValueAt(3)); assertEquals(5, bat.getValueAt(4)); assertEquals(6, bat.getValueAt(5)); assertEquals(7, bat.getValueAt(6)); assertEquals(8, bat.getValueAt(7)); assertEquals(9, bat.getValueAt(8)); assertEquals(10, bat.getValueAt(9)); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(10)); // Normal assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(11)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(12)); normal = (DocumentEntry)fs.getRoot().getEntry("Normal"); assertEquals(4106, normal.getSize()); assertEquals(4106, ((DocumentNode)normal).getProperty().getSize()); // Make it small, will trigger the SBAT stream and free lots up byte[] mini = new byte[] { 42, 0, 1, 2, 3, 4, 42 }; normal = (DocumentEntry)fs.getRoot().getEntry("Normal"); nout = new NDocumentOutputStream(normal); nout.write(mini); nout.close(); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1)); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(2)); // SBAT assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(3)); // Mini Stream assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(4)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(5)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(6)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(7)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(8)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(9)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(10)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(11)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(12)); normal = (DocumentEntry)fs.getRoot().getEntry("Normal"); assertEquals(7, normal.getSize()); assertEquals(7, ((DocumentNode)normal).getProperty().getSize()); // Finally back to big again nout = new NDocumentOutputStream(normal); nout.write(main4096); nout.close(); // Will keep the mini stream, now empty assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1)); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(2)); // SBAT assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(3)); // Mini Stream assertEquals(5, bat.getValueAt(4)); assertEquals(6, bat.getValueAt(5)); assertEquals(7, bat.getValueAt(6)); assertEquals(8, bat.getValueAt(7)); assertEquals(9, bat.getValueAt(8)); assertEquals(10, bat.getValueAt(9)); assertEquals(11, bat.getValueAt(10)); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(11)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(12)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(13)); normal = (DocumentEntry)fs.getRoot().getEntry("Normal"); assertEquals(4096, normal.getSize()); assertEquals(4096, ((DocumentNode)normal).getProperty().getSize()); // Save, re-load, re-check fs = writeOutAndReadBack(fs); bat = fs.getBATBlockAndIndex(0).getBlock(); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(0)); assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, bat.getValueAt(1)); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(2)); // SBAT assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(3)); // Mini Stream assertEquals(5, bat.getValueAt(4)); assertEquals(6, bat.getValueAt(5)); assertEquals(7, bat.getValueAt(6)); assertEquals(8, bat.getValueAt(7)); assertEquals(9, bat.getValueAt(8)); assertEquals(10, bat.getValueAt(9)); assertEquals(11, bat.getValueAt(10)); assertEquals(POIFSConstants.END_OF_CHAIN, bat.getValueAt(11)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(12)); assertEquals(POIFSConstants.UNUSED_BLOCK, bat.getValueAt(13)); normal = (DocumentEntry)fs.getRoot().getEntry("Normal"); assertEquals(4096, normal.getSize()); assertEquals(4096, ((DocumentNode)normal).getProperty().getSize()); fs.close(); } }