JAVA——构建FAT32文件系统的DBR(DOS引导记录)类

Maven

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

解决方案

DMR 

package cn.edu.zstu.fms.storage;

import lombok.Data;

/**
 * 是DOS引导记录,也称为操作系统引导记录,在DBR之后往往会有一些保留扇区。
 * @see <a href="https://blog.51cto.com/dengqi/1349327">FAT32文件系统详解</a>
 * @author ShenTuZhiGang
 * @version 1.0.0
 * @date 2020-12-29 18:28
 */
@Data
public class DOSBootRecord {
    /**
     * 跳转指令 开始字节位置
     */
    public static final int JUMP_COMMAND_START =0;
    /**
     * 跳转指令 长度
     */
    public static final int JUMP_COMMAND_LENGTH =2;
    /**
     * OEM代号 开始字节位置
     */
    public static final int OEM_CODE_START =3;
    /**
     * OEM代号 长度
     */
    public static final int OEM_CODE_LENGTH =8;
    /**
     * BPB 开始字节位置
     */
    public static final int BIOS_PARAMETER_BLOCK_START = 11;
    /**
     * BPB 长度
     */
    public static final int BIOS_PARAMETER_BLOCK_LENGTH =79;
    /**
     * 引导程序代码 开始字节位置
     */
    public static final int BOOTLOADER_CODE_START = 90;
    /**
     * 引导程序代码 长度
     */
    public static final int BOOTLOADER_CODE_LENGTH =420;
    /**
     * BPB 开始字节位置
     */
    public static final int END_SIGN_START = 510;
    /**
     * 结束标志 长度
     */
    public static final int END_SIGN_LENGTH =2;
    /**
     * 源数据
     */
    private byte[] source;

    /**
     * 跳转指令
     * 占2字节,它将程序执行流程跳转到引导程序处。
     * 跳转指令后,一条空的指令NOP(90H)
     * @see <a href="https://blog.csdn.net/pnzrk/article/details/88943746">FAT32</a>
     */
    private byte[] jumpCommand = new byte[2];

    /**
     * OEM代号
     * 占8字节,其内容由创建该文件系统的OEM厂商具体安排。
     */
    private byte[] oemCode = new byte[8];

    /**
     * BPB BIOS参数块
     * BIOS Parameter Block
     * FAT32的BPB从DBR的第12个字节开始,占用79字节,记录了有关该文件系统的重要信息
     */
    private BIOSParameterBlock biosParameterBlock;

    /**
     * 引导程序代码
     * FAT32的DBR引导程序占用420字节,对于没有安装操作系统的分区来说这段程序是没有用处的。
     */
    private byte[] bootloaderCode = new byte[420];

    /**
     * 结束标志
     * 0xAA55
     */
    private byte[] endSign = new byte[2];

    /**
     *
     * @param source
     */
    public DOSBootRecord(byte[] source){
        this.source = source;
        //跳转指令
        jumpCommand = new byte[JUMP_COMMAND_LENGTH];
        System.arraycopy(this.source, JUMP_COMMAND_START, jumpCommand, 0, JUMP_COMMAND_LENGTH);
        //OEM代号
        oemCode = new byte[OEM_CODE_LENGTH];
        System.arraycopy(this.source, OEM_CODE_START, oemCode, 0, OEM_CODE_LENGTH);
        //BIOS参数块
        byte[] biosParameterBlockSource = new byte[BIOS_PARAMETER_BLOCK_LENGTH];
        System.arraycopy(this.source, BIOS_PARAMETER_BLOCK_START, biosParameterBlockSource, 0, BIOS_PARAMETER_BLOCK_LENGTH);
        this.biosParameterBlock = new BIOSParameterBlock(biosParameterBlockSource);
        //引导程序代码
        bootloaderCode = new byte[BOOTLOADER_CODE_LENGTH];
        System.arraycopy(this.source, BOOTLOADER_CODE_START, bootloaderCode, 0, BOOTLOADER_CODE_LENGTH);
        //结束标志
        endSign = new byte[END_SIGN_LENGTH];
        System.arraycopy(this.source, END_SIGN_START, endSign, 0, END_SIGN_LENGTH);
    }
}

BPB 

package cn.edu.zstu.fms.storage;

import lombok.Data;

import java.io.Serializable;
import java.nio.charset.Charset;

