[fs] linux如何获知文件在分区上对应的块

概述

在包括 Linux ext 系列在内的 inode 文件系统中,文件自身的数据保存在磁盘分区数据块中,并由 inode 索 引。在某些情况下,我们需要知道一个文件在磁盘分区中是由第几个数据块开始的;本文即是介绍其方法。

使用debugfs和stat命令

对于获得文件详细信息,通常使用 stat 命令;但可以发现其中并没有数据块信息,如下图-1 所示:

How-to-know-block-number/pic-1-stat.png

这时我们需要使用 debugfs 来获得更为详尽的信息。
首先,在此文件所在分区中启动 debugfs。然后,再使用 stat 命令。详细步骤参见图-2:

  1. 使用 lsblk 命令确认当前分区信息。我们要查看的文件在/dev/sda5 分区上。
  2. 在这个分区上打开 debugfs。
  3. 使用 stat 命令,我们可以得到结果,如图-3 所示。
  4. 使用 q 退出 debugfs。

How-to-know-block-number/pic-2-debugfs-1.png

在 debugfs 中的 stat 命令结果,如下图-3 所示:
How-to-know-block-number/pic-3-debugfs-2.png

可以看到除了刚刚 stat 得到的普通结果外,“(0):31758604”为数据块信息,其含义是:此文件第 0 块数据块为 31758604。

使用FIBMAP ioctl

写一个程序,使用 FIBMAP ioctl 获知某个数据块是第几块。如代码 ff.c 所示:

#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <linux/hdreg.h>
#include <assert.h>
#include <string.h>
#include <errno.h>

int main(int argc, char *argv[])
{
	int fd = 0;
	int i = 0;
	int block = 0;
	int blocksize = 0;
	int bcount = 0;
	struct stat st;

	assert(NULL != argv[1]);
	assert(fd = open(argv[1], O_RDONLY));
	assert(0 == ioctl(fd, FIGETBSZ, &blocksize));
	assert(!fstat(fd, &st));
	bcount = (st.st_size + blocksize - 1) / blocksize;

	printf("File: %s Size: %ld Blocks: %d Blocksize: %d\n",
			argv[1], st.st_size, bcount, blocksize);

	for (i = 0; i < bcount; i++) {
		block = i;
		if (ioctl(fd, FIBMAP, &block))
			printf("FIBMAP ioctl failed - errno: %s\n", strerror(errno));
		printf("%3d %10d\n", i, block);
	}

	close(fd);
	return 0;
}

运行结果如下图-4 所示:
How-to-know-block-number/pic-4-ff-result.png

可知这与 debugfs 中使用 stat 命令得到的结果一致。

参考信息

https://serverfault.com/questions/29886/how-do-i-list-a-files-data-blocks-on-linux
https://lists.debian.org/debian-mips/2002/04/msg00059.html

END

Aningsk
2020-03-24

Written on March 24, 2020