Reading boot sector of a FAT32 file system - linux

I am writing a program and I need to access some information in the boot sector about the FAT32 file system that I have mounted.
Here is what I did, fully commented.
int main (void) {
unsigned short *i; //2 byte unsigned integer pointer
char tmp[1024]; //buffer
int fd; //file descriptor from opening device
fd = open("/dev/sdf", O_RDONLY); //open the device file
lseek(fd, 14, SEEK_SET); //set offset to 14 (0x0E), i.e. storing "no. of reserved sectors" of the FAT32 system
read(fd, tmp, 2); //read off 2 bytes, "no. of reserved sectors" is represented by 2 bytes
i = &tmp; //point j at those 2 bytes
printf("*j: %d\n", *j); //print *j out
close(fd); //close device
return 0;
}
The output of *i is 38, which is nonsense. I formatted the file system with mkfs.vfat. I set the "no. of reserved sectors" to 32.
What I have tried:
i = (unsigned short *) &tmp, do a casting, this removes the warning when I compile, but doesn't help
read(fd, tmp, 512), load the whole boot sector (512 bytes) into tmp, then read from the buffer, but doesn't help, result still 38 though.
fiddle with the offset, i.e. change 14 to 13 or 15, in case I get the index wrong. It prints out 9744 for 13 and 512 for 15 respectively, so doesn't work.
I'm not sure whether I'm doing it the right way. Can someone please point me in the right direction?
Thanks in advance,
Felastine.

Try running:
$ dd if=/dev/sdf of=/tmp/x.bin bs=512 count=1
And then:
$ hd /tmp/x.bin
Or
$ od -tx2 /tmp/x.bin
And post the first lines.
Chances are that your fattools are adding 6 extra reserved sectors of their own. And then they substract them before showing the data.

unsigned short *i; //2 byte unsigned integer pointer
char tmp[1024];
[...]
i = &tmp; //point j at those 2 bytes
tmp is char[], &tmp something of order char**.
Think again, you don't want the & here.

Related

Why does cat call read() twice when once was enough?

