问题:
linux下一块磁盘(容量10G)格式化为ext4文件系统,挂载后,写入2G数据,通过分析linux ext4文件系统的结构,提供找到磁盘设备中的有效数据块的方法(即磁盘中哪个地址有数据,哪些地址是空块)
方案:
1.前期工作:磁盘(容量10G)格式化为ext4文件系统并挂载
1. 查看磁盘情况
首先,需要查看系统中所有磁盘的情况。这可以通过fdisk -l或lsblk命令来完成。
sudo fdisk -l
或
sudo lsblk
这将显示磁盘列表,包括它们的大小、分区情况以及挂载点(如果有的话)。
2. 使用fdisk或parted进行分区
假设决定在/dev/sdb上创建一个新的分区。可以使用fdisk或parted工具来创建分区。这里使用fdisk作为示例。
sudo fdisk /dev/sdb
在fdisk的命令行界面中,可以按照以下步骤操作:
- 输入
m获取帮助。 - 输入
n创建一个新分区。 - 选择分区类型(通常选择主分区或逻辑分区)。
- 选择分区的起始和结束位置。
- 输入
w保存并退出。
3. 格式化分区为EXT4
一旦分区被创建,可以使用mkfs命令来格式化它为EXT4文件系统。
sudo mkfs.ext4 /dev/sdbX
这里的/dev/sdbX是刚刚创建的分区。注意X是分区号,例如sdb1、sdb2等。
4. 挂载分区
在格式化之后,可以将分区挂载到文件系统的某个目录上,以便访问它。首先,创建一个挂载点目录:
sudo mkdir /mnt/mydisk
然后,将分区挂载到这个目录:
sudo mount /dev/sdbX /mnt/mydisk
现在,可以通过/mnt/mydisk目录来访问这个分区上的文件了
2.分析linux ext4文件系统的结构,提供找到磁盘设备中的有效数据块的方法
-
打开设备文件:
- 使用
os.Open打开指定的磁盘设备文件(例如/dev/sdX1)。 - 设备文件通常是块设备文件,表示磁盘分区。
- 使用
-
读取超级块:
- 超级块位于磁盘的特定偏移量(通常为1024字节)。它包含文件系统的总体信息,如块大小、块组数量、每组的块和索引节点数量等。
readSuperblock函数从磁盘中读取超级块数据,并将其解析为Superblock结构体。
-
读取块组描述符:
- ext4文件系统将磁盘划分为多个块组。每个块组有一个描述符,存储块组的各种信息,包括块位图的偏移量。
readGroupDescriptor函数根据块组编号(示例中假设为块组0)计算块组描述符的位置,并读取描述符的内容。描述符包括块位图的位置。
-
读取块位图:
- 块位图是一个表示哪些数据块被使用的位图。每一位表示一个块的使用状态。
readBlockBitmap函数根据块位图在磁盘上的偏移量读取块位图数据。偏移量由超级块和块组描述符提供。
-
打印有效数据块:
printUsedBlocks函数分析块位图数据。每个位表示一个块是否被使用。如果位为1,则块被使用;如果为0,则块为空闲。- 该函数遍历块位图,输出所有被标记为使用的数据块号。
3.附件代码
package main
import (
"bytes"
"encoding/binary"
"fmt"
"os"
)
const (
// 偏移量为超级块的位置
SUPERBLOCK_OFFSET = 1024
// 每个块的大小
BLOCK_SIZE = 4096
// 块组描述符的数量
GROUP_DESCRIPTOR_SIZE = 32
)
type Superblock struct {
InodesCount uint32
BlocksCount uint32
ReservedBlocks uint32
FreeBlocksCount uint32
FreeInodesCount uint32
FirstDataBlock uint32
BlockSize uint32
BlocksPerGroup uint32
InodesPerGroup uint32
// 省略其他字段
}
type GroupDescriptor struct {
TotalBlocks uint32
FreeBlocks uint32
FreeInodes uint32
Directories uint32
BlockBitmap uint32
InodeBitmap uint32
InodeTable uint32
// 省略其他字段
}
func readSuperblock(file *os.File) (*Superblock, error) {
file.Seek(SUPERBLOCK_OFFSET, 0)
buf := make([]byte, 1024)
if _, err := file.Read(buf); err != nil {
return nil, err
}
sb := &Superblock{}
bufReader := bytes.NewReader(buf)
if err := binary.Read(bufReader, binary.LittleEndian, sb); err != nil {
return nil, err
}
return sb, nil
}
func readGroupDescriptor(file *os.File, group int) (*GroupDescriptor, error) {
offset := SUPERBLOCK_OFFSET + BLOCK_SIZE + int64(group*GROUP_DESCRIPTOR_SIZE)
file.Seek(offset, 0)
buf := make([]byte, GROUP_DESCRIPTOR_SIZE)
if _, err := file.Read(buf); err != nil {
return nil, err
}
gd := &GroupDescriptor{}
bufReader := bytes.NewReader(buf)
if err := binary.Read(bufReader, binary.LittleEndian, gd); err != nil {
return nil, err
}
return gd, nil
}
func readBlockBitmap(file *os.File, blockBitmapOffset int64) ([]byte, error) {
file.Seek(blockBitmapOffset, 0)
bitmap := make([]byte, BLOCK_SIZE)
if _, err := file.Read(bitmap); err != nil {
return nil, err
}
return bitmap, nil
}
func printUsedBlocks(bitmap []byte) {
for i, b := range bitmap {
for j := 0; j < 8; j++ {
if (b & (1 << j)) != 0 {
fmt.Printf("Block %d is used\n", i*8+j)
}
}
}
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: go run main.go <device>")
return
}
device := os.Args[1]
file, err := os.Open(device)
if err != nil {
fmt.Printf("Error opening device: %v\n", err)
return
}
defer file.Close()
sb, err := readSuperblock(file)
if err != nil {
fmt.Printf("Error reading superblock: %v\n", err)
return
}
fmt.Printf("Superblock Info: %+v\n", sb)
// 假设块组0
gd, err := readGroupDescriptor(file, 0)
if err != nil {
fmt.Printf("Error reading group descriptor: %v\n", err)
return
}
blockBitmapOffset := SUPERBLOCK_OFFSET + int64(sb.BlockSize)*int64(gd.BlockBitmap)
bitmap, err := readBlockBitmap(file, blockBitmapOffset)
if err != nil {
fmt.Printf("Error reading block bitmap: %v\n", err)
return
}
printUsedBlocks(bitmap)
}
解释
- 超级块:
readSuperblock函数从磁盘读取超级块信息,包含文件系统的基本参数。 - 块组描述符:
readGroupDescriptor函数读取指定块组的描述符,从中获取块位图的位置。 - 块位图:
readBlockBitmap函数从指定位置读取块位图数据。 - 打印有效块:
printUsedBlocks函数根据块位图的数据打印出被使用的块号。
注意事项
- 文件路径:运行时需要指定设备路径,例如
/dev/sdX1。 - 偏移量和大小:不同文件系统或配置可能需要调整偏移量和大小。
- 权限:访问设备文件需要适当的权限,通常需要以root用户或具有相应权限的用户运行。