jext2.DirectoryEntry.java Source code

Java tutorial

Introduction

Here is the source code for jext2.DirectoryEntry.java

Source

/*
 * 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 jext2.exceptions.FileNameTooLong;
import jext2.exceptions.IoError;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * Directory entry structure for linked list directories.
 */

public class DirectoryEntry extends PartialBlock {

    private long ino;
    private int recLen;
    private short nameLen = 0;
    private short fileType;
    private String name;

    public static final int MAX_NAME_LEN = 255;
    public static final short FILETYPE_UNKNOWN = 0;
    public static final short FILETYPE_REG_FILE = 1;
    public static final short FILETYPE_DIR = 2;
    public static final short FILETYPE_CHRDEV = 3;
    public static final short FILETYPE_BLKDEV = 4;
    public static final short FILETYPE_FIFO = 5;
    public static final short FILETYPE_SOCK = 6;
    public static final short FILETYPE_SYMLINK = 7;
    public static final short FILETYPE_MAX = 8;

    // EXT2_DIR_PAD defines the directory entries boundaries
    // NOTE: It must be a multiple of 4
    public static final int DIR_PAD = 4;
    public static final int DIR_ROUND = (DIR_PAD - 1);
    public static final int MAX_REC_LEN = ((1 << 16) - 1);

    public DirectoryEntry(long blockNr, int offset) {
        super(blockNr, offset);
    }

    public DirectoryEntry() {
        this(-1, -1);
    }

    public final long getIno() {
        return this.ino;
    }

    public final int getRecLen() {
        return this.recLen;
    }

    public final short getNameLen() {
        return this.nameLen;
    }

    public final short getFileType() {
        return this.fileType;
    }

    public final String getName() {
        return this.name;
    }

    public final void setIno(long ino) {
        this.ino = ino;
    }

    public final void setFileType(short fileType) {
        this.fileType = fileType;
    }

    public final void setRecLen(int recLen) {
        this.recLen = recLen;
    }

    /** set entry length to minimum required */
    public final void truncateRecord() {
        this.recLen = minSizeNeeded(this.nameLen);
    }

    public final void clearName() {
        this.name = "";
        this.nameLen = 0;
    }

    public final boolean isUnused() {
        return (this.ino == 0);
    }

    public boolean isOfUnknownFileType() {
        return getFileType() == FILETYPE_UNKNOWN;
    }

    public boolean isRegularFile() {
        return getFileType() == FILETYPE_REG_FILE;
    }

    public boolean isDirectory() {
        return getFileType() == FILETYPE_DIR;
    }

    public boolean isCharacterDevice() {
        return getFileType() == FILETYPE_CHRDEV;
    }

    public boolean isBlockDevice() {
        return getFileType() == FILETYPE_BLKDEV;
    }

    public boolean isFiFo() {
        return getFileType() == FILETYPE_FIFO;
    }

    public boolean isSocket() {
        return getFileType() == FILETYPE_SOCK;
    }

    public boolean isSymlink() {
        return getFileType() == FILETYPE_SYMLINK;
    }

    @Override
    protected void read(ByteBuffer buf) throws IoError {
        this.ino = Ext2fsDataTypes.getLE32U(buf, offset);
        this.recLen = Ext2fsDataTypes.getLE16U(buf, 4 + offset);
        this.nameLen = Ext2fsDataTypes.getLE8U(buf, 6 + offset);
        this.fileType = Ext2fsDataTypes.getLE8U(buf, 7 + offset);
        this.name = Ext2fsDataTypes.getString(buf, 8 + offset, this.nameLen);
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
    }

    /**
     * Create a new directory entry. Note that the name is mandatory because
     * it dictates the record length on disk
     */
    // TODO make visibility package
    public static DirectoryEntry create(String name) throws FileNameTooLong {
        int nameLen = Ext2fsDataTypes.getStringByteLength(name);

        if (nameLen > MAX_NAME_LEN) {
            throw new FileNameTooLong();
        }

        DirectoryEntry dir = new DirectoryEntry();

        /*
         * The directory entry must be divisible by 4, so the name
         * gets zero padded
         */
        short padNameLen = (short) (nameLen + (DIR_PAD - (nameLen % DIR_PAD)));

        String namePadded = StringUtils.rightPad(name, padNameLen, (char) (0x00));

        dir.recLen = (short) (8 + padNameLen);
        dir.nameLen = (short) nameLen;
        dir.name = namePadded;

        if (dir.recLen > MAX_REC_LEN)
            throw new RuntimeException("MAX_REC_LEN");

        return dir;
    }

    /**
     * Create the dummy directory entry which occupies the unused part of a
     * block.
     */
    // TODO make visibility package
    public static DirectoryEntry createRestDummy(DirectoryEntry last) {
        DirectoryEntry dir = new DirectoryEntry();
        dir.fileType = FILETYPE_UNKNOWN;
        dir.ino = 0;
        dir.nameLen = 0;
        dir.recLen = Superblock.getInstance().getBlocksize() - last.getRecLen();

        return dir;
    }

    public static int numPadBytes(int nameLen) {
        return 4 - (nameLen % 4);
    }

    /**
     * Return entry size based on name length
     */
    public static int minSizeNeeded(int nameLen) {
        return 8 + nameLen + numPadBytes(nameLen);
    }

    static DirectoryEntry fromByteBuffer(ByteBuffer buf, long blockNr, int offset) throws IoError {
        DirectoryEntry dir = new DirectoryEntry(blockNr, offset);
        dir.read(buf);
        return dir;
    }

    public static int readRecLen(ByteBuffer buf, int offset) {
        return Ext2fsDataTypes.getLE16(buf, offset + 4);
    }

    /**
     * Export data structure to ByteBuffer which in turn can be written
     * to disk
     */
    public ByteBuffer toByteBuffer() throws IoError {
        ByteBuffer buf = ByteBuffer.allocate(4 + 2 + 1 + 1 + this.nameLen);

        Ext2fsDataTypes.putLE32U(buf, this.ino, 0);
        Ext2fsDataTypes.putLE16U(buf, this.recLen, 4);
        Ext2fsDataTypes.putLE8U(buf, this.nameLen, 6);
        Ext2fsDataTypes.putLE8U(buf, this.fileType, 7);
        if (this.nameLen > 0)
            Ext2fsDataTypes.putString(buf, this.name, this.nameLen, 8);

        return buf;
    }

    @Override
    public void write() throws IoError {
        super.write(this.toByteBuffer());
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().appendSuper(super.hashCode()).append(ino).append(recLen).append(nameLen)
                .append(fileType).append(name).toHashCode();
    }
}