blob: f1c8c180ae31d60e15208e6c71559a9926e80e39 [file] [log] [blame]
/* ====================================================================
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();
}
}