/** @return number of big blocks making up this document */ int countBlocks() { if (isValid()) { if (_writer == null) { return bigBlocks.length; } return (_size + _bigBlockSize.getBigBlockSize() - 1) / _bigBlockSize.getBigBlockSize(); } return 0; }
/** * Read and process the PropertiesTable and the FAT / XFAT blocks, so that we're ready to work * with the file */ private void readCoreContents() throws IOException { // Grab the block size bigBlockSize = _header.getBigBlockSize(); // Each block should only ever be used by one of the // FAT, XFAT or Property Table. Ensure it does ChainLoopDetector loopDetector = getChainLoopDetector(); // Read the FAT blocks for (int fatAt : _header.getBATArray()) { readBAT(fatAt, loopDetector); } // Work out how many FAT blocks remain in the XFATs int remainingFATs = _header.getBATCount() - _header.getBATArray().length; // Now read the XFAT blocks, and the FATs within them BATBlock xfat; int nextAt = _header.getXBATIndex(); for (int i = 0; i < _header.getXBATCount(); i++) { loopDetector.claim(nextAt); ByteBuffer fatData = getBlockAt(nextAt); xfat = BATBlock.createBATBlock(bigBlockSize, fatData); xfat.setOurBlockIndex(nextAt); nextAt = xfat.getValueAt(bigBlockSize.getXBATEntriesPerBlock()); _xbat_blocks.add(xfat); // Process all the (used) FATs from this XFAT int xbatFATs = Math.min(remainingFATs, bigBlockSize.getXBATEntriesPerBlock()); for (int j = 0; j < xbatFATs; j++) { int fatAt = xfat.getValueAt(j); if (fatAt == POIFSConstants.UNUSED_BLOCK || fatAt == POIFSConstants.END_OF_CHAIN) break; readBAT(fatAt, loopDetector); } remainingFATs -= xbatFATs; } // We're now able to load steams // Use this to read in the properties _property_table = new NPropertyTable(_header, this); // Finally read the Small Stream FAT (SBAT) blocks BATBlock sfat; List<BATBlock> sbats = new ArrayList<BATBlock>(); _mini_store = new NPOIFSMiniStore(this, _property_table.getRoot(), sbats, _header); nextAt = _header.getSBATStart(); for (int i = 0; i < _header.getSBATCount(); i++) { loopDetector.claim(nextAt); ByteBuffer fatData = getBlockAt(nextAt); sfat = BATBlock.createBATBlock(bigBlockSize, fatData); sfat.setOurBlockIndex(nextAt); sbats.add(sfat); nextAt = getNextBlock(nextAt); } }
private BATBlock createBAT(int offset, boolean isBAT) throws IOException { // Create a new BATBlock BATBlock newBAT = BATBlock.createEmptyBATBlock(bigBlockSize, !isBAT); newBAT.setOurBlockIndex(offset); // Ensure there's a spot in the file for it ByteBuffer buffer = ByteBuffer.allocate(bigBlockSize.getBigBlockSize()); int writeTo = (1 + offset) * bigBlockSize.getBigBlockSize(); // Header isn't in BATs _data.write(buffer, writeTo); // All done return newBAT; }
/** Load the block at the given offset, extending the file if needed */ @Override protected ByteBuffer createBlockIfNeeded(final int offset) throws IOException { try { return getBlockAt(offset); } catch (IndexOutOfBoundsException e) { // The header block doesn't count, so add one long startAt = (offset + 1) * bigBlockSize.getBigBlockSize(); // Allocate and write ByteBuffer buffer = ByteBuffer.allocate(getBigBlockSize()); _data.write(buffer, startAt); // Retrieve the properly backed block return getBlockAt(offset); } }
/** * write the blocks to a stream * * @param stream the stream to which the data is to be written */ void writeBlocks(OutputStream stream) throws IOException { if (isValid()) { if (_writer != null) { DocumentOutputStream dstream = new DocumentOutputStream(stream, _size); _writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, _path, _name, _size)); dstream.writeFiller( countBlocks() * _bigBlockSize.getBigBlockSize(), DocumentBlock.getFillByte()); } else { for (int k = 0; k < bigBlocks.length; k++) { bigBlocks[k].writeBlocks(stream); } } } }
private NPOIFSFileSystem(boolean newFS) { _header = new HeaderBlock(bigBlockSize); _property_table = new NPropertyTable(_header); _mini_store = new NPOIFSMiniStore(this, _property_table.getRoot(), new ArrayList<BATBlock>(), _header); _xbat_blocks = new ArrayList<BATBlock>(); _bat_blocks = new ArrayList<BATBlock>(); _root = null; if (newFS) { // Data needs to initially hold just the header block, // a single bat block, and an empty properties section _data = new ByteArrayBackedDataSource(new byte[bigBlockSize.getBigBlockSize() * 3]); } }
/** @return The Big Block size, normally 512 bytes, sometimes 4096 bytes */ public int getBigBlockSize() { return bigBlockSize.getBigBlockSize(); }
/** * Finds a free block, and returns its offset. This method will extend the file if needed, and if * doing so, allocate new FAT blocks to address the extra space. */ @Override protected int getFreeBlock() throws IOException { // First up, do we have any spare ones? int offset = 0; for (int i = 0; i < _bat_blocks.size(); i++) { int numSectors = bigBlockSize.getBATEntriesPerBlock(); // Check this one BATBlock bat = _bat_blocks.get(i); if (bat.hasFreeSectors()) { // Claim one of them and return it for (int j = 0; j < numSectors; j++) { int batValue = bat.getValueAt(j); if (batValue == POIFSConstants.UNUSED_BLOCK) { // Bingo return offset + j; } } } // Move onto the next BAT offset += numSectors; } // If we get here, then there aren't any free sectors // in any of the BATs, so we need another BAT BATBlock bat = createBAT(offset, true); bat.setValueAt(0, POIFSConstants.FAT_SECTOR_BLOCK); _bat_blocks.add(bat); // Now store a reference to the BAT in the required place if (_header.getBATCount() >= 109) { // Needs to come from an XBAT BATBlock xbat = null; for (BATBlock x : _xbat_blocks) { if (x.hasFreeSectors()) { xbat = x; break; } } if (xbat == null) { // Oh joy, we need a new XBAT too... xbat = createBAT(offset + 1, false); xbat.setValueAt(0, offset); bat.setValueAt(1, POIFSConstants.DIFAT_SECTOR_BLOCK); // Will go one place higher as XBAT added in offset++; // Chain it if (_xbat_blocks.size() == 0) { _header.setXBATStart(offset); } else { _xbat_blocks .get(_xbat_blocks.size() - 1) .setValueAt(bigBlockSize.getXBATEntriesPerBlock(), offset); } _xbat_blocks.add(xbat); _header.setXBATCount(_xbat_blocks.size()); } // Allocate us in the XBAT for (int i = 0; i < bigBlockSize.getXBATEntriesPerBlock(); i++) { if (xbat.getValueAt(i) == POIFSConstants.UNUSED_BLOCK) { xbat.setValueAt(i, offset); } } } else { // Store us in the header int[] newBATs = new int[_header.getBATCount() + 1]; System.arraycopy(_header.getBATArray(), 0, newBATs, 0, newBATs.length - 1); newBATs[newBATs.length - 1] = offset; _header.setBATArray(newBATs); } _header.setBATCount(_bat_blocks.size()); // The current offset stores us, but the next one is free return offset + 1; }
/** Load the block at the given offset. */ @Override protected ByteBuffer getBlockAt(final int offset) throws IOException { // The header block doesn't count, so add one long startAt = (offset + 1) * bigBlockSize.getBigBlockSize(); return _data.read(bigBlockSize.getBigBlockSize(), startAt); }