Search code examples
linuxshellcommand-linefilesystemsfuse

Is there a linux command (along the lines of cat or dd) that will allow me to specify the offset of the read syscall?


I am working on a homework assignment for an operating systems class, and we are implementing basic versions of certain file system operations using FUSE.

The only operation that we are implementing that I couldn't test to a point I was happy with was the read() syscall. I am having trouble finding a way to get the read() syscall to be called with an offset other than 0.

I tried some of the commands (like dd, head, and tail) mentioned in answers to this question, but by the time that they reached my implementation of the read() syscall the offset was 0. To clarify, when I called these commands I received (at the calling terminal) the bytes in the file that were specified in the calls, but in another terminal that was displaying the syscalls that were being handled by FUSE, and hence my implementations, it displayed that my implementation of the read() syscall was always being called with offset 0 (and usually size of 4096, which I presume is the block size of the real linux file system I am using). I assume that these commands are making read() syscalls in blocks of 4096 bytes, then internally (i.e., within the dd, head, or tail command's code rather than through syscalls) modifying the output to what is seen on the calling terminal.

Is there any command (or script) I can run (or write and then run in the case of the script) that will allow me to test this syscall with varying offset values?


Solution

  • I figured out the issue I was having. For posterity, I will record the answer rather than just delete my question, because the answer wasn't necessarily easy to find.

    Essentially, the issue occurred within FUSE. FUSE defaults to not using direct I/O (which is definitely the correct default to have, don't get me wrong), which is what resulted in the reads in size chunks of 4096 (these are the result of FUSE using a page cache of file contents [AKA a file content cache] in the kernel). For what I wanted to test (as explained in the question), I needed to enable direct I/O. There are a few ways of doing this, but the simplest way for me to do this was to pass -o direct_io as a command line argument. This worked for me because I was using the fuse_main call in the main function of my program.

    So my main function looked like this:

    int main(int argc, char *argv[])
    {
        return fuse_main(argc, argv, &my_operations_structure, NULL);
    }
    

    and I was able to call my program like this (I used the -d option in addtion to the -o direct_io option in order to display the syscalls that FUSE was processing and the output/debug info from my program):

    ./prog_name -d -o direct_io test_directory
    

    Then, I tested my program with the following simple test program (I know I don't do very much error checking, but this program is only for some quick and dirty tests):

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    int main(int argc, char *argv[])
    {
        FILE * file;
        char buf[4096];
    
        int fd;
    
        memset(&buf[0], 0, sizeof(buf));
    
        if (argc != 4)
        {
            printf("usage: ./readTest [size] [offset] [filename]\n");
            return 0;
        }
    
        file = fopen(argv[3], "r");
        if (file == NULL)
        {
            printf("Couldn't open file\n");
            return -1;
        }   
    
        fd = fileno(file);
    
        pread(fd, (void *) buf, atoi(argv[1]), (off_t) atoi(argv[2]));
    
        printf("%s\n", buf);
    
        return 0;
     }