Android Open Source - libaums F A T






From Project

Back to project page libaums.

License

The source code is released under:

Apache License

If you think the Android project libaums listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * (C) Copyright 2014 mjahnen <jahnen@in.tum.de>
 */*from   ww  w. j a v a2s  .  c  om*/
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.github.mjdev.libaums.fs.fat32;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;

import android.util.Log;

import com.github.mjdev.libaums.driver.BlockDeviceDriver;

/**
 * This class represents the File Allocation Table (FAT) in a FAT32 file system.
 * The FAT is used to allocate the space of the disk to the different files and
 * directories.
 * <p>
 * The FAT distributes clusters with a specific cluster size
 * {@link com.github.mjdev.libaums.fs.fat32.Fat32BootSector #getBytesPerCluster()}
 * . Every entry in the FAT is 32 bit. The FAT is a (linked) list where the
 * clusters can be followed until a cluster chain ends.
 * <p>
 * For more information you should refer to the official documentation of FAT32.
 * 
 * @author mjahnen
 * 
 */
public class FAT {

  private static final String TAG = FAT.class.getSimpleName();

  /**
   * End of file / chain marker. This is used to determine when following a
   * cluster chain should be stopped. (Last allocated cluster has been found.)
   */
  private static final int FAT32_EOF_CLUSTER = 0x0FFFFFF8;

  private BlockDeviceDriver blockDevice;
  private long fatOffset[];
  private int fatNumbers[];
  private FsInfoStructure fsInfoStructure;

  /**
   * Constructs a new FAT.
   * 
   * @param blockDevice
   *            The block device where the FAT is located.
   * @param bootSector
   *            The corresponding boot sector of the FAT32 file system.
   * @param fsInfoStructure
   *            The info structure where the last allocated block and the free
   *            clusters are saved.
   */
  /* package */FAT(BlockDeviceDriver blockDevice, Fat32BootSector bootSector,
      FsInfoStructure fsInfoStructure) {
    this.blockDevice = blockDevice;
    this.fsInfoStructure = fsInfoStructure;
    if (!bootSector.isFatMirrored()) {
      int fatNumber = bootSector.getValidFat();
      fatNumbers = new int[] { fatNumber };
      Log.i(TAG, "fat is not mirrored, fat " + fatNumber + " is valid");
    } else {
      int fatCount = bootSector.getFatCount();
      fatNumbers = new int[fatCount];
      for (int i = 0; i < fatCount; i++) {
        fatNumbers[i] = i;
      }
      Log.i(TAG, "fat is mirrored, fat count: " + fatCount);
    }

    fatOffset = new long[fatNumbers.length];
    for (int i = 0; i < fatOffset.length; i++) {
      fatOffset[i] = bootSector.getFatOffset(fatNumbers[i]);
    }
  }

  /**
   * This methods gets a chain by following the given start cluster to an end
   * mark.
   * 
   * @param startCluster
   *            The start cluster where the chain starts.
   * @return The chain including the start cluster.
   * @throws IOException
   *             If reading from device fails.
   */
  /* package */Long[] getChain(long startCluster) throws IOException {
    final ArrayList<Long> result = new ArrayList<Long>();
    final int bufferSize = blockDevice.getBlockSize() * 2;
    // for performance reasons we always read or write two times the block
    // size
    // this is esp. good for long cluster chains because it reduces of read
    // or writes
    // and mostly cluster chains are located consecutively in the FAT
    final ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
    buffer.order(ByteOrder.LITTLE_ENDIAN);

    long currentCluster = startCluster;
    long offset;
    long offsetInBlock;
    long lastOffset = -1;

    do {
      result.add(currentCluster);
      offset = ((fatOffset[0] + currentCluster * 4) / bufferSize) * bufferSize;
      offsetInBlock = ((fatOffset[0] + currentCluster * 4) % bufferSize);

      // if we have a new offset we are forced to read again
      if (lastOffset != offset) {
        buffer.clear();
        blockDevice.read(offset, buffer);
        lastOffset = offset;
      }

      currentCluster = buffer.getInt((int) offsetInBlock);
    } while (currentCluster < FAT32_EOF_CLUSTER);

    return result.toArray(new Long[0]);
  }

