/* * $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.exfat; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.jnode.fs.spi.AbstractFSObject; /** * @author Matthias Treydte <waldheinz at gmail.com> */ public final class ExFatSuperBlock extends AbstractFSObject { /** * The size of the ExFAT super block in bytes. */ private static final int SIZE = 512; private static final String OEM_NAME = "EXFAT "; //NOI18N private final DeviceAccess da; private long blockStart; private long blockCount; private long fatBlockStart; private long fatBlockCount; private long clusterBlockStart; private long clusterCount; private long rootDirCluster; private int volumeSerial; private byte fsVersionMinor; private byte fsVersionMajor; private short volumeState; private byte blockBits; private byte blocksPerClusterBits; private byte percentInUse; public ExFatSuperBlock(ExFatFileSystem fs) { super(fs); this.da = new DeviceAccess(fs.getApi()); } public static ExFatSuperBlock read(ExFatFileSystem fs) throws IOException { final ByteBuffer b = ByteBuffer.allocate(SIZE); b.order(ByteOrder.LITTLE_ENDIAN); fs.getApi().read(0, b); /* check OEM name */ final byte[] oemBytes = new byte[OEM_NAME.length()]; b.position(0x03); b.get(oemBytes); final String oemString = new String(oemBytes); if (!OEM_NAME.equals(oemString)) { throw new IOException("OEM name mismatch"); } /* check fat count */ if ((b.get(0x6e) & 0xff) != 1) { throw new IOException("invalid FAT count"); } /* check drive # */ if ((b.get(0x6f) & 0xff) != 0x80) { throw new IOException("invalid drive number"); } /* check boot signature */ if ((b.get(510) & 0xff) != 0x55 || (b.get(511) & 0xff) != 0xaa) throw new IOException("missing boot sector signature"); final ExFatSuperBlock result = new ExFatSuperBlock(fs); result.blockStart = b.getLong(0x40); result.blockCount = b.getLong(0x48); result.fatBlockStart = b.getInt(0x50); result.fatBlockCount = b.getInt(0x54); result.clusterBlockStart = b.getInt(0x58); result.clusterCount = b.getInt(0x5c); result.rootDirCluster = b.getInt(0x60); result.volumeSerial = b.getInt(0x64); result.fsVersionMinor = b.get(0x68); result.fsVersionMajor = b.get(0x69); result.volumeState = b.getShort(0x6a); result.blockBits = b.get(0x6c); result.blocksPerClusterBits = b.get(0x6d); result.percentInUse = b.get(0x70); /* check version */ if (result.fsVersionMajor != 1) { throw new IOException("unsupported version major " + result.fsVersionMajor); } if (result.fsVersionMinor != 0) { throw new IOException("unsupported version minor " + result.fsVersionMinor); } return result; } public long clusterToBlock(long cluster) throws IOException { Cluster.checkValid(cluster); return this.clusterBlockStart + ((cluster - Cluster.FIRST_DATA_CLUSTER) << this.blocksPerClusterBits); } public long blockToOffset(long block) { return (block << this.blockBits); } public long clusterToOffset(long cluster) throws IOException { return blockToOffset(clusterToBlock(cluster)); } public void readCluster(ByteBuffer dest, long cluster) throws IOException { assert (dest.remaining() <= this.getBytesPerCluster()) : "read over cluster bundary"; da.read(dest, clusterToOffset(cluster)); } public DeviceAccess getDeviceAccess() { return da; } public long getBlockStart() { return blockStart; } public long getBlockCount() { return blockCount; } public long getFatBlockStart() { return fatBlockStart; } public long getFatBlockCount() { return fatBlockCount; } public long getClusterBlockStart() { return clusterBlockStart; } /** * Returns the total number of data clusters available on the file system. * To iterate the clusters the range {@code 0..count} must be shifted by * {@link Cluster#FIRST_DATA_CLUSTER}. * * @return the number of usable clusters on the file system */ public long getClusterCount() { return clusterCount; } public long getRootDirCluster() { return rootDirCluster; } public int getVolumeSerial() { return volumeSerial; } public byte getFsVersionMajor() { return fsVersionMajor; } public byte getFsVersionMinor() { return fsVersionMinor; } public short getVolumeState() { return volumeState; } public int getBlockSize() { return (1 << blockBits); } public int getBlocksPerCluster() { return (1 << blocksPerClusterBits); } public int getBytesPerCluster() { return (getBlockSize() << this.blocksPerClusterBits); } /** * Returns the percentage of allocated clusters, rounded down to * integer value. {@code 0xff} means this value is not available. * * @return the percent of used clusters */ public byte getPercentInUse() { return percentInUse; } }