文件系统占用全部空间

文件系统不覆盖整个分区时,如何才能find文件系统在块设备上实际占用了多less空间?

考虑演示核心问题的基本示例:

dd if=/dev/zero bs=1024 count=20480 of=something.img losetup -f $PWD/something.img mke2fs /dev/loop0 mount /dev/loop0 /mnt 

但是df -k /mnt给出了这个输出:

 Filesystem 1K-blocks Used Available Use% Mounted on /dev/loop0 19827 172 18631 1% /mnt 

我创build了一个精确2048KB的设备,但statvfs()系统调用(和上面显示的df类似)报告文件系统仅占用19827KB。

看来, statvfs()仅报告用户可用的块,但不报告文件系统的全部实际大小。

到目前为止,我只能find一个ext2 / 3/4特定的hacky 解决schemetune2fs -l /dev/loop0 | grep 'Block count:' tune2fs -l /dev/loop0 | grep 'Block count:'或者dumpe2fs -h /dev/loop0 | grep 'Block count:' dumpe2fs -h /dev/loop0 | grep 'Block count:' 。 更清洁一点是使用libext2fs库( e2fprogs包的一部分)来读取超级块,但是我更喜欢文件系统中立的系统调用(如果可用的话)。

如果你正在寻找一个POSIX系统调用来检索设备级别的信息,我相信没有。

对于文件系统级别的信息(便携式)调用,据我所知,您将能够检索data blocks的总数,而不是剩余的开销 (如inode tables等)。

问题

statvfs(2)fstatvfs(2)将检索“可用”(又名data blocks )文件系统级别信息(例如, vfatext2ext3 …),而不是基础设备级别信息(例如/dev/loop0/dev/sda ,…)。

[部分/可能]非便携式解决方案

[ 编辑3 ]:不是什么操作系统正在寻找(这只是设备级信息,并不包括文件系统不映射整个设备的大小的情况下)。

在Linux下(假定是相对较新的内核版本),可以使用ioctl(2)以字节为单位检索设备大小( blockdev(8)执行的相同方法):

 #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/fs.h> int main(void) { int fd = 0; unsigned long long size = 0; if (getuid() != 0) { puts("You need to be root."); exit(EXIT_FAILURE); } if ((fd = open("/dev/loop0", O_RDONLY)) < 0) { printf("open(): %s\n", strerror(errno)); exit(EXIT_FAILURE); } if (ioctl(fd, BLKGETSIZE64, &size) < 0) { printf("ioctl(): %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("%llu\n", size); return 0; } 

编辑

正如评论中所讨论的那样,这是device level information ,所以如果文件系统没有映射整个设备的大小,它不会解决问题(我们仍然不知道文件系统在设备中占用的总大小)。

尝试一个便携式解决方案:)

恐怕你真的需要一切手工 一步一步来做。

你需要知道确切的文件系统,了解它的结构,并做所有的数学。 例如,在你的例子中,你正在使用一个20MiB的Ext2文件系统。 所以,结构看起来像这样(为了这个例子简化):

方块0

  • 引导记录和额外引导记录数据: 1个块 (2 * 512字节)

块组0

  • 超级1块 (1024字节)

  • 块组描述符表,块位图和Inode位图: 3个块

  • Inode表: 214个块

    数据块…

块组1

  • 超级 (备份): 1块

  • 块组描述符表(备份),块位图和Inode位图: 3个块

  • Inode表: 214个块

    数据块…

座组2

  • 块位图和Inode位图: 2个块

  • Inode表: 214个块

    数据块…

现在做数学

  • 1 + 1 + 3 + 214 + 1 + 3 + 214 + 2 + 214 == 653

总和65319827 (你可以用statvfs(2)检索):

  • 653 + 19827 == 20480块

编辑2

正如在注释中所讨论的那样,ext2超级block count已经包含了文件系统占用的 (不仅像我以前认为的那样是可用的块)。 一个简单的测试来观察它(跳过错误检查简单的例子):

 #include <stdio.h> #include <string.h> #include <inttypes.h> #include <sys/types.h> #include <unistd.h> #include <sys/types.h> #include <fcntl.h> struct ext2_super_block { uint32_t s_inodes_count; uint32_t s_blocks_count; uint32_t s_r_blocks_count; uint32_t s_free_blocks_count; uint32_t s_free_inodes_count; uint32_t s_first_data_block; uint32_t s_log_block_size; uint32_t s_dummy3[7]; unsigned char s_magic[2]; uint16_t s_state; uint32_t s_dummy5[8]; uint32_t s_feature_compat; uint32_t s_feature_incompat; uint32_t s_feature_ro_compat; unsigned char s_uuid[16]; char s_volume_name[16]; char s_last_mounted[64]; uint32_t s_algorithm_usage_bitmap; uint8_t s_prealloc_blocks; uint8_t s_prealloc_dir_blocks; uint16_t s_reserved_gdt_blocks; uint8_t s_journal_uuid[16]; uint32_t s_journal_inum; uint32_t s_journal_dev; uint32_t s_last_orphan; uint32_t s_hash_seed[4]; uint8_t s_def_hash_version; uint8_t s_jnl_backup_type; uint16_t s_reserved_word_pad; uint32_t s_default_mount_opts; uint32_t s_first_meta_bg; uint32_t s_mkfs_time; uint32_t s_jnl_blocks[17]; uint32_t s_blocks_count_hi; uint32_t s_r_blocks_count_hi; uint32_t s_free_blocks_hi; uint16_t s_min_extra_isize; uint16_t s_want_extra_isize; uint32_t s_flags; uint16_t s_raid_stride; uint16_t s_mmp_interval; uint64_t s_mmp_block; uint32_t s_raid_stripe_width; uint32_t s_reserved[163]; }; int main(void) { int fd = 0; struct ext2_super_block sb; /* Reset sb memory */ memset(&sb, 0, sizeof(struct ext2_super_block)); /* * /tmp/loop.img created with: * * dd if=/dev/zero bs=1024 count=20480 of=/tmp/loop.img * * ... and the ext2 file system maps the entire device. * */ fd = open("/tmp/loop.img", O_RDONLY); /* Jump to superblock */ lseek(fd, 1024, SEEK_SET); /* Read the superblock */ read(fd, &sb, sizeof(struct ext2_super_block)); /* Print the total block count */ printf("s_blocks_count: %" PRIu32 "\n", sb.s_blocks_count); /* Prints 20480 */ return 0; } 

一些最后的想法

  • 这样做是疯了,国际海事组织(你必须有一个非常非常好的理由这样做:))[ 编辑2 ]:这比我以前认为更简单,因为[可能]所有的文件系统超级块[可能]包含总阻塞计数(请参阅本答复评论的相关讨论)。
  • 如果我是你的话,我会评估这段代码应该运行多少个不同的操作系统,而只是用非可移植的调用来实现设备级信息收集器(他们是直接的系统调用(如果有的话)或库实现)由每个操作系统提供。 然后,构建过程将能够确定和配置代码,以在目标机器上正确编译[ 编辑2 ]:这仍然是最好的方法(利用各自的文件系统库)。
  • 如果你真的不想(或者需要)编写任何代码,而且你只是在寻找一个特定于Linux的命令行工具来检索这些数据(在设备级别),那么可以使用: blockdev --getsize64 /dev/loop0 [ 编辑2 ]:这是设备级别的信息,这不是问题所指的。
  • [ 编辑 ]否则,如果设备级别的信息不够(在文件系统只映射设备大小的一部分),你已经有了答案:用dumpe2fstune2fs和类似的。