  /**
   * This methods searches for free clusters in the chain and then assigns it
   * to the existing chain which is given at a parameter. The current chain
   * given as parameter can also be empty so that a completely new chain (with
   * a new start cluster) is created.
   * 
   * @param chain
   *            The existing chain or an empty array to create a completely
   *            new chain.
   * @param numberOfClusters
   *            The number of clusters which shall newly be allocated.
   * @return The new chain including the old and the newly allocated clusters.
   * @throws IOException
   *             If reading or writing to the FAT fails.
   */
  /* package */Long[] alloc(Long[] chain, int numberOfClusters) throws IOException {
    final ArrayList<Long> result = new ArrayList<Long>(chain.length + numberOfClusters);
    result.addAll(Arrays.asList(chain));
    // for performance reasons we always read or write two times the block
    // size
    // this is esp. good for long cluster chains because it reduces of read
    // or writes
    // and mostly cluster chains are located consecutively in the FAT
    final int bufferSize = blockDevice.getBlockSize() * 2;
    final ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
    buffer.order(ByteOrder.LITTLE_ENDIAN);

    final long cluster;
    if (chain.length != 0)
      cluster = chain[chain.length - 1];
    else
      cluster = -1;

    long lastAllocated = fsInfoStructure.getLastAllocatedClusterHint();
    if (lastAllocated == FsInfoStructure.INVALID_VALUE) {
      // we have to start from the beginning because there is no hint!
      lastAllocated = 2;
    }

    long currentCluster = lastAllocated;

    long offset;
    long offsetInBlock;
    long lastOffset = -1;

    // first we search all needed cluster and save them
    while (numberOfClusters > 0) {
      currentCluster++;
      offset = ((fatOffset[0] + currentCluster * 4) / bufferSize) * bufferSize;
      offsetInBlock = ((fatOffset[0] + currentCluster * 4) % bufferSize);

      // if we have a new offset we are forced to read again
      if (lastOffset != offset) {
        buffer.clear();
        blockDevice.read(offset, buffer);
        lastOffset = offset;
      }

      if (buffer.getInt((int) offsetInBlock) == 0) {
        result.add(currentCluster);
        numberOfClusters--;
      }
    }

    // TODO we should write in in all FATs when they are mirrored!
    if (cluster != -1) {
      // now it is time to write the partial cluster chain
      // start with the last cluster in the existing chain
      offset = ((fatOffset[0] + cluster * 4) / bufferSize) * bufferSize;
      offsetInBlock = ((fatOffset[0] + cluster * 4) % bufferSize);

      // if we have a new offset we are forced to read again
      if (lastOffset != offset) {
        buffer.clear();
        blockDevice.read(offset, buffer);
        lastOffset = offset;
      }
      buffer.putInt((int) offsetInBlock, (int) result.get(chain.length).longValue());
    }

    // write the new allocated clusters now
    for (int i = chain.length; i < result.size() - 1; i++) {
      currentCluster = result.get(i);
      offset = ((fatOffset[0] + currentCluster * 4) / bufferSize) * bufferSize;
      offsetInBlock = ((fatOffset[0] + currentCluster * 4) % bufferSize);

      // if we have a new offset we are forced to read again
      if (lastOffset != offset) {
        buffer.clear();
        blockDevice.write(lastOffset, buffer);
        buffer.clear();
        blockDevice.read(offset, buffer);
        lastOffset = offset;
      }

      buffer.putInt((int) offsetInBlock, (int) result.get(i + 1).longValue());
    }

    // write end mark to last newly allocated cluster now
    currentCluster = result.get(result.size() - 1);
    offset = ((fatOffset[0] + currentCluster * 4) / bufferSize) * bufferSize;
    offsetInBlock = ((fatOffset[0] + currentCluster * 4) % bufferSize);

    // if we have a new offset we are forced to read again
    if (lastOffset != offset) {
      buffer.clear();
      blockDevice.write(lastOffset, buffer);
      buffer.clear();
      blockDevice.read(offset, buffer);
      lastOffset = offset;
    }
    buffer.putInt((int) offsetInBlock, FAT32_EOF_CLUSTER);
    buffer.clear();
    blockDevice.write(offset, buffer);

    // refresh the info structure
    fsInfoStructure.setLastAllocatedClusterHint(currentCluster);
    fsInfoStructure.decreaseClusterCount(numberOfClusters);
    fsInfoStructure.write();

    Log.i(TAG, "allocating clusters finished");

    return result.toArray(new Long[0]);
  }

