/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.fs.ext2; import java.io.IOException; import org.apache.log4j.Logger; import org.jnode.util.LittleEndian; /** * @author Andras Nagy * */ public class GroupDescriptor { public static final int GROUPDESCRIPTOR_LENGTH = 32; private byte data[]; private Ext2FileSystem fs; private int groupNr; private boolean dirty; private final Logger log = Logger.getLogger(getClass()); public GroupDescriptor() { data = new byte[GROUPDESCRIPTOR_LENGTH]; } /* * create() and read() precedes any access to the inners of the group * descriptor, so no synchronization is needed */ public void read(int groupNr, Ext2FileSystem fs) throws IOException { // read the group descriptors from the main copy in block group 0 long baseBlock = fs.getSuperblock().getFirstDataBlock() + 1; long blockOffset = (groupNr * GROUPDESCRIPTOR_LENGTH) / fs.getBlockSize(); long offset = (groupNr * GROUPDESCRIPTOR_LENGTH) % fs.getBlockSize(); byte[] blockData = fs.getBlock(baseBlock + blockOffset); System.arraycopy(blockData, (int) offset, data, 0, GROUPDESCRIPTOR_LENGTH); this.groupNr = groupNr; this.fs = fs; setDirty(false); } /* * create() and read() precedes any access to the inners of the group * descriptor, so no synchronization is needed */ public void create(int groupNr, Ext2FileSystem fs) { this.fs = fs; this.groupNr = groupNr; long desc; // the length of the superblock and group descriptor copies // in the block group if (!fs.groupHasDescriptors(groupNr)) desc = 0; else desc = 1 + /* superblock */ Ext2Utils.ceilDiv(fs.getGroupCount() * GroupDescriptor.GROUPDESCRIPTOR_LENGTH, fs.getBlockSize()); /* GDT */ Superblock superblock = fs.getSuperblock(); setBlockBitmap(superblock.getFirstDataBlock() + groupNr * superblock.getBlocksPerGroup() + desc); setInodeBitmap(getBlockBitmap() + 1); setInodeTable(getBlockBitmap() + 2); int iNodeSize = fs.getSuperblock().getINodeSize(); long inodeTableSize = Ext2Utils.ceilDiv(superblock.getINodesPerGroup() * iNodeSize, fs.getBlockSize()); long blockCount; if (groupNr == fs.getGroupCount() - 1) blockCount = superblock.getBlocksCount() - superblock.getBlocksPerGroup() * (fs.getGroupCount() - 1) - superblock.getFirstDataBlock(); else blockCount = superblock.getBlocksPerGroup(); setFreeBlocksCount((int) (blockCount - desc /* * superblock copy, GDT * copies */ - 2 /* block and inode bitmaps */ - inodeTableSize)); /* inode table */ if (groupNr == 0) setFreeInodesCount((int) (superblock.getINodesPerGroup() - superblock.getFirstInode() + 1)); else setFreeInodesCount((int) (superblock.getINodesPerGroup())); setUsedDirsCount(0); } /** * Update all copies of a single group descriptor. (GroupDescriptors are * duplicated in some (or all) block groups: if a GroupDescriptor changes, * all copies have to be changed.) * * The method is synchronized with all methods that modify the group * descriptor (to "this") to ensure that it is not modified until all copies * are written to disk */ protected synchronized void updateGroupDescriptor() throws IOException { if (isDirty()) { log.debug("Updating groupdescriptor copies"); Superblock superblock = fs.getSuperblock(); for (int i = 0; i < fs.getGroupCount(); i++) { // check if there is a group descriptor table copy in the block // group if (!fs.groupHasDescriptors(i)) continue; long block = superblock.getFirstDataBlock() + 1 + superblock.getBlocksPerGroup() * i; long pos = groupNr * GROUPDESCRIPTOR_LENGTH; block += pos / fs.getBlockSize(); long offset = pos % fs.getBlockSize(); byte[] blockData = fs.getBlock(block); // update the block with the new group descriptor System.arraycopy(data, 0, blockData, (int) offset, GROUPDESCRIPTOR_LENGTH); fs.writeBlock(block, blockData, true); } setDirty(false); } } public int size() { return GROUPDESCRIPTOR_LENGTH; } // this field is only written during format (so no synchronization issues // here) public long getBlockBitmap() { return LittleEndian.getUInt32(data, 0); } public void setBlockBitmap(long l) { Ext2Utils.set32(data, 0, l); setDirty(true); } // this field is only written during format (so no synchronization issues // here) public long getInodeBitmap() { return LittleEndian.getUInt32(data, 4); } public void setInodeBitmap(long l) { Ext2Utils.set32(data, 4, l); setDirty(true); } // this field is only written during format (so no synchronization issues // here) public long getInodeTable() { return LittleEndian.getUInt32(data, 8); } public void setInodeTable(long l) { Ext2Utils.set32(data, 8, l); setDirty(true); } public synchronized int getFreeBlocksCount() { return LittleEndian.getUInt16(data, 12); } public synchronized void setFreeBlocksCount(int count) { LittleEndian.setInt16(data, 12, count); setDirty(true); } public synchronized int getFreeInodesCount() { return LittleEndian.getUInt16(data, 14); } public synchronized void setFreeInodesCount(int count) { LittleEndian.setInt16(data, 14, count); setDirty(true); } public synchronized int getUsedDirsCount() { return LittleEndian.getUInt16(data, 16); } public synchronized void setUsedDirsCount(int count) { LittleEndian.setInt16(data, 16, count); setDirty(true); } /** * @return the dirty flag for the descriptor */ public boolean isDirty() { return dirty; } /** * @param b */ public void setDirty(boolean b) { dirty = b; } }