/*
* $Id$
*
* Copyright (C) 2003-2014 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 java.util.Arrays;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.jnode.fs.FileSystemException;
import org.jnode.fs.ext2.exception.UnallocatedBlockException;
import org.jnode.fs.ext4.ExtentHeader;
/**
* This class represents an inode. Once they are allocated, inodes are read and
* written by the INodeTable (which is accessible through desc.getINodeTable().
*
* @author Andras Nagy
*/
public class INode {
public static final int INODE_LENGTH = 128;
private final Logger log = Logger.getLogger(getClass());
/**
* the data constituting the inode itself
*/
private byte[] data;
private volatile boolean dirty;
/**
* If an inode is locked, it may not be flushed from the cache (locked
* counts the number of threads that have locked the inode)
*/
private volatile int locked;
/**
* nonpersistent data stored in memory only
*/
INodeDescriptor desc = null;
private Ext2FileSystem fs;
/**
* The cached extent header.
*/
private ExtentHeader extentHeader;
/**
* Create an INode object from an existing inode on the disk.
*
* @param fs
* @param desc
*/
public INode(Ext2FileSystem fs, INodeDescriptor desc) {
this.fs = fs;
this.desc = desc;
this.data = new byte[INODE_LENGTH];
locked = 0;
log.setLevel(Level.INFO);
}
public void read(byte[] data) {
System.arraycopy(data, 0, this.data, 0, INODE_LENGTH);
setDirty(false);
}
/**
* Create a new INode object from scratch
*/
public void create(int fileFormat, int accessRights, int uid, int gid) {
long time = System.currentTimeMillis() / 1000;
log.debug("TIME: " + time);
setUid(uid);
setGid(gid);
setMode(fileFormat | accessRights);
setSize(0);
setAtime(time);
setCtime(time);
setMtime(time);
setDtime(0);
setLinksCount(0);
//TODO: set other persistent parameters?
setDirty(true);
log.setLevel(Level.DEBUG);
}
public int getINodeNr() {
return desc.getINodeNr();
}
protected void finalize() throws Exception {
flush();
}
/**
* Called when an inode is flushed from the inode buffer and its state must
* be saved to the disk
*/
public void flush() throws IOException, FileSystemException {
log.debug("Flush called for inode " + getINodeNr());
freePreallocatedBlocks();
update();
}
/**
* write an inode back to disk
*
* @throws IOException synchronize to avoid that half-set fields get written to the inode
*/
protected synchronized void update() throws IOException {
try {
if (dirty) {
log.debug(" ** updating inode **");
desc.getINodeTable().writeInodeData(desc.getIndex(), data);
dirty = false;
}
} catch (FileSystemException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
}
}
/**
* Return the number of the group that contains the inode.
*
* @return the group number
*/
protected long getGroup() {
return desc.getGroup();
}
public Ext2FileSystem getExt2FileSystem() {
return fs;
}
/**
* return the number of direct blocks that an indirect block can point to
*
* @return the count
*/
private final int getIndirectCount() {
return fs.getSuperblock().getBlockSize() >> 2; //a block index is 4
// bytes long
}
/**
* Parse the indirect blocks of level <code>indirectionLevel</code> and
* return the address of the <code>offset</code> th block. For example,
* indirectRead( doubleIndirectBlockNumber, 45, 3) will return the 45th
* block that is reachable via triple indirection, which is the (12 +
* getIndirectCount() +getIndirectCount()^2 + 45)th block of the inode (12
* direct blocks, getIndirectCount() simple indirect blocks,
* getIndirectCount()^2 double indirect blocks, 45th triple indirect block).
*
* @param indirectionLevel 0 is a direct block, 1 is a simple indirect block, and
* so on.
*/
private final long indirectRead(long dataBlockNr, long offset, int indirectionLevel)
throws IOException {
byte[] data = fs.getBlock(dataBlockNr);
if (indirectionLevel == 1)
//data is a (simple) indirect block
return Ext2Utils.get32(data, (int) offset * 4);
long blockIndex = offset / (long) Math.pow(getIndirectCount(), indirectionLevel - 1);
long blockOffset = offset % (long) Math.pow(getIndirectCount(), indirectionLevel - 1);
long blockNr = Ext2Utils.get32(data, (int) blockIndex * 4);
return indirectRead(blockNr, blockOffset, indirectionLevel - 1);
}
/**
* Parse the indirect blocks of level <code>indirectionLevel</code> and
* register the address of the <code>offset</code> th block. Also see
* indirectRead().
*
* @param allocatedBlocks (the number of blocks allocated so far)-1
*/
private final void indirectWrite(long dataBlockNr, long offset, long allocatedBlocks,
long value, int indirectionLevel) throws IOException, FileSystemException {
log.debug("indirectWrite(blockNr=" + dataBlockNr + ", offset=" + offset + "...)");
byte[] data = fs.getBlock(dataBlockNr);
if (indirectionLevel == 1) {
//data is a (simple) indirect block
Ext2Utils.set32(data, (int) offset * 4, value);
//write back the updated block
fs.writeBlock(dataBlockNr, data, false);
return;
}
long blockNr;
long blockIndex = offset / (long) Math.pow(getIndirectCount(), indirectionLevel - 1);
long blockOffset = offset % (long) Math.pow(getIndirectCount(), indirectionLevel - 1);
if (blockOffset == 0) {
//need to reserve the indirect block itself
blockNr = findFreeBlock(allocatedBlocks++);
Ext2Utils.set32(data, (int) blockIndex * 4, blockNr);
fs.writeBlock(dataBlockNr, data, false);
//need to blank the block so that e2fsck does not complain
byte[] zeroes = new byte[fs.getBlockSize()]; //blank the block
Arrays.fill(zeroes, 0, fs.getBlockSize(), (byte) 0);
fs.writeBlock(blockNr, zeroes, false);
} else {
blockNr = Ext2Utils.get32(data, (int) blockIndex * 4);
}
indirectWrite(blockNr, blockOffset, allocatedBlocks, value, indirectionLevel - 1);
}
/**
* Free up block dataBlockNr, and free up any indirect blocks, if needed
*
* @param dataBlockNr
* @param offset
* @param indirectionLevel
* @throws IOException
*/
private final void indirectFree(long dataBlockNr, long offset, int indirectionLevel)
throws IOException, FileSystemException {
log.debug("indirectFree(datablockNr=" + dataBlockNr + ", offset=" + offset + ", ind=" +
indirectionLevel + ")");
if (indirectionLevel == 0) {
fs.freeBlock(dataBlockNr);
return;
}
byte[] data = fs.getBlock(dataBlockNr);
long blockIndex = offset / (long) Math.pow(getIndirectCount(), indirectionLevel - 1);
long blockOffset = offset % (long) Math.pow(getIndirectCount(), indirectionLevel - 1);
long blockNr = Ext2Utils.get32(data, (int) blockIndex * 4);
indirectFree(blockNr, blockOffset, indirectionLevel - 1);
if (offset == 0) {
//block blockNr has been the last block pointer on the indirect
// block,
//so the indirect block can be freed up as well
fs.freeBlock(dataBlockNr);
long block512 = fs.getBlockSize() / 512;
setBlocks(getBlocks() - block512);
}
}
/**
* Gets the data stored inline in the inode's i_block element.
*
* @return the inode block data.
*/
public byte[] getINodeBlockData() {
byte[] buffer = new byte[64];
System.arraycopy(data, 40, buffer, 0, buffer.length);
return buffer;
}
/**
* Return the number of the block in the filesystem that stores the ith
* block of the inode (i is a sequential index from the beginning of the
* file)
* <p/>
* [Naming convention used: in the code, a <code>...BlockNr</code> always
* means an absolute block nr (of the filesystem), while a
* <code>...BlockIndex</code> means an index relative to the beginning of
* a block]
*
* @param i
* @return the block number
* @throws IOException
*/
private long getDataBlockNr(long i) throws IOException {
if ((getFlags() & Ext2Constants.EXT4_INODE_EXTENTS_FLAG) != 0) {
if (extentHeader == null) {
extentHeader = new ExtentHeader(getINodeBlockData());
}
return extentHeader.getBlockNumber(fs, i);
} else {
return getDataBlockNrIndirect(i);
}
}
/**
* Return the number of the block in the filesystem that stores the ith
* block of the inode (i is a sequential index from the beginning of the
* file) using an indirect (ext2 / ext3) lookup.
* <p/>
* [Naming convention used: in the code, a <code>...BlockNr</code> always
* means an absolute block nr (of the filesystem), while a
* <code>...BlockIndex</code> means an index relative to the beginning of
* a block]
*
* @param i
* @return the block number
* @throws IOException
*/
private long getDataBlockNrIndirect(long i) throws IOException {
final long blockCount = getAllocatedBlockCount();
final int indirectCount = getIndirectCount();
if (i > blockCount - 1) {
throw new IOException("Trying to read block " + i + " (counts from 0), while" +
" INode contains only " + blockCount + " blocks");
}
//get the direct blocks (0; 11)
if (i < 12) {
log.debug("getDataBlockNr(): block nr: " + Ext2Utils.get32(data, 40 + (int) i * 4));
return Ext2Utils.get32(data, 40 + (int) i * 4);
}
//see the indirect blocks (12; indirectCount-1)
i -= 12;
if (i < indirectCount) {
//the 12th index points to the indirect block
return indirectRead(Ext2Utils.get32(data, 40 + 12 * 4), i, 1);
}
//see the double indirect blocks (indirectCount; doubleIndirectCount-1)
i -= indirectCount;
if (i < indirectCount * indirectCount) {
//the 13th index points to the double indirect block
return indirectRead(Ext2Utils.get32(data, 40 + 13 * 4), i, 2);
}
//see the triple indirect blocks (doubleIndirectCount;
// tripleIndirectCount-1)
i -= indirectCount * indirectCount;
if (i < indirectCount * indirectCount * indirectCount) {
//the 14th index points to the triple indirect block
return indirectRead(Ext2Utils.get32(data, 40 + 14 * 4), i, 3);
}
//shouldn't get here
throw new IOException("Internal FS exception: getDataBlockIndex(i=" + i + ")");
}
/**
* Read the ith block of the inode (i is a sequential index from the
* beginning of the file, and not an absolute block number)
*
* @param i
* @return the data block
* @throws IOException
*/
public byte[] getDataBlock(long i) throws IOException {
return fs.getBlock(getDataBlockNr(i));
}
/**
* A new block has been allocated for the inode, so register it (the
* <code>i</code> th block of the inode is the block at
* <code>blockNr</code>
* <p/>
* [Naming convention used: in the code, a <code>...BlockNr</code> always
* means an absolute block nr (of the filesystem), while a
* <code>...BlockIndex</code> means an index relative to the beginning of
* a block]
*
* @param i the ith block of the inode has been reserved
* @param blockNr the block (in the filesystem) that has been reserved
*/
private final void registerBlockIndex(long i, long blockNr)
throws FileSystemException, IOException {
final long blockCount = getSizeInBlocks();
final int indirectCount = getIndirectCount();
long allocatedBlocks = i;
if (i != blockCount) {
throw new FileSystemException("Trying to register block " + i +
" (counts from 0), when INode contains only " + blockCount + " blocks");
}
log.debug("registering block #" + blockNr);
setDirty(true);
//the direct blocks (0; 11)
if (i < 12) {
Ext2Utils.set32(data, 40 + (int) i * 4, blockNr);
return;
}
//see the indirect blocks (12; indirectCount-1)
i -= 12;
if (i < indirectCount) {
long indirectBlockNr;
//the 12th index points to the indirect block
if (i == 0) {
//need to reserve the indirect block itself, as this is the
//first time it is used
indirectBlockNr = findFreeBlock(allocatedBlocks++);
Ext2Utils.set32(data, 40 + 12 * 4, indirectBlockNr);
//log.debug("reserved indirect block: "+indirectBlockNr);
//need to blank the block so that e2fsck does not complain
byte[] zeroes = new byte[fs.getBlockSize()]; //blank the block
Arrays.fill(zeroes, 0, fs.getBlockSize(), (byte) 0);
fs.writeBlock(indirectBlockNr, zeroes, false);
} else {
//the indirect block has already been used
indirectBlockNr = Ext2Utils.get32(data, 40 + 12 * 4);
}
indirectWrite(indirectBlockNr, i, allocatedBlocks, blockNr, 1);
return;
}
//see the double indirect blocks (indirectCount; doubleIndirectCount-1)
i -= indirectCount;
final int doubleIndirectCount = indirectCount * indirectCount;
if (i < doubleIndirectCount) {
long doubleIndirectBlockNr;
//the 13th index points to the double indirect block
if (i == 0) {
//need to reserve the double indirect block itself
doubleIndirectBlockNr = findFreeBlock(allocatedBlocks++);
Ext2Utils.set32(data, 40 + 13 * 4, doubleIndirectBlockNr);
//log.debug("reserved double indirect block:
// "+doubleIndirectBlockNr);
//need to blank the block so that e2fsck does not complain
byte[] zeroes = new byte[fs.getBlockSize()]; //blank the block
Arrays.fill(zeroes, 0, fs.getBlockSize(), (byte) 0);
fs.writeBlock(doubleIndirectBlockNr, zeroes, false);
} else {
doubleIndirectBlockNr = Ext2Utils.get32(data, 40 + 13 * 4);
}
indirectWrite(doubleIndirectBlockNr, i, allocatedBlocks, blockNr, 2);
return;
}
//see the triple indirect blocks (doubleIndirectCount;
// tripleIndirectCount-1)
final int tripleIndirectCount = indirectCount * indirectCount * indirectCount;
i -= doubleIndirectCount;
if (i < tripleIndirectCount) {
long tripleIndirectBlockNr;
//the 14th index points to the triple indirect block
if (i == 0) {
//need to reserve the triple indirect block itself
tripleIndirectBlockNr = findFreeBlock(allocatedBlocks++);
Ext2Utils.set32(data, 40 + 13 * 4, tripleIndirectBlockNr);
//log.debug("reserved triple indirect block:
// "+tripleIndirectBlockNr);
//need to blank the block so that e2fsck does not complain
byte[] zeroes = new byte[fs.getBlockSize()]; //blank the block
Arrays.fill(zeroes, 0, fs.getBlockSize(), (byte) 0);
fs.writeBlock(tripleIndirectBlockNr, zeroes, false);
} else {
tripleIndirectBlockNr = Ext2Utils.get32(data, 40 + 14 * 4);
}
indirectWrite(tripleIndirectBlockNr, i, allocatedBlocks, blockNr, 3);
return;
}
//shouldn't get here
throw new FileSystemException("Internal FS exception: getDataBlockIndex(i=" + i + ")");
}
/**
* Free the preallocated blocks
*
* @throws FileSystemException
* @throws IOException
*/
private void freePreallocatedBlocks() throws FileSystemException, IOException {
int preallocCount = desc.getPreallocCount();
if (preallocCount > 0) {
log.debug("Freeing preallocated blocks");
} else {
log.debug("No preallocated blocks in the inode");
return;
}
long prealloc512 = preallocCount * (fs.getBlockSize() / 512);
setBlocks(getBlocks() - prealloc512);
while (desc.getPreallocCount() > 0) {
fs.freeBlock(desc.usePreallocBlock());
}
}
/**
* Free up the ith data block of the inode. It is neccessary to free up
* indirect blocks as well, if the last pointer on an indirect block has
* been freed.
*
* @param i
* @throws IOException
*/
protected synchronized void freeDataBlock(long i) throws IOException, FileSystemException {
final long blockCount = getAllocatedBlockCount();
final int indirectCount = getIndirectCount();
if (i != blockCount - 1) {
throw new IOException("Only the last block of the inode can be freed." +
"You were trying to free block nr. " + i + ", while inode contains " +
blockCount + " blocks.");
}
desc.setLastAllocatedBlockIndex(i - 1);
//preallocated blocks follow the last allocated block: when the last
// block is freed,
//free the preallocated blocks as well
freePreallocatedBlocks();
long block512 = fs.getBlockSize() / 512;
setBlocks(getBlocks() - block512);
setDirty(true);
//see the direct blocks (0; 11)
if (i < 12) {
indirectFree(Ext2Utils.get32(data, 40 + (int) i * 4), 0, 0);
Ext2Utils.set32(data, 40 + (int) i * 4, 0);
return;
}
//see the indirect blocks (12; indirectCount-1)
i -= 12;
if (i < indirectCount) {
//the 12th index points to the indirect block
indirectFree(Ext2Utils.get32(data, 40 + 12 * 4), i, 1);
//if this was the last block on the indirect block, then delete the
// record of
//the indirect block from the inode
if (i == 0) {
Ext2Utils.set32(data, 40 + 12 * 4, 0);
}
return;
}
//see the double indirect blocks (indirectCount; doubleIndirectCount-1)
i -= indirectCount;
if (i < indirectCount * indirectCount) {
//the 13th index points to the double indirect block
indirectFree(Ext2Utils.get32(data, 40 + 13 * 4), i, 2);
//if this was the last block on the double indirect block, then
// delete the record of
//the double indirect block from the inode
if (i == 0) {
Ext2Utils.set32(data, 40 + 13 * 4, 0);
}
return;
}
//see the triple indirect blocks (doubleIndirectCount;
// tripleIndirectCount-1)
i -= indirectCount * indirectCount;
if (i < indirectCount * indirectCount * indirectCount) {
//the 14th index points to the triple indirect block
indirectFree(Ext2Utils.get32(data, 40 + 14 * 4), i, 3);
//if this was the last block on the triple indirect block, then
// delete the record of
//the triple indirect block from the inode
if (i == 0) {
Ext2Utils.set32(data, 40 + 14 * 4, 0);
}
return;
}
//shouldn't get here
throw new IOException("Internal FS exception: getDataBlockIndex(i=" + i + ")");
}
/**
* Write the i. data block of the inode (i is a sequential index from the
* beginning of the file, and not an absolute block number)
* <p/>
* This method assumes that the block has already been reserved.
*
* @param i
* @param data
*/
public void writeDataBlock(long i, byte[] data) throws IOException {
//see if the block is already reserved for the inode
long blockCount = getAllocatedBlockCount();
if (i < blockCount) {
long blockIndex = getDataBlockNr(i);
//overwrite the block
fs.writeBlock(blockIndex, data, false);
} else {
throw new UnallocatedBlockException("Block " + i + " not yet reserved " +
"for the inode");
}
}
/**
* Get the number of blocks allocated so far for the inode. It is possible
* that a new block has been allocated, but not yet been written to. In this
* case, it is not counted by getSizeInBlocks(), because it returns the size
* of the file in blocks, counting only written bytes
*
* @return the count
*/
protected long getAllocatedBlockCount() {
if (desc.getLastAllocatedBlockIndex() != -1) {
return desc.getLastAllocatedBlockIndex() + 1;
} else {
return getSizeInBlocks();
}
}
/**
* Allocate the ith data block of the inode (i is a sequential index from
* the beginning of the file, and not an absolute block number)
*
* @param i
*/
public synchronized void allocateDataBlock(long i) throws FileSystemException, IOException {
if (i < getAllocatedBlockCount()) {
throw new IOException(i + " blocks are already allocated for this inode");
}
if (i > getAllocatedBlockCount()) {
throw new IOException("Allocate block " + getAllocatedBlockCount() + " first!");
}
long newBlock = findFreeBlock(i);
log.debug("Allocated new block " + newBlock);
desc.setLastAllocatedBlockIndex(i);
registerBlockIndex(i, newBlock);
}
/**
* FINDS a free block which will be the indexth block of the inode: -first
* check the preallocated blocks -then check around the last allocated block
* and ALLOCATES it in the block bitmap at the same time.
* <p/>
* Block allocation should be contiguous if possible, i.e. the new block
* should be the one that follows the last allocated block (that's why the
* <code>index</code> parameter is needed).
*
* @param index the block to be found should be around the (index-1)th block
* of the inode (which is already allocated, unless index==0)
*/
private long findFreeBlock(long index) throws IOException, FileSystemException {
//long newBlock;
long lastBlock = -1;
BlockReservation reservation;
//first, see if preallocated blocks exist
if (desc.getPreallocCount() > 0) {
return desc.usePreallocBlock();
}
//no preallocated blocks:
//check around the last allocated block
if (index > 0)
lastBlock = getDataBlockNr(index - 1);
if (lastBlock != -1) {
for (int i = 1; i < 16; i++) {
reservation = getExt2FileSystem().testAndSetBlock(lastBlock + i);
if (reservation.isSuccessful()) {
desc.setPreallocBlock(reservation.getBlock() + 1);
desc.setPreallocCount(reservation.getPreallocCount());
long prealloc512 =
(1 + reservation.getPreallocCount()) * (fs.getBlockSize() / 512);
setBlocks(getBlocks() + prealloc512);
return lastBlock + i;
}
}
for (int i = -15; i < 0; i++) {
reservation = getExt2FileSystem().testAndSetBlock(lastBlock + i);
if (reservation.isSuccessful()) {
desc.setPreallocBlock(reservation.getBlock() + 1);
desc.setPreallocCount(reservation.getPreallocCount());
long prealloc512 =
(1 + reservation.getPreallocCount()) * (fs.getBlockSize() / 512);
setBlocks(getBlocks() + prealloc512);
return lastBlock + i;
}
}
}
//then check the current block group from the beginning
//(threshold=1 means: find is successul if at least one free block is
// found)
reservation = getExt2FileSystem().findFreeBlocks(desc.getGroup(), 1);
if (reservation.isSuccessful()) {
desc.setPreallocBlock(reservation.getBlock() + 1);
desc.setPreallocCount(reservation.getPreallocCount());
long prealloc512 = (1 + reservation.getPreallocCount()) * (fs.getBlockSize() / 512);
setBlocks(getBlocks() + prealloc512);
return reservation.getBlock();
}
//then check the other block groups, first those that have "more" free
// space,
//but take a note if a non-full group is found
long nonfullBlockGroup = -1;
for (int i = 0; i < getExt2FileSystem().getGroupCount(); i++) {
if (i == desc.getGroup()) {
continue;
}
long threshold =
(getExt2FileSystem().getSuperblock().getBlocksPerGroup() *
Ext2Constants.EXT2_BLOCK_THRESHOLD_PERCENT) / 100;
reservation = getExt2FileSystem().findFreeBlocks(i, threshold);
if (reservation.isSuccessful()) {
desc.setPreallocBlock(reservation.getBlock() + 1);
desc.setPreallocCount(reservation.getPreallocCount());
long prealloc512 = (1 + reservation.getPreallocCount()) * (fs.getBlockSize() / 512);
setBlocks(getBlocks() + prealloc512);
return reservation.getBlock();
}
if (reservation.getFreeBlocksCount() > 0) {
nonfullBlockGroup = i;
}
}
//if no block group with at least the threshold number of free blocks
// is found,
//then check if there was any nonfull group
if (nonfullBlockGroup != -1) {
reservation = getExt2FileSystem().findFreeBlocks(desc.getGroup(), 1);
if (reservation.isSuccessful()) {
desc.setPreallocBlock(reservation.getBlock() + 1);
desc.setPreallocCount(reservation.getPreallocCount());
long prealloc512 = (1 + reservation.getPreallocCount()) * (fs.getBlockSize() / 512);
setBlocks(getBlocks() + prealloc512);
return reservation.getBlock();
}
}
throw new IOException("No free blocks: disk full!");
}
// **************** other persistent inode data *******************
public synchronized int getMode() {
int iMode = Ext2Utils.get16(data, 0);
//log.debug("INode.getIMode(): "+Ext2Print.hexFormat(iMode));
return iMode;
}
public synchronized void setMode(int imode) {
Ext2Utils.set16(data, 0, imode);
setDirty(true);
}
public synchronized int getUid() {
return Ext2Utils.get16(data, 2);
}
public synchronized void setUid(int uid) {
Ext2Utils.set16(data, 2, uid);
setDirty(true);
}
/**
* Return the size of the file in bytes.
*
* @return the size of the file in bytes
*/
public synchronized long getSize() {
return Ext2Utils.get32(data, 4);
}
public synchronized void setSize(long size) {
Ext2Utils.set32(data, 4, size);
setDirty(true);
}
/**
* Return the size in ext2-blocks (getBlocks() returns the size in 512-byte
* blocks, but an ext2 block can be of different size).
*
* @return the size
*/
public long getSizeInBlocks() {
return Ext2Utils.ceilDiv(getSize(), getExt2FileSystem().getBlockSize());
}
public synchronized long getAtime() {
return Ext2Utils.get32(data, 8);
}
public synchronized void setAtime(long atime) {
Ext2Utils.set32(data, 8, atime);
setDirty(true);
}
public synchronized long getCtime() {
return Ext2Utils.get32(data, 12);
}
public synchronized void setCtime(long ctime) {
Ext2Utils.set32(data, 12, ctime);
setDirty(true);
}
public synchronized long getMtime() {
return Ext2Utils.get32(data, 16);
}
public synchronized void setMtime(long mtime) {
Ext2Utils.set32(data, 16, mtime);
setDirty(true);
}
public synchronized long getDtime() {
return Ext2Utils.get32(data, 20);
}
public synchronized void setDtime(long dtime) {
Ext2Utils.set32(data, 20, dtime);
setDirty(true);
}
public synchronized int getGid() {
return Ext2Utils.get16(data, 24);
}
public synchronized void setGid(int gid) {
Ext2Utils.set16(data, 24, gid);
setDirty(true);
}
public synchronized int getLinksCount() {
return Ext2Utils.get16(data, 26);
}
public synchronized void setLinksCount(int lc) {
Ext2Utils.set16(data, 26, lc);
setDirty(true);
}
/**
* Return the size in 512-byte blocks.
*/
public synchronized long getBlocks() {
return Ext2Utils.get32(data, 28);
}
public synchronized void setBlocks(long count) {
log.debug("setBlocks(" + count + ")");
Ext2Utils.set32(data, 28, count);
setDirty(true);
}
//this value is set by setSize
public synchronized long getFlags() {
return Ext2Utils.get32(data, 32);
}
public synchronized void setFlags(long flags) {
Ext2Utils.set32(data, 32, flags);
setDirty(true);
}
public synchronized long getOSD1() {
return Ext2Utils.get32(data, 36);
}
public synchronized void setOSD1(long osd1) {
Ext2Utils.set32(data, 36, osd1);
setDirty(true);
}
public synchronized long getGeneration() {
return Ext2Utils.get32(data, 100);
}
public synchronized void setGeneration(long gen) {
Ext2Utils.set32(data, 100, gen);
setDirty(true);
}
public synchronized long getFileACL() {
return Ext2Utils.get32(data, 104);
}
public synchronized void setFileACL(long acl) {
Ext2Utils.set32(data, 104, acl);
setDirty(true);
}
public synchronized long getDirACL() {
return Ext2Utils.get32(data, 108);
}
public synchronized void setDirACL(long acl) {
Ext2Utils.set32(data, 108, acl);
setDirty(true);
}
public synchronized long getFAddr() {
return Ext2Utils.get32(data, 112);
}
public synchronized void setFAddr(long faddr) {
Ext2Utils.set32(data, 112, faddr);
setDirty(true);
}
//TODO: return OSD2 fields (12 bytes from offset 116)
public boolean isDirty() {
return dirty;
}
public void setDirty(boolean b) {
dirty = b;
if (dirty) {
extentHeader = null;
}
}
public synchronized boolean isLocked() {
return locked > 0;
}
public synchronized void incLocked() {
++locked;
}
public synchronized void decLocked() {
--locked;
if (locked == 0) {
this.notifyAll();
}
if (locked < 0) {
//What!??
locked = 0;
throw new RuntimeException("INode has been unlocked more than locked");
}
}
}