/**
 * BIOS参数块
 * BIOS Parameter Block
 * FAT32的BPB从DBR的第12个字节开始,占用79字节,记录了有关该文件系统的重要信息
 * @see <a href="https://blog.csdn.net/pnzrk/article/details/88943746">FAT32</a>
 * @author ShenTuZhiGang
 * @version 1.0.0
 * @date 2020-12-29 19:23
 */
@Data
public class BIOSParameterBlock implements Serializable {
    /*
     *  字节偏移
     */
    /**
     * 每扇区字节数 字节偏移
     */
    public static final Byte SECTOR_BYTE_NUM_OFFSET_POSITION = 0x0B - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 每簇扇区数 字节偏移
     */
    public static final Byte CLUSTER_SECTOR_NUM_OFFSET_POSITION = 0x0D - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * DOS保留扇区数 字节偏移
     */
    public static final Byte DOS_RESERVED_SECTOR_NUM_OFFSET_POSITION = 0x1E - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * FAT个数 字节偏移
     */
    public static final Byte FILE_ALLOCATION_TABLE_NUM_OFFSET_POSITION = 0x10 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 未用 字节偏移
     */
    public static final Byte UNUSED_1_OFFSET_POSITION = 0x11 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 未用 字节偏移
     */
    public static final Byte UNUSED_2_OFFSET_POSITION = 0x13 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 介质描述符 字节偏移
     */
    public static final Byte MEDIA_DESCRIPTOR_OFFSET_POSITION = 0x15 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 未用 字节偏移
     */
    public static final Byte UNUSED_3_OFFSET_POSITION = 0x16 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 每磁道扇区数 字节偏移
     */
    public static final Byte TRACK_SECTOR_NUM_OFFSET_POSITION = 0x18 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 磁头数 字节偏移
     */
    public static final Byte HEAD_NUM_OFFSET_POSITION = 0x1A - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 隐藏扇区数 字节偏移
     */
    public static final Byte HIDDEN_SECTOR_OFFSET_POSITION = 0x1C - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 分区扇区总数 字节偏移
     */
    public static final Byte PARTITION_SECTOR_NUM_OFFSET_POSITION = 0x20 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;

    /*
     * 扩展BPB
     */

    /**
     * 每FAT扇区数 字节偏移
     */
    public static final Byte FAT_SECTOR_NUM_OFFSET_POSITION = 0x24 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 标记 字节偏移
     */
    public static final Byte SIGN_OFFSET_POSITION = 0x28 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 版本 字节偏移
     */
    public static final Byte VERSION_OFFSET_POSITION = 0x2A - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 根目录首簇号 字节偏移
     */
    public static final Byte ROOT_CLUSTER_NUMBER_OFFSET_POSITION = 0x2C - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 文件系统信息扇区号 字节偏移
     */
    public static final Byte FILE_SYSTEM_INFORMATION_SECTOR_NUMBER_OFFSET_POSITION = 0x30 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * DBR备份扇区号 字节偏移
     */
    public static final Byte BACKUP_SECTOR_NUMBER_OFFSET_POSITION = 0x32 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 保留 字节偏移
     */
    public static final Byte RETAIN_OFFSET_POSITION = 0x34 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * BIOS驱动器号 字节偏移
     */
    public static final Byte bios_Drive_Number_OFFSET_POSITION = 0x40 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 未用 字节偏移
     */
    public static final Byte extend_Boot_Flag_OFFSET_POSITION = 0x41 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 扩展引导标记 字节偏移
     */
    public static final Byte UNUSED_4_OFFSET_POSITION = 0x42 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 卷序列号 字节偏移
     */
    public static final Byte VOLUME_SERIAL_NUMBER_OFFSET_POSITION = 0x43 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 卷标 字节偏移
     */
    public static final Byte VOLUME_LABEL_OFFSET_POSITION = 0x47 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;
    /**
     * 文件系统类型 字节偏移
     */
    public static final Byte FILE_SYSTEM_TYPE_OFFSET_POSITION = 0x52 - DOSBootRecord.BIOS_PARAMETER_BLOCK_START;

    /*
     *   长度
     */

