Search code examples
androidcandroid-ndkjava-native-interfaceinvalid-argument

Write error: Invalid argument, when file is opened with O_DIRECT


it is very important to me to write to a file with the O_DIRECT flag.

This is how I open the file:

//Open the file
int fd;
if((fd = open(inFilepath, O_WRONLY | O_CREAT |O_SYNC |O_DIRECT,S_IRUSR|S_IWUSR))<0) {
    //Error handling
    return;
}

I know about O_DIRECT's alignment restrictions. This is why I initialize my buffer with calloc:

char *buff = (char *) calloc((size_t) 1,sizeof(char));

if(write(fd,buff,(size_t)1)<1) {
    //Error logging
    free(buff);
    return -1;
}

And I get the write: Invalid argument error. I even tried to use more extreme measures such as memalign and posix_memalign, but had issues with them (memalign got stuck, and posix_memalign is missing for the ARM processor).

When I comment out the O_DIRECT flag, everything works as it should (but I/O is not direct, which is what I need).

Anyone has any insight as to why this is happening? If O_DIRECT was not implemented in Android, then it should've failed at open(), not at write(); so I must be doing something wrong!

Thanks -LD


Solution

  • I solved it (with your guidance)- and wanted to post my solution in case anyone in the future has similar problems.

    The trick was that with the O_DIRECT flag you need to align both the memory address and your buffer to the filesystem's block size (or at least, block size worked for me; sector didn't).

    struct stat fstat;
    stat(filepath, &fstat); 
    int blksize = (int)fstat.st_blksize;
    int align = blksize-1;
    
    const char *buff = (char *) malloc((int)blksize+align);
    buff = (char *)(((uintptr_t)buff+align)&~((uintptr_t)align));
    
    if(write(fd,buff,(size_t)blksize)<1) { 
            //Error handling
            free((char *)buff);
            return -1;
    }
    

    I did two main things:

    1. Found the filesystem hosting my file's block size using stat() and accessing the st_blksize attribute.
    2. Allocated align more bytes than I need. I then added those extra align bytes to the pointer address so that masking off the bits to the lower block size alignment wouldn't leave me with less memory allocated than I wanted. Then of course you AND the bits with the mask (created by flipping the bits of align which is blksize-1), and voila- your buffer is blksize-aligned.

    Also note that the amount you write also has to be aligned to block size (at least in my case).

    -LD