jext2.SymlinkInode.java Source code

Java tutorial

Introduction

Here is the source code for jext2.SymlinkInode.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 java.util.Date;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.commons.lang.builder.HashCodeBuilder;

import jext2.annotations.NotThreadSafe;
import jext2.exceptions.FileTooLarge;
import jext2.exceptions.IoError;
import jext2.exceptions.JExt2Exception;
import jext2.exceptions.NoSpaceLeftOnDevice;

public class SymlinkInode extends DataInode {
    private String symlink = "";

    public int FAST_SYMLINK_MAX = Constants.EXT2_N_BLOCKS * 4;

    private ReentrantReadWriteLock symlinkLock = new JextReentrantReadWriteLock(true);

    @NotThreadSafe(useLock = true)
    private String readSlowSymlink() throws JExt2Exception, FileTooLarge {
        ByteBuffer buf = readData((int) getSize(), 0);
        return Ext2fsDataTypes.getString(buf, 0, buf.limit());
    }

    @NotThreadSafe(useLock = true)
    private void writeSlowSymlink(String link, int size) throws JExt2Exception, NoSpaceLeftOnDevice, FileTooLarge {
        ByteBuffer buf = ByteBuffer.allocate(Ext2fsDataTypes.getStringByteLength(link));
        Ext2fsDataTypes.putString(buf, link, buf.capacity(), 0);
        buf.rewind();
        writeData(buf, 0);
    }

    @NotThreadSafe(useLock = true)
    private String readFastSymlink(ByteBuffer buf) throws IoError {
        return Ext2fsDataTypes.getString(buf, 40 + offset, (int) getSize());
    }

    @NotThreadSafe(useLock = true)
    private void writeFastSymlink(String link, int size) throws IoError {
        this.symlink = link;
        setSize(size);
        setBlocks(0);
        write();
    }

    @NotThreadSafe(useLock = true)
    private void writeSymlink(String link, int size) throws NoSpaceLeftOnDevice, FileTooLarge, JExt2Exception {

        if (size < FAST_SYMLINK_MAX) {
            writeFastSymlink(link, size);
        } else {
            writeSlowSymlink(link, size);
        }
    }

    @NotThreadSafe(useLock = true)
    private void cleanupSlowToFast() throws JExt2Exception {
        try {
            accessData().truncate(0);
        } catch (FileTooLarge e) {
            throw new RuntimeException("should not happen");
        }
    }

    private void cleanupFastToSlow() {
        this.symlink = "";
    }

    @NotThreadSafe(useLock = true)
    private void cleanup(String newLink, int newSize) throws JExt2Exception {
        if (isSlowSymlink() && newSize <= FAST_SYMLINK_MAX) {
            cleanupFastToSlow();
        } else {
            cleanupSlowToFast();
        }
    }

    public final String getSymlink() throws JExt2Exception, FileTooLarge {
        symlinkLock.readLock().lock();

        String result;

        if (isFastSymlink())
            result = this.symlink;
        else
            result = readSlowSymlink();

        symlinkLock.readLock().unlock();
        return result;
    }

    /**
     * Set new symlink. We either write data blocks or characters to the
     * data block pointer depending on the symlink length.
     * @throws FileTooLarge This is unlikely to happen because is requires
     *     a symlink spanning thousands of blocks.
     */
    public void setSymlink(String newLink) throws NoSpaceLeftOnDevice, JExt2Exception, FileTooLarge {
        int newSize = Ext2fsDataTypes.getStringByteLength(newLink); // this is quite expensive and thus passed on

        setSize(0);

        symlinkLock.writeLock().lock();
        cleanup(newLink, newSize);
        writeSymlink(newLink, newSize);
        symlinkLock.writeLock().unlock();

        setStatusChangeTime(new Date());
    }

    protected SymlinkInode(long blockNr, int offset) throws IoError {
        super(blockNr, offset);
    }

    @Override
    protected void read(ByteBuffer buf) throws IoError {
        super.read(buf);

        if (isFastSymlink())
            this.symlink = readFastSymlink(buf);
    }

    @Override
    protected void write(ByteBuffer buf) throws IoError {
        if (isFastSymlink() && getSize() > 0)
            Ext2fsDataTypes.putString(buf, symlink, (int) getSize(), 40);
        super.write(buf);
    }

    @Override
    public void write() throws IoError {
        ByteBuffer buf = allocateByteBuffer();
        write(buf);
    }

    public static SymlinkInode fromByteBuffer(ByteBuffer buf, int offset) throws IoError {
        SymlinkInode inode = new SymlinkInode(-1, offset);
        inode.read(buf);
        return inode;
    }

    /**
     * Create empty Inode. Initialize *Times, block array.
     */
    public static SymlinkInode createEmpty() throws JExt2Exception {
        SymlinkInode inode = new SymlinkInode(-1, -1);
        Date now = new Date();

        inode.setModificationTime(now);
        inode.setAccessTime(now);
        inode.setStatusChangeTime(now);
        inode.setDeletionTime(new Date(0));
        inode.setMode(new ModeBuilder().link().allReadWriteExecute().create());
        inode.setBlock(new long[Constants.EXT2_N_BLOCKS]);
        inode.setBlocks(0);
        inode.symlink = "";
        inode.setSize(0);

        return inode;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().appendSuper(super.hashCode()).append(symlink).toHashCode();
    }

    @Override
    public short getFileType() {
        return DirectoryEntry.FILETYPE_SYMLINK;
    }

    @Override
    public boolean isSymlink() {
        return true;
    }

    @Override
    public boolean isDirectory() {
        return false;
    }

    @Override
    public boolean isRegularFile() {
        return false;
    }

    /**
     * Check if inode is a fast symlink. A fast symlink stores the link in the
     * data block pointers instead of data blocks itself
     *
     * @return true if Inode is fast symlink; false otherwise.
     */
    @Override
    public boolean isFastSymlink() {
        return (getBlocks() == 0);
    }

    @Override
    public boolean isSlowSymlink() {
        return !isFastSymlink();
    }

    @Override
    public boolean hasDataBlocks() {
        return isSlowSymlink();
    }
}