    /**
     * 每扇区字节数 长度
     */
    public static final Byte SECTOR_BYTE_NUM_LENGTH = 2;
    /**
     * 每簇扇区数 长度
     */
    public static final Byte CLUSTER_SECTOR_NUM_LENGTH = 1;
    /**
     * DOS保留扇区数 长度
     */
    public static final Byte DOS_RESERVED_SECTOR_NUM_LENGTH = 2;
    /**
     * FAT个数 长度
     */
    public static final Byte FILE_ALLOCATION_TABLE_NUM_LENGTH = 1;
    /**
     * 未用 长度
     */
    public static final Byte UNUSED_1_LENGTH = 2;
    /**
     * 未用 长度
     */
    public static final Byte UNUSED_2_LENGTH = 2;
    /**
     * 介质描述符 长度
     */
    public static final Byte MEDIA_DESCRIPTOR_LENGTH = 1;
    /**
     * 未用 长度
     */
    public static final Byte UNUSED_3_LENGTH = 2;
    /**
     * 每磁道扇区数 长度
     */
    public static final Byte TRACK_SECTOR_NUM_LENGTH = 2;
    /**
     * 磁头数 长度
     */
    public static final Byte HEAD_NUM_LENGTH = 2;
    /**
     * 隐藏扇区数 长度
     */
    public static final Byte HIDDEN_SECTOR_LENGTH = 4;
    /**
     * 分区扇区总数 长度
     */
    public static final Byte PARTITION_SECTOR_NUM_LENGTH = 4;

    /*
     * 扩展BPB
     */

    /**
     * 每FAT扇区数 长度
     */
    public static final Byte FAT_SECTOR_NUM_LENGTH = 4;
    /**
     * 标记 长度
     */
    public static final Byte SIGN_LENGTH = 2;
    /**
     * 版本 长度
     */
    public static final Byte VERSION_LENGTH = 2;
    /**
     * 根目录首簇号 长度
     */
    public static final Byte ROOT_CLUSTER_NUMBER_LENGTH = 4;
    /**
     * 文件系统信息扇区号 长度
     */
    public static final Byte FILE_SYSTEM_INFORMATION_SECTOR_NUMBER_LENGTH = 2;
    /**
     * DBR备份扇区号 长度
     */
    public static final Byte BACKUP_SECTOR_NUMBER_LENGTH = 2;
    /**
     * 保留 长度
     */
    public static final Byte RETAIN_LENGTH = 12;
    /**
     * BIOS驱动器号 长度
     */
    public static final Byte bios_Drive_Number_LENGTH = 1;
    /**
     * 未用 长度
     */
    public static final Byte extend_Boot_Flag_LENGTH = 1;
    /**
     * 扩展引导标记 长度
     */
    public static final Byte UNUSED_4_LENGTH = 1;
    /**
     * 卷序列号 长度
     */
    public static final Byte VOLUME_SERIAL_NUMBER_LENGTH = 4;
    /**
     * 卷标 长度
     */
    public static final Byte VOLUME_LABEL_LENGTH = 11;
    /**
     * 文件系统类型 长度
     */
    public static final Byte FILE_SYSTEM_TYPE_LENGTH = 8;

    /**
     * 源数据
     */
    private byte[] source;

    /**
     * 每扇区字节数
     * 字节偏移 0x0B
     * 长度 2个字节
     * 记录每个逻辑扇区的大小,其常见的值为512,但是并不是固定的值。
     * 该值可以由程序定义,合法值包括512、1024、2048、4096。
     */
    private Short sectorByteNum;

    /**
     * 每簇扇区数
     * 字节偏移 0x0D
     * 长度 1个字节
     * 记录着文件系统的簇大小,即由多少个扇区组成一个簇。
     * 簇是FAT12,FAT16及FAT32文件系统下数据的最小存储单元,
     * 一个簇由一组连续的扇区组成,簇所含的扇区数必须是2的整数次幂,
     * 如1,2,4,8,16,32,64,128.
     * 在Windows 2000以前族最大值为64扇区,之后最大值为128。
     * 在FAT文件系统中,所有的簇是从2开始编号,
     * 每个簇都有一个自己的地址编号,
     * 但是所有的簇都位于数据区,在数据区之前没有簇。
     */
    private Byte clusterSectorNum;

    /**
     * DOS保留扇区数
     * 字节偏移 0x0E
     * 长度 1个字节
     * DBR到FAT1表之间的扇区数,或者说是FAT1的开始扇区号,对于FAT32文件系统来说,该值的范围是32扇区到38扇区之间。
     */
    private Short dosReservedSectorNum;

    /**
     * FAT个数
     * 字节偏移 0x10
     * 长度 1个字节
     * 一般为2
     */
    private Byte FATNum;

