Java tutorial
/* * Copyright (c) 2011 Marcel Lauhoff. * * This file is part of jext2. * * jext2 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * jext2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with jext2. If not, see <http://www.gnu.org/licenses/>. */ package jext2; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Date; import java.util.LinkedList; import java.util.Iterator; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Logger; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import jext2.annotations.MustReturnLock; import jext2.annotations.NotThreadSafe; import jext2.exceptions.FileTooLarge; import jext2.exceptions.IoError; import jext2.exceptions.JExt2Exception; import jext2.exceptions.NoSpaceLeftOnDevice; public class DataBlockAccess { protected static Superblock superblock = Superblock.getInstance(); protected static BlockAccess blocks = BlockAccess.getInstance(); protected static BlockGroupAccess blockGroups = BlockGroupAccess.getInstance(); protected static BitmapAccess bitmaps = BitmapAccess.getInstance(); protected DataInode inode = null; /** * This is one of the most important locks. It synchronizes changes to the * inode's block hierarchy. The linux implementation has something simillar * called the truncate_lock */ private ReentrantReadWriteLock hierarchyLock = new JextReentrantReadWriteLock(true); ReentrantReadWriteLock getHierarchyLock() { return hierarchyLock; } // used by findGoal private long lastAllocLogicalBlock = 0; private long lastAllocPhysicalBlock = 0; /** number of pointers in indirection block */ private static int ptrs = superblock.getAddressesPerBlock(); /** number of direct Blocks */ public static final long directBlocks = Constants.EXT2_NDIR_BLOCKS; /** number of indirect Blocks */ public static final long indirectBlocks = ptrs; /** number of double indirect blocks */ public static final long doubleBlocks = ptrs * ptrs; /** number of triple indirect blocks */ public static final long trippleBlocks = ptrs * ptrs * ptrs; public class DataBlockIterator implements Iterator<Long>, Iterable<Long> { Inode inode; long remaining; long current; int locks; LinkedList<Long> blocks; /* cache for block nrs */ DataBlockIterator(DataInode inode, long start) { this.inode = inode; this.current = start; this.remaining = inode.getBlocks() / (superblock.getBlocksize() / 512); } DataBlockIterator(DataInode inode) { this(inode, -1); } @Override public boolean hasNext() { fetchNext(); return ((remaining > 0) || ((blocks != null) && (blocks.size() > 0))); } private void fetchNext() { try { if (remaining > 0) { /* still blocks to fetch */ if (blocks == null || blocks.size() == 0) { /* blockNr cache empty */ try { blocks = getBlocks(current + 1, remaining); unlockHierarchyChanges(); } catch (FileTooLarge e) { blocks = null; } if (blocks == null) { remaining = 0; return; } remaining -= blocks.size(); current += blocks.size(); } } } catch (JExt2Exception e) { // XXX this is potentially to broad } } @Override public Long next() { fetchNext(); return (blocks.removeFirst()); } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public DataBlockIterator iterator() { return this; } } public void lockHierarchyChanges() { hierarchyLock.readLock().lock(); } public void unlockHierarchyChanges() { hierarchyLock.readLock().unlock(); assert hierarchyLock.getReadHoldCount() == 0; } @NotThreadSafe(useLock = true) public DataBlockIterator iterateBlocks() { return new DataBlockIterator(inode); } @NotThreadSafe(useLock = true) public DataBlockIterator iterateBlocksStartAt(long start) { return new DataBlockIterator(inode, start); } /** * parse the block number into array of offsets * * @param fileBlockNr block number to be parsed * @return array of offsets, length is the path depth * @throws FileTooLarge When the offset calculated from fileBlockNr is larger * than the last possible triple indirection offset for this blocksize */ private static int[] blockToPath(long fileBlockNr) throws FileTooLarge { // fileBlockNr will allways be less than blockSize -> int is ok if (fileBlockNr < 0) { throw new RuntimeException("blockToPath: file block number < 0"); } else if (fileBlockNr < Constants.EXT2_NDIR_BLOCKS) { return new int[] { (int) fileBlockNr }; } else if ((fileBlockNr -= directBlocks) < indirectBlocks) { return new int[] { Constants.EXT2_IND_BLOCK, (int) fileBlockNr }; } else if ((fileBlockNr -= indirectBlocks) < doubleBlocks) { return new int[] { Constants.EXT2_DIND_BLOCK, (int) fileBlockNr / ptrs, (int) fileBlockNr % ptrs }; } else if ((fileBlockNr -= doubleBlocks) < trippleBlocks) { return new int[] { Constants.EXT2_TIND_BLOCK, (int) fileBlockNr / (ptrs * ptrs), ((int) fileBlockNr / ptrs) % ptrs, (int) fileBlockNr % ptrs }; } else { throw new FileTooLarge(); } } /** * Read the chain of indirect blocks leading to data * @param inode Inode in question * @param offsets offsets of pointers in inode/indirect blocks * @return array of length depth with block numbers on the path. * array[depth-1] is data block number rest is the indirection on the path. * * If the chain is incomplete return.length < offsets.length */ @NotThreadSafe(useLock = true) private long[] getBranch(int[] offsets) throws IoError { int depth = offsets.length; long[] blockNrs = new long[depth]; blockNrs[0] = inode.getBlock()[offsets[0]]; if (blockNrs[0] == 0) { return new long[] {}; } for (int i = 1; i < depth; i++) { long nr = blocks.readBlockNumberFromBlock(blockNrs[i - 1], offsets[i]); blockNrs[i] = nr; if (nr == 0) { /* chain is incomplete */ long[] result = new long[i]; for (int k = 0; k < i; k++) result[k] = blockNrs[k]; return result; } } return blockNrs; } /** Find a preferred place for allocation * @param inode owner * @param block block we want * @return Preferrred place for a block (the goal) */ @NotThreadSafe(useLock = true) private long findGoal(long block, long[] blockNrs, int[] offsets) throws IoError { if (block == (lastAllocLogicalBlock + 1) && (lastAllocPhysicalBlock != 0)) { return (lastAllocPhysicalBlock + 1); } return findNear(inode, blockNrs, offsets); } /** Find a place for allocation with sufficient locality * @param inode owner * @return Preferred place for block allocation. It is used when the heuristic for sequential allocation fails. * * Rules are: * - if there is a block to the left of our position - allocate near it * - if pointer will live in indirect block - allocate near that block * - if pointer will live in inode - allocate in the same cylinder group */ @NotThreadSafe(useLock = true) private long findNear(DataInode inode, long[] blockNrs, int[] offsets) throws IoError { int depth = blockNrs.length; /* Try to find previous block */ if (depth == 0) { /* search direct blocks */ long[] directBlocks = inode.getBlock(); for (int i = directBlocks.length - 1; i >= 0; i--) { if (directBlocks[i] != 0) { return directBlocks[i]; } } } else { /* search last indirect block */ ByteBuffer indirectBlock = blocks.read(blockNrs[depth - 1]); for (int i = offsets[depth - 1] - 1; i >= 0; i--) { long pointer = Ext2fsDataTypes.getLE32U(indirectBlock, i * 4); if (pointer != 0) { return pointer; } } } /* No such thing, so let's try location of indirect block */ if (depth > 1) return blockNrs[depth - 1]; /* It is going to be refered from inode itself? OK just put i into * the same cylinder group then */ long bgStart = BlockGroupDescriptor.firstBlock(inode.getBlockGroup()); long colour = (Filesystem.getPID() % 16) * (superblock.getBlocksPerGroup() / 16); return bgStart + colour; } /** Allocate and set up a chain of blocks. * @param inode owner * @param num depth of the chain (number of blocks to allocate) * @param goal gloal block * @param offsets offsets in the indirection chain * @param blockNrs chain of allready allocated blocks * @throws NoSpaceLeftOnDevice * * This function allocates num blocks, zeros out all but the last one, links * them into a chain and writes them to disk. */ @NotThreadSafe(useLock = true) private LinkedList<Long> allocBranch(int num, long goal, int[] offsets, long[] blockNrs) throws JExt2Exception, NoSpaceLeftOnDevice { int n = 0; LinkedList<Long> result = new LinkedList<Long>(); try { long parent = allocateBlock(goal); result.addLast(parent); if (parent > 0) { for (n = 1; n < num; n++) { /* allocate the next block */ long nr = allocateBlock(parent); if (nr > 0) { result.addLast(nr); ByteBuffer buf = ByteBuffer.allocate(superblock.getBlocksize()); Ext2fsDataTypes.putLE32U(buf, nr, offsets[n] * 4); buf.rewind(); blocks.write(parent, buf); } else { break; } parent = nr; } } } catch (NoSpaceLeftOnDevice e) { for (long nr : result) { freeBlocks(new long[] { nr }); } } if (num == n) /* Allocation successful */ return result; else /* Allocation failed */ throw new NoSpaceLeftOnDevice(); } /** * Splice the allocated branch onto inode * @throws IOException */ @NotThreadSafe(useLock = true) private void spliceBranch(long logicalBlock, int[] offsets, long[] blockNrs, LinkedList<Long> newBlockNrs) throws IoError { int existDepth = blockNrs.length; if (existDepth == 0) { /* add direct block */ long[] directBlocks = inode.getBlock(); directBlocks[offsets[0]] = newBlockNrs.getFirst().longValue(); } else { ByteBuffer buf = blocks.read(blockNrs[existDepth - 1]); Ext2fsDataTypes.putLE32U(buf, newBlockNrs.getFirst().longValue(), offsets[existDepth] * 4); buf.rewind(); blocks.write(blockNrs[existDepth - 1], buf); } lastAllocLogicalBlock = logicalBlock; lastAllocPhysicalBlock = newBlockNrs.getLast().intValue(); inode.setBlocks(inode.getBlocks() + newBlockNrs.size() * (superblock.getBlocksize() / 512)); inode.setModificationTime(new Date()); } /** * Get up to maxBlocks block numbers for the logical block number. Do not allocate new blocks * @see getBlocksAllocate * @param fileBlockNr logical block address * @param maxBlocks maximum blocks returned * @return list of block nrs or null if logical block not found * @throws FileTooLarge fileBlockNr bigger than maximum indirection offset * @throws IOException */ @MustReturnLock public LinkedList<Long> getBlocks(long fileBlockNr, long maxBlocks) throws JExt2Exception, FileTooLarge { try { return getBlocks(fileBlockNr, maxBlocks, false); } catch (NoSpaceLeftOnDevice e) { throw new RuntimeException("should not happen"); } } /** * Get up to maxBlocks block numbers for the logical block number. Allocate new blocks if * necessary. In case of block allocation only one is blockNr returned. * @see getBlocksAllocate * @param fileBlockNr logical block address * @param maxBlocks maximum blocks returned * @return list of block nrs * @throws NoSpaceLeftOnDevice * @throws FileTooLarge fileBlockNr bigger than maximum indirection offset * @throws IOException * @see #getBlocks(long fileBlockNr, long maxBlocks, boolean create) * @see #getBlocks(long fileBlockNr, long maxBlocks) */ @MustReturnLock public LinkedList<Long> getBlocksAllocate(long fileBlockNr, long maxBlocks) throws JExt2Exception, NoSpaceLeftOnDevice, FileTooLarge { return getBlocks(fileBlockNr, maxBlocks, true); } /** Get up to maxBlocks BlockNrs for the logical fileBlockNr. I dont really like to change behavior * by specifing a flag variable but this is more or less like the linux implementation. Use getBlocks * or getBlocksAllocate. * @param inode Inode of the data block * @param fileBlockNr the logical block address * @param maxBlocks maximum blocks returned * @param create true: create blocks if nesseccary; false: just read * @return list of block nrs; if create=false null is returned if block does not exist * @throws NoSpaceLeftOnDevice * @throws FileTooLarge * @throws IOException */ @MustReturnLock private LinkedList<Long> getBlocks(long fileBlockNr, long maxBlocks, boolean create) throws JExt2Exception, NoSpaceLeftOnDevice, FileTooLarge { if (fileBlockNr < 0 || maxBlocks < 1) throw new IllegalArgumentException(); LinkedList<Long> result = new LinkedList<Long>(); int[] offsets; long[] blockNrs; int depth; int existDepth; hierarchyLock.readLock().lock(); offsets = blockToPath(fileBlockNr); depth = offsets.length; blockNrs = getBranch(offsets); existDepth = blockNrs.length; /* Simplest case - block found, no allocation needed */ if (depth == existDepth) { long firstBlockNr = blockNrs[depth - 1]; result.addFirst(firstBlockNr); long blocksToBoundary = 0; if (depth >= 2) /* indirect blocks */ blocksToBoundary = superblock.getAddressesPerBlock() - offsets[depth - 1] - 1; else /* direct blocks */ blocksToBoundary = Constants.EXT2_NDIR_BLOCKS - offsets[0] - 1; int count = 1; while (count < maxBlocks && count <= blocksToBoundary) { long nextByNumber = firstBlockNr + count; long nextOnDisk = -1; if (depth >= 2) /* indirect blocks */ nextOnDisk = blocks.readBlockNumberFromBlock(blockNrs[depth - 2], offsets[depth - 1] + count); else /* direct blocks */ nextOnDisk = inode.getBlock()[offsets[0] + count]; /* check if next neighbor block belongs to inode */ if (nextByNumber == nextOnDisk) { result.addLast(nextByNumber); count++; } else { break; } } assert hierarchyLock.getReadHoldCount() == 1 : "Returning without holding lock"; return result; } assert hierarchyLock.getReadHoldCount() == 1 : "Should have a read lock around"; /* Next simple case - plain lookup mode */ if (!create) { assert hierarchyLock.getReadHoldCount() == 1 : "Returning without holding lock"; return null; } hierarchyLock.readLock().unlock(); /* Okay, we need to do block allocation. */ hierarchyLock.writeLock().lock(); long goal = findGoal(fileBlockNr, blockNrs, offsets); int count = depth - existDepth; LinkedList<Long> newBlockNrs = allocBranch(count, goal, offsets, blockNrs); spliceBranch(fileBlockNr, offsets, blockNrs, newBlockNrs); result.add(newBlockNrs.getLast()); hierarchyLock.readLock().lock(); hierarchyLock.writeLock().unlock(); /* Again return with an open read lock */ assert hierarchyLock.getReadHoldCount() == 1 : "Returning without holding lock"; return result; } /** * Try to allocate a block by looping over block groups and calling * newBlockInGroup */ @NotThreadSafe(useLock = true) private static long newBlock(long goal) throws JExt2Exception, NoSpaceLeftOnDevice { if (!superblock.hasFreeBlocks()) { throw new NoSpaceLeftOnDevice(); } if (goal < superblock.getFirstDataBlock() || goal >= superblock.getBlocksCount()) goal = superblock.getFirstDataBlock(); int goalGroup = Calculations.groupOfBlk(goal); int start = (int) ((goal - superblock.getFirstDataBlock()) % superblock.getBlocksPerGroup()); /* start at the goalGroup and search forward for first free block */ for (BlockGroupDescriptor descr : blockGroups.iterateBlockGroups(goalGroup)) { long blockNr = newBlockInGroup(start, descr); if (blockNr > 0) { return blockNr; } start = 0; } /* start at first group and search the rest */ for (BlockGroupDescriptor descr : blockGroups.iterateBlockGroups()) { long blockNr = newBlockInGroup(0, descr); if (blockNr > 0) { return blockNr; } } throw new NoSpaceLeftOnDevice(); } /** * Allocate a new block. Uses a goal block to assist allocation. If * the goal is free, or there is a free block within 32 blocks of the gloal, that block is * allocated. Otherwise a forward search is made for a free block. * @param goal the goal block * @return pointer to allocated block * @throws IOException * @throws NoSpaceLeftOnDevice */ @NotThreadSafe(useLock = true) private static long allocateBlock(long goal) throws NoSpaceLeftOnDevice, JExt2Exception { long blockNr = newBlock(goal); /* Finally return pointer to allocated block or an error */ superblock.setFreeBlocksCount(superblock.getFreeBlocksCount() - 1); return blockNr; } /** * Find free block in single block group * @param start bitmap index to begin (think of goal) * @param descr group descriptor to search * @return block number or -1 * @throws JExt2Exception */ @NotThreadSafe(useLock = true) private static long newBlockInGroup(int start, BlockGroupDescriptor descr) throws JExt2Exception { Bitmap bitmap = bitmaps.openDataBitmap(descr); if (descr.getFreeBlocksCount() > 0) { int freeIndex = bitmap.getNextZeroBitPos(start); if (freeIndex > 0) { long blockNr = descr.firstBlock() + freeIndex; /* Check to see if we are trying to allocate a system block */ if (!(descr.isValidDataBlockNr(blockNr))) { bitmaps.closeBitmap(bitmap); throw new RuntimeException("Trying to allocate in system zone" + " blockNr=" + blockNr + " group=" + descr.getBlockGroup() + " index=" + freeIndex); } bitmap.setBit(freeIndex, true); bitmap.write(); bitmaps.closeBitmap(bitmap); descr.setFreeBlocksCount(descr.getFreeBlocksCount() - 1); return Calculations.blockNrOfLocal(freeIndex, descr.getBlockGroup()); } else { start = 0; // XXX this does not make sense :-/ } } bitmaps.closeBitmap(bitmap); return -1; } private DataBlockAccess(DataInode inode) { this.inode = inode; } /** * Create access provider to inode data */ static DataBlockAccess fromInode(DataInode inode) { return new DataBlockAccess(inode); } /** * Free single data block. Update inode.blocks. * @param blockNr physical block to free */ @NotThreadSafe(useLock = true) private void freeDataBlock(long blockNr) throws JExt2Exception { freeDataBlocksContiguous(blockNr, 1); } /** * Free count blocks. Update inode.blocks. * @param blockNr start physical block to free * @param count number of blocks to free * @throws JExt2Exception */ @NotThreadSafe(useLock = true) private void freeDataBlocksContiguous(long blockNr, long count) throws JExt2Exception { /* counter to set {superblock|blockgroup}.freeBlocksCount */ int groupFreed = 0; int freed = 0; if (blockNr < superblock.getFirstDataBlock() || blockNr + count < blockNr || blockNr + count > superblock.getBlocksCount()) { throw new RuntimeException("Free blocks not in datazone" + " blockNr=" + blockNr + " count=" + count); } long overflow; do { overflow = 0; int groupNr = Calculations.groupOfBlk(blockNr); int groupIndex = Calculations.groupIndexOfBlk(blockNr); BlockGroupDescriptor groupDescr = blockGroups.getGroupDescriptor(groupNr); /* Check to see if we are freeing blocks across a group boundary. */ if (groupIndex + count > superblock.getBlocksPerGroup()) { overflow = groupIndex + count - superblock.getBlocksPerGroup(); count -= overflow; } Bitmap bitmap = bitmaps.openDataBitmap(groupDescr); /* Check to see if we are trying to free a system block */ if (!(groupDescr.isValidDataBlockNr(blockNr) && groupDescr.isValidDataBlockNr(blockNr + count - 1))) { throw new RuntimeException("Freeing blocks in system zones"); } /* Set block bits to "free" */ groupFreed = 0; for (int i = 0; i < count; i++) { if (!(bitmap.isSet(groupIndex + i))) { Logger log = Filesystem.getLogger(); log.severe(String.format("Bit allready cleared! block=%d groupIndex=%d bitmap=%s inode=%d", (blockNr + i), (groupIndex + i), bitmap.getBitStringContaining(groupIndex + i), inode.getIno())); } if (groupIndex + i > superblock.getBlocksPerGroup()) { groupFreed++; } else { groupFreed++; bitmap.setBit(groupIndex + i, false); } } bitmap.write(); bitmaps.closeBitmap(bitmap); groupDescr.setFreeBlocksCount(groupDescr.getFreeBlocksCount() + groupFreed); freed += groupFreed; blockNr += count; count = overflow; } while (overflow > 0); inode.setBlocks(inode.getBlocks() - freed * (superblock.getBlocksize() / 512)); inode.setModificationTime(new Date()); superblock.setFreeBlocksCount(superblock.getFreeBlocksCount() + freed); } /** * Free array of data blocks and update inode.blocks appropriately. * @param blockNrs array of block numbers */ @NotThreadSafe(useLock = true) private void freeBlocks(long[] blockNrs) throws JExt2Exception { long blockToFree = 0; long count = 0; for (long nr : blockNrs) { if (nr > 0) { if (count == 0) { blockToFree = nr; count = 1; } else if (blockToFree == nr - count) { count++; } else { freeDataBlocksContiguous(blockToFree, count); blockToFree = nr; count = 1; } } } if (count > 0) { freeDataBlocksContiguous(blockToFree, count); } } /** * Free branches starting at the blocks in blockNrs. * @param depth depth of the free recursion * @param blockNrs blockNrs to start * */ @NotThreadSafe(useLock = true) private void freeBranches(int depth, long[] blockNrs) throws JExt2Exception { if (depth > 0) { /* indirection exists -> go down */ depth -= 1; for (long nr : blockNrs) { if (nr == 0) continue; long[] nextBlockNrs = blocks.readAllBlockNumbersFromBlock(nr); freeBranches(depth, nextBlockNrs); freeDataBlock(nr); } } else { /* just data pointers left */ freeBlocks(blockNrs); } } /** * Dump indirection hierachy into a nicely formatted tree. Nice for * debuggung of the block allocation. toString uses this as well */ public String dumpHierachy() { hierarchyLock.readLock().lock(); StringBuilder sb = new StringBuilder("Inode has " + inode.getBlocks() + " blocks"); /* direct blocks */ long[] direct = inode.getBlock(); for (int i = 0; i < Constants.EXT2_NDIR_BLOCKS; i++) { sb.append("[" + i + "] " + direct[i] + "\n"); } /* indirection */ try { for (int i = 0; i < 3; i++) { long blockNr = direct[Constants.EXT2_IND_BLOCK + i]; LinkedList<Long> blockNrs = blocks.readAllBlockNumbersFromBlockSkipZero(blockNr); sb.append((i + 1) + "IND " + blockNr + "\n"); dumpBranches(i, blockNrs, sb); } } catch (JExt2Exception e) { } hierarchyLock.readLock().unlock(); return sb.toString(); } @NotThreadSafe(useLock = true) private void dumpBranches(int depth, LinkedList<Long> blockNrs, StringBuilder sb) throws JExt2Exception { if (blockNrs.size() == 0) return; if (depth > 0) { /* indirection exists -> go down */ depth -= 1; for (long nr : blockNrs) { if (nr == 0) continue; LinkedList<Long> nextBlockNrs = blocks.readAllBlockNumbersFromBlockSkipZero(nr); sb.append(String.format("%" + ((depth + 1) * 10) + "s \\_ %10s \n", " ", nr)); dumpBranches(depth, nextBlockNrs, sb); } } else { /* just data pointers left */ sb.append(String.format("%" + ((depth + 2) * 10) + "s -> %10s \n", " ", blockNrs)); } } /** * Truncate data blocks toSize. * @throws IOException * @throws FileTooLarge When you try to truncate to a size * beyond the max. blocks count */ public void truncate(long toSize) throws JExt2Exception, FileTooLarge { if (inode.isFastSymlink()) return; hierarchyLock.writeLock().lock(); // TODO check inode flags for append or immutable long[] directBlocks = inode.getBlock(); int blocksize = superblock.getBlocksize(); long blockToKill = (toSize + blocksize - 1) / blocksize; int[] offsets = blockToPath(blockToKill); int depth = offsets.length; /* kill direct blocks */ if (depth == 1) { long[] blocksToFree = Arrays.copyOfRange(directBlocks, offsets[0], Constants.EXT2_NDIR_BLOCKS); for (int i = offsets[0]; i < Constants.EXT2_NDIR_BLOCKS; i++) { directBlocks[i] = 0; } freeBlocks(blocksToFree); } /* kill partial branches */ long[] branchNrs = getBranch(offsets); int existDepth = branchNrs.length; for (int i = existDepth - 1; i > 0; i--) { long nr = branchNrs[i - 1]; int start = offsets[i]; long[] blockNrs = blocks.readBlockNrsFromBlock(nr, start, ptrs - 1); blocks.zeroOut(nr, start * 4, (ptrs - 1) * 4); freeBranches((existDepth - 2) - i, blockNrs); } directBlocks[offsets[0]] = 0; /* kill the remaining (whole) subtrees */ long nr = -1; switch (offsets[0]) { default: nr = directBlocks[Constants.EXT2_IND_BLOCK]; if (nr > 0) { directBlocks[Constants.EXT2_IND_BLOCK] = 0; freeBranches(1, new long[] { nr }); } case Constants.EXT2_IND_BLOCK: nr = directBlocks[Constants.EXT2_DIND_BLOCK]; if (nr > 0) { directBlocks[Constants.EXT2_DIND_BLOCK] = 0; freeBranches(2, new long[] { nr }); } case Constants.EXT2_DIND_BLOCK: nr = directBlocks[Constants.EXT2_TIND_BLOCK]; if (nr > 0) { directBlocks[Constants.EXT2_TIND_BLOCK] = 0; freeBranches(3, new long[] { nr }); } case Constants.EXT2_TIND_BLOCK: } hierarchyLock.writeLock().unlock(); } @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) .append("lastAllocLogicalBlock", lastAllocLogicalBlock) .append("lastAllocPhysicalBlock", lastAllocPhysicalBlock).append("hierarchyLock", hierarchyLock) .toString(); } }