I am new to Linux kernel module. I am learning char driver module based on a web course. I have a very simple module that creates a /dev/chardevexample, and I have a question for my understanding:
When I do echo "hello4" > /dev/chardevexample, I see the write execute exactly once as expected. However, when I do cat /dev/chardevexample, I see the read executed two times.
I see this both in my code and in the course material. All the data was returned in the first read(), so why does cat call it again?
All the things I did so far are as follows:
insmod chardev.ko to load my module
echo "hello4" > /dev/chardevexample. This is the write and I see it happening exactly once in dmesg
cat /dev/chardevexample. This is the read, and dmesg shows it happening twice.
I did strace cat /dev/chardevexample, and I indeed see the function call being called twice for read. There is a write in between as well
read(3, "hello4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072) = 4096
write(1, "hello4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096hello4) = 4096
read(3, "", 131072)
dmesg after read (cat command)
[909836.517402] DEBUG-device_read: To User hello4 and bytes_to_do 4096 ppos 0 # Read #1
[909836.517428] DEBUG-device_read: Data send to app hello4, nbytes=4096 # Read #1
[909836.519086] DEBUG-device_read: To User and bytes_to_do 0 ppos 4096 # Read #2
[909836.519093] DEBUG-device_read: Data send to app hello4, nbytes=0 # Read #2
Code snippet for read, write and file_operations is attached. Any
guidance would help. I searched extensively and couldn't understand.
Hence the post.
/*!
* #brief Write to device from userspace to kernel space
* #returns Number of bytes written
*/
static ssize_t device_write(struct file *file, //!< File pointer
const char *buf,//!< from for copy_from_user. Takes 'buf' from user space and writes to
//!< kernel space in 'buffer'. Happens on fwrite or write
size_t lbuf, //!< length of buffer
loff_t *ppos) //!< position to write to
{
int nbytes = lbuf - copy_from_user(
buffer + *ppos, /* to */
buf, /* from */
lbuf); /* how many bytes */
*ppos += nbytes;
buffer[strcspn(buffer, "\n")] = 0; // Remove End of line character
pr_info("Recieved data \"%s\" from apps, nbytes=%d\n", buffer, nbytes);
return nbytes;
}
/*!
* #brief Read from device - from kernel space to user space
* #returns Number of bytes read
*/
static ssize_t device_read(struct file *file,//!< File pointer
char *buf, //!< for copy_to_user. buf is 'to' from buffer
size_t lbuf, //!< Length of buffer
loff_t *ppos)//!< Position {
int nbytes;
int maxbytes;
int bytes_to_do;
maxbytes = PAGE_SIZE - *ppos;
if(maxbytes >lbuf)
bytes_to_do = lbuf;
else
bytes_to_do = maxbytes;
buffer[strcspn(buffer, "\n")] = 0; // Remove End of line character
printk("DEBUG-device_read: To User %s and bytes_to_do %d ppos %lld\n", buffer + *ppos, bytes_to_do, *ppos);
nbytes = bytes_to_do - copy_to_user(
buf, /* to */
buffer + *ppos, /* from */
bytes_to_do); /* how many bytes*/
*ppos += nbytes;
pr_info("DEBUG-device_read: Data send to app %s, nbytes=%d\n", buffer, nbytes);
return nbytes;} /* Every Device is like a file - this is device file operation */ static struct file_operations device_fops = {
.owner = THIS_MODULE,
.write = device_write,
.open = device_open,
.read = device_read,};
The Unix convention for indicating end-of-file is to have read return 0 bytes.
In this case, cat asks for 131072 bytes and only receives 4096. This is normal and not to be interpreted as having reached the end of the file. For example, it happens when you read from the keyboard but the user only inputs a small amount of data.
Because cat has not yet seen EOF (i.e. read did not return 0), it continues to issue read calls until it does. This means that if there's any data, you will always see a minimum of two read calls: one (or more) for the data, and one final one that returns 0.

Logical block number to address (Linux filesystem)

int block_count;
struct stat statBuf;
int block;
int fd = open("file.txt", O_RDONLY);
fstat(fd, &statBuf);
block_count = (statBuf.st_size + statBuf.st_blksize - 1) / statBuf.st_blksize;
int i,add;
for(i = 0; i < block_count; i++) {
block = i;
if (ioctl(fd, FIBMAP, &block)) {
perror("FIBMAP ioctl failed");
}
printf("%3d %10d\n", i, block);
add = block;
}
char buffer[255];
int fd2 = open("/dev/sda1", O_RDONLY);
lseek(fd2, add, SEEK_SET);
read(fd2, buffer, 20);
printf("%s%s\n","ss ",buffer);
Output:
0 5038060
1 5038061
2 5038062
3 5038063
4 5038064
5 5038065
ss
I am using the above code to get the logical block number of a file. Lets suppose I want to read the contents of the last block number, How would I do that?
Is there a way to get the address of a block from logical block number?
PS: I am using linux and filesystem is ext4
The above code actually is getting you the physical block number. The FIBMAP ioctl takes as input the logical block number, and returns the physical block number. You can then multiply that by the blocksize (which is 4k for most ext4 file systems, but you can get that by using the BLKBSZGET ioctl) to get the byte offset if you want to read that disk block using lseek and read.
Note that the more modern interface that people tend to use today is the FIEMAP interface. This doesn't require root, and returns the physical byte offset, plus a lot more data. For more information, please see:
https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
Or you can look at the source code for the filefrag command, which is part of e2fsprogs:
https://git.kernel.org/cgit/fs/ext2/e2fsprogs.git/tree/misc/filefrag.c

ioctl() call resets file descriptor to 0

Consider the following code:
file_fd = open(device, O_RDWR);
if (file_fd < 0) {
perror("open");
return -1;
}
printf("File descriptor: %d\n", file_fd);
uint32_t DskSize;
if (ioctl(file_fd, BLKGETSIZE, &DskSize) < 0) {
perror("ioctl");
return -1;
}
printf("File descriptor after: %d\n", file_fd);
This snippet yields this:
File descriptor: 3
File descriptor after: 0
Why does my file descriptor get reset to 0? The program writes the stuff out to stdout instead of my block device.
This should not happen. I expect my file_fd to be non-zero and retain its value.
Looks like you smash your stack.
Since there are only two stack variables file_fd and DskSize and changing DskSize changes file_fd suggests that DiskSize must be unsigned long or size_t (a 64-bit value), not uint32_t.
Looking at BLKGETSIZE implementation confirms that the value type is unsigned long.
You may like to run your applications under valgrind, it reports this kind of errors.

kmalloc: only allocating 4 bytes

So I am trying to dynamically allocate a buffer on module initialization. The buffer needs to be in scope at all times as it stores data that user space programs interact with. So here is my code:
static char* file_data
#define MAX_SIZE 256
.
.
.
{
file_data = kzalloc(MAX_SIZE, GFP_KERNEL)
.
.
.
}
However when I do sizeof file_data it always returns 4. What am I doing wrong?
Edit: The buffer stores input from a user space program, but 4 characters is all that can be stored.
size_t read_file(char* __user buf, size_t count)
{
unsigned int len = 0;
len = copy_to_user(buf, file_data, count);
return count;
}
ssize_t write_file(char* __user buf, size_t count)
{
if(count >= MAX_SIZE)
return -EINVAL;
copy_from_user(file_data, buf,count)
return count;
}
file_data is a pointer. On a 32-bit platform, it's size is 32 bits, or 4 bytes. What you want to know is the size of the data pointed to by file_data. You can't use the sizeof operator for this because sizeof is a compile time operation. You can't use it on things allocated dynamically at run time.
(Besides, you already know the size of the data pointed to by file_data -- it's MAX_SIZE?)
char *file_data is a pointer to a char. Evidently you're on a 32-bit system so any pointer is 4 bytes. The compiler (which handles sizeof) doesn't know or care how much memory you're allocating for file_data to point to, it just knows you're asking for the size of the pointer (which you are, whether you meant to or not). If you want the size of the memory it points to, you'll have to keep track of it yourself.

How to read and write with mmap() on a Linux system

I need to make some stream in and out classes using mmap() in Linux. To do so I tried to make some test code that writes some integers to a file, saves it, loads it again and write the data in the file to cout. If that test code works, then It wont be a problem making stream in and out afterwards.
When I first started out I got segment faults and If I did not get that nothing happened, so I googled a little. I found this book http://www.advancedlinuxprogramming.com/alp-folder/alp-ch05-ipc.pdf where around page 107 there is some usefull code. I copy pasted that code and made some small changes and got this code:
int fd;
void* file_memory;
/* Prepare a file large enough to hold an unsigned integer. */
fd = open ("mapTester", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
//Make the file big enough
lseek (fd, 4 * 10 + 1, SEEK_SET);
write (fd, "", 1);
lseek (fd, 0, SEEK_SET);
/* Create the memory mapping. */
file_memory = mmap (0, 4 * 10, PROT_WRITE, MAP_SHARED, fd, 0);
close (fd);
/* Write a random integer to memory-mapped area. */
sprintf((char*) file_memory, "%d\n", 22);
/* Release the memory (unnecessary because the program exits). */
munmap (file_memory, 4 * 10);
cout << "Mark" << endl;
//Start the part where I read from the file
int integer;
/* Open the file. */
fd = open (argv[1], O_RDWR, S_IRUSR | S_IWUSR);
/* Create the memory mapping. */
file_memory = mmap (0, 4 * 10, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
close (fd);
/* Read the integer, print it out, and double it. */
scanf ((char *) file_memory, "%d", &integer);
printf ("value: %d\n", integer);
sprintf ((char*) file_memory, "%d\n", 2 * integer);
/* Release the memory (unnecessary because the program exits). */
munmap (file_memory, 4 * 10);
But I get a segment fults after the "mark" cout.
Then I replace the "read part" with this:
fd = open("mapTester", O_RDONLY);
int* buffer = (int*) malloc (4*10);
read(fd, buffer, 4 * 10);
for(int i = 0; i < 1; i++)
{
cout << buffer[i] << endl;
}
That is some working code that shows me that the file is empty. I tries out a couple of ways to write to the mapping without any change in result.
So how am I amble to make my code write?
And does my mmap read code seem okay (just in case you can see some obvious flaws)?
I have found some other resources that did not help me yet, but because I am a new user I may only post max 2 links.
You should test the result of mmap. If it gives MAP_FAILED check out errno to find out why.
And you'll better mmap a multiple of pages, often 4K bytes each, and given by sysconf(_SC_PAGESIZE)
You can use stat to find out the size of (and many other numbers about) some given file.
You could use strace on existing Linux programs to learn what syscalls they are doing.
See also this about /proc/ etc.
Your scanf() call should be sscanf(), and the second open should use "mapTester" instead of argv[1] as the filename. When I fix these bugs, your posted program works (printing out 22 and leaving 44 in the file).

Resources