    /**
     * 介质描述符
     * 字节偏移 0x15
     * 长度 1个字节
     * 描述磁盘介质的参数,根据磁盘性质的不同,取不同的值。0xF8标准值,可移动存储介质,常用 0xF0
     */
    private Byte mediaDescriptor;

    /**
     * 每磁道扇区数
     * 字节偏移 0x18
     * 长度 1个字节
     * 一般其值为63
     */
    private Short trackSectorNum;

    /**
     * 磁头数
     * 字节偏移 0x1A
     * 长度 1个字节
     * 一般为255
     */
    private Short headNum;

    /**
     * 隐藏扇区数
     * 字节偏移 0x1C
     * 长度 1个字节
     * 是MBR到DBR之间的扇区数,对于扩展分区中逻辑驱动顺来说,是其EBR到DBR。可以为0。
     */
    private Integer hiddenSector;

    /**
     * 分区扇区总数
     * 字节偏移 0x20
     * 长度 4个字节
     * 分区的总扇区数,也就是FAT32分区的大小
     */
    private Integer partitionSectorNum;

    /*
     * 扩展BPB
     */


    /**
     * 每FAT扇区数
     * 字节偏移 0x24
     * 长度 1个字节
     * FAT表占用扇区数
     */
    private Integer FATSectorNum;

    /**
     * 标记
     * 字节偏移 0x28
     * 长度 1个字节
     * FAT32是否可用,2为可用,此域FAT32 特有
     */
    private Short sign;

    /**
     * 版本
     * 字节偏移 0x2A
     * 长度 1个字节
     * FAT32版本号0.0,FAT32特有
     */
    private Short version;

    /**
     * 根目录首簇号
     * 字节偏移 0x2C
     * 长度 4个字节
     * 分区在格式化的时候,
     * 格式化程序会在数据区中指派一个簇作为FAT32的根目录的开始,并把该簇号记录在BPB中。
     * 通常都是把数据区中的第一簇分配给根目录使用,也就是2号簇。
     */
    private Integer rootClusterNumber;

    /**
     * 文件系统信息扇区号
     * 字节偏移 0x30
     * 长度 2个字节
     * FAT32文件系统在DBR的保留扇区中安排了一个文件系统信息扇区,用以记录数据区中空闲簇的数量及下一个可用的空闲簇的簇号,
     * 该扇区一般在分区的1号扇区,也就是紧跟在DBR之后的一个扇区。
     * FSINFO(文件系统信息扇区)扇区号1,该扇区为操作系统提供关于空簇总数及下一可用簇的信息
     */
    private Short fileSystemInformationSectorNumber;

    /**
     * DBR备份扇区号
     * 字节偏移 0x32
     * 长度 2个字节
     * FAT32文件系统在DBR的保留扇区中安排了一个DBR的备份,
     * 一般在6号扇区,也就是分区的第7个扇区,该备份扇区与原DBR扇区的内容完全一样,如果原DBR遭到破坏,可以用备份扇区号修复。
     */
    private Short backupSectorNumber;

    /**
     * BIOS驱动器号
     * 字节偏移 0x40
     * 长度 1个字节
     * 这是BIOS的INT 13H所描述的设备号码,一般硬盘为80H,一般软盘为00H等
     */
    private Byte biosDriveNumber;

    /**
     * 扩展引导标记
     * 字节偏移 0x41
     * 长度 1个字节
     * 用来确认后面的三个参数是否有效,FAT为29H
     */
    private Byte extendBootFlag;

    /**
     * 卷序列号
     * 字节偏移 0x43
     * 长度 4个字节
     * 磁盘序列号,通常为一随机数
     */
    private Integer volumeSerialNumber;

    /**
     * 卷标
     * 字节偏移 0x47 {@link cn.edu.zstu.fms.storage.BIOSParameterBlock#VOLUME_LABEL_OFFSET_POSITION }
     * 长度 11个字节 {@link cn.edu.zstu.fms.storage.BIOSParameterBlock#VOLUME_LABEL_LENGTH }
     * 用户设置的卷标ASCII,如果没有则4E 4F 20 4E 41 4D 45 20 20 20 20 即NO NAME。如果建立文件系统的时候指定了卷标,会保存在此
     */
    private Character[] volumeLabel;

