In one of my classes our assignment is to find and print some values in the first superblock and then compare those values against all other superblocks in the power of 3, 5 and 7 until the end of the partition.
So for example, you compare superblock0 with superblock1, 0 and 3, 0 and 5, 0 and 7, 0 and 9 and so on.
My code to output the values in a superblock works, and I have an algorithm in mind that will get all powers of 3, 5 and 7 but I'm not sure how to detect the end of the partition.
And how to loop through all superblocks with those powers until the end or what the break case would be.
Below is my code to access the first superblock.
int fd;
super_block_t s;
if((fd = open(DEVICE, O_RDONLY)) < 0){ //check if disk can be read
perror(DEVICE);
exit(1);
}
//read superblock
lseek(fd, OFFSET, SEEK_SET);
read(fd, &s, sizeof(s));
close(fd);
CodePudding user response:
You can either try to seek there and see if you get a EINVAL
, or you can use the BLKGETSIZE64
ioctl to fetch the size of the block device:
#include <linux/fs.h>
#include <stdint.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
void main(int argc, char** argv) {
int fd = open(argv[1], O_RDONLY);
uint64_t size;
ioctl(fd, BLKGETSIZE64, &size);
printf("Size in bytes: %llu\n", size);
}
CodePudding user response:
There is a difference between the size of the block device a filesystem is on, and the size of that filesystem itself. Given your task is to operate on the superblocks within the filesystem itself, I'll presume that you are more interested in the latter. If you're more interested in the actual device size, then the answer by @that-other-guy is correct.
Assuming that you are working with ext4
for the filesystem, and based on the information here, the full size of the filesystem would be the total block count multiplied by the block size. In the structure of the superblock, the relevant fields are:
s_blocks_count_lo
at offset 0x4, ands_log_block_size
at offset 0x18
s_blocks_count_lo
is straightforward, but s_log_block_size
needs a bit of processing, as the value stored means:
Block size is 2 ^ (10 s_log_block_size).
Putting all that together, you can do something like:
uintmax_t get_filesystem_size(const char *device) {
int fd;
if((fd = open(device, O_RDONLY)) < 0) {
perror(device);
exit(1);
}
if (lseek(fd, 1024, SEEK_SET) < 0) {
perror("lseek");
exit(1);
}
uint8_t block0[1024];
if (read(fd, &block0, 1024) < 0) {
perror("read");
exit(1);
}
if (s_magic(block0) != 0xef53) {
fprintf(stderr, "bad magic\n");
exit(1);
}
close(fd);
return s_blocks_count_lo(block0) * s_block_size(block0);
}
with the following ext4 superblock specific helper functions:
uint16_t s_magic(const uint8_t *buffer) {
return le16(buffer 0x38);
}
uint32_t s_blocks_count_lo(const uint8_t *buffer) {
return le32(buffer 0x4);
}
uintmax_t s_block_size(const uint8_t *buffer) {
return 1 << (10 le32(buffer 0x18));
}
and the following general endianness helper functions:
uint16_t le16(const uint8_t *buffer) {
int result = 0;
for (int i = 1; i >= 0; i--) {
result *= 256;
result = buffer[i];
}
return result;
}
uint32_t le32(const uint8_t *buffer) {
int result = 0;
for (int i = 3; i >= 0; i--) {
result *= 256;
result = buffer[i];
}
return result;
}