  /**
   * This methods frees the desired number of clusters in the FAT and then
   * sets the last remaining cluster to the end mark. If all clusters are
   * requested to be freed the last step will be omitted.
   * <p>
   * This methods frees the clusters starting at the end of the existing
   * cluster chain.
   * 
   * @param chain
   *            The existing chain where the clusters shall be freed from.
   * @param numberOfClusters
   *            The amount of clusters which shall be freed.
   * @return The new chain without the unneeded clusters.
   * @throws IOException
   *             If reading or writing to the FAT fails.
   * @throws IllegalStateException
   *             If more clusters are requested to be freed than currently
   *             exist in the chain.
   */
  /* package */Long[] free(Long[] chain, int numberOfClusters) throws IOException {
    final int offsetInChain = chain.length - numberOfClusters;
    // for performance reasons we always read or write two times the block
    // size
    // this is esp. good for long cluster chains because it reduces of read
    // or writes
    // and mostly cluster chains are located consecutively in the FAT
    final int bufferSize = blockDevice.getBlockSize() * 2;
    final ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
    buffer.order(ByteOrder.LITTLE_ENDIAN);

    if (offsetInChain < 0)
      throw new IllegalStateException(
          "trying to remove more clusters in chain than currently exist!");

    long currentCluster;

    long offset;
    long offsetInBlock;
    long lastOffset = -1;

    // free all unneeded clusters
    for (int i = offsetInChain; i < chain.length; i++) {
      currentCluster = chain[i];
      offset = ((fatOffset[0] + currentCluster * 4) / bufferSize) * bufferSize;
      offsetInBlock = ((fatOffset[0] + currentCluster * 4) % bufferSize);

      // if we have a new offset we are forced to read again
      if (lastOffset != offset) {
        if (lastOffset != -1) {
          buffer.clear();
          blockDevice.write(lastOffset, buffer);
        }

        buffer.clear();
        blockDevice.read(offset, buffer);
        lastOffset = offset;
      }

      buffer.putInt((int) offsetInBlock, 0);
    }

    // TODO we should write in in all FATs when they are mirrored!
    if (offsetInChain > 0) {
      // write the end mark to last cluster in the new chain
      currentCluster = chain[offsetInChain - 1];
      offset = ((fatOffset[0] + currentCluster * 4) / bufferSize) * bufferSize;
      offsetInBlock = ((fatOffset[0] + currentCluster * 4) % bufferSize);

      // if we have a new offset we are forced to read again
      if (lastOffset != offset) {
        buffer.clear();
        blockDevice.write(lastOffset, buffer);
        buffer.clear();
        blockDevice.read(offset, buffer);
        lastOffset = offset;
      }
      buffer.putInt((int) offsetInBlock, FAT32_EOF_CLUSTER);
      buffer.clear();
      blockDevice.write(offset, buffer);
    } else {
      // if we freed all clusters we have to write the last change of the
      // for loop above
      buffer.clear();
      blockDevice.write(lastOffset, buffer);
    }

    Log.i(TAG, "freed " + numberOfClusters + " clusters");

    // increase the free cluster count by decreasing with a negative value
    fsInfoStructure.decreaseClusterCount(-numberOfClusters);
    fsInfoStructure.write();

    return Arrays.copyOfRange(chain, 0, offsetInChain);
  }
}




Java Source Code List

com.github.mjdev.libaums.UsbCommunication.java
com.github.mjdev.libaums.UsbMassStorageDevice.java
com.github.mjdev.libaums.driver.BlockDeviceDriverFactory.java
com.github.mjdev.libaums.driver.BlockDeviceDriver.java
com.github.mjdev.libaums.driver.scsi.ScsiBlockDevice.java
com.github.mjdev.libaums.driver.scsi.commands.CommandBlockWrapper.java
com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiInquiryResponse.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiInquiry.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiRead10.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiReadCapacityResponse.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiReadCapacity.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiRequestSense.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiTestUnitReady.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiWrite10.java
com.github.mjdev.libaums.fs.FileSystemFactory.java
com.github.mjdev.libaums.fs.FileSystem.java
com.github.mjdev.libaums.fs.UsbFile.java
com.github.mjdev.libaums.fs.fat32.ClusterChain.java
com.github.mjdev.libaums.fs.fat32.FAT.java
com.github.mjdev.libaums.fs.fat32.Fat32BootSector.java
com.github.mjdev.libaums.fs.fat32.Fat32FileSystem.java
com.github.mjdev.libaums.fs.fat32.FatDirectoryEntry.java
com.github.mjdev.libaums.fs.fat32.FatDirectory.java
com.github.mjdev.libaums.fs.fat32.FatFile.java
com.github.mjdev.libaums.fs.fat32.FatLfnDirectoryEntry.java
com.github.mjdev.libaums.fs.fat32.FsInfoStructure.java
com.github.mjdev.libaums.fs.fat32.ShortNameGenerator.java
com.github.mjdev.libaums.fs.fat32.ShortName.java
com.github.mjdev.libaums.partition.PartitionTableEntry.java
com.github.mjdev.libaums.partition.PartitionTableFactory.java
com.github.mjdev.libaums.partition.PartitionTable.java
com.github.mjdev.libaums.partition.Partition.java
com.github.mjdev.libaums.partition.mbr.MasterBootRecord.java
com.github.mjdev.libaums.usbfileman.MainActivity.java
com.github.mjdev.libaums.usbfileman.MoveClipboard.java
com.github.mjdev.libaums.usbfileman.UsbFileListAdapter.java