How do you find where the end of a partition ends on a Linux drive?

195 views Asked by At

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);
2

There are 2 answers

1
that other guy On BEST ANSWER

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);
}
0
msbit On

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 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;
}