    /**
     * 文件系统类型
     * 字节偏移 0x52 {@link cn.edu.zstu.fms.storage.BIOSParameterBlock#FILE_SYSTEM_TYPE_OFFSET_POSITION }
     * 长度 8个字节 {@link cn.edu.zstu.fms.storage.BIOSParameterBlock#FILE_SYSTEM_TYPE_LENGTH }
     * 使用ASCII码记录当前分区的文件系统类型,46 41 54 33 32 20 20 20即FAT32
     */
    private Character[] fileSystemType;
    /**
     *
     * @param source
     */
    public BIOSParameterBlock(byte[] source){
        this.source = source;
    }
}


class ByteUtil {
    public static byte[] getBytes(short data) {
        byte[] bytes = new byte[2];
        bytes[0] = (byte) (data & 0xff);
        bytes[1] = (byte) ((data & 0xff00) >> 8);
        return bytes;
    }

    public static byte[] getBytes(char data) {
        byte[] bytes = new byte[2];
        bytes[0] = (byte) (data);
        bytes[1] = (byte) (data >> 8);
        return bytes;
    }

    public static byte[] getBytes(int data) {
        byte[] bytes = new byte[4];
        bytes[0] = (byte) (data & 0xff);
        bytes[1] = (byte) ((data & 0xff00) >> 8);
        bytes[2] = (byte) ((data & 0xff0000) >> 16);
        bytes[3] = (byte) ((data & 0xff000000) >> 24);
        return bytes;
    }

    public static byte[] getBytes(long data) {
        byte[] bytes = new byte[8];
        bytes[0] = (byte) (data & 0xff);
        bytes[1] = (byte) ((data >> 8) & 0xff);
        bytes[2] = (byte) ((data >> 16) & 0xff);
        bytes[3] = (byte) ((data >> 24) & 0xff);
        bytes[4] = (byte) ((data >> 32) & 0xff);
        bytes[5] = (byte) ((data >> 40) & 0xff);
        bytes[6] = (byte) ((data >> 48) & 0xff);
        bytes[7] = (byte) ((data >> 56) & 0xff);
        return bytes;
    }

    public static byte[] getBytes(float data) {
        int intBits = Float.floatToIntBits(data);
        return getBytes(intBits);
    }

    public static byte[] getBytes(double data) {
        long intBits = Double.doubleToLongBits(data);
        return getBytes(intBits);
    }

    public static byte[] getBytes(String data, String charsetName) {
        Charset charset = Charset.forName(charsetName);
        return data.getBytes(charset);
    }

    public static byte[] getBytes(String data) {
        return getBytes(data, "UTF-8");
    }


    public static short getShort(byte[] bytes) {
        return (short) ((0xff & bytes[0]) | (0xff00 & (bytes[1] << 8)));
    }

    public static char getChar(byte[] bytes) {
        return (char) ((0xff & bytes[0]) | (0xff00 & (bytes[1] << 8)));
    }

    public static int getInt(byte[] bytes) {
        return (0xff & bytes[0]) | (0xff00 & (bytes[1] << 8)) | (0xff0000 & (bytes[2] << 16)) | (0xff000000 & (bytes[3] << 24));
    }

    public static long getLong(byte[] bytes) {
        return(0xffL & (long)bytes[0]) | (0xff00L & ((long)bytes[1] << 8)) | (0xff0000L & ((long)bytes[2] << 16)) | (0xff000000L & ((long)bytes[3] << 24))
                | (0xff00000000L & ((long)bytes[4] << 32)) | (0xff0000000000L & ((long)bytes[5] << 40)) | (0xff000000000000L & ((long)bytes[6] << 48)) | (0xff00000000000000L & ((long)bytes[7] << 56));
    }

    public static float getFloat(byte[] bytes) {
        return Float.intBitsToFloat(getInt(bytes));
    }

    public static double getDouble(byte[] bytes) {
        long l = getLong(bytes);
        System.out.println(l);
        return Double.longBitsToDouble(l);
    }

    public static String getString(byte[] bytes, String charsetName) {
        return new String(bytes, Charset.forName(charsetName));
    }

    public static String getString(byte[] bytes) {
        return getString(bytes, "UTF-8");
    }
}

参考文章

FAT32

FAT32文件系统详解

javadoc中{@link}与@see的简单使用以及区别

FAT32文件系统快速入门

FAT32文件系统结构详解

上一篇:ORA-01589错误 要打开数据库则必须使用 RESETLOGS 或 NORESETLOGS 选项


下一篇:在虚拟机中为Linux系统挂载移动存储设备(FAT32格式)