-2

When trying to read input_events from /dev/input/event16 I noticed that the size of the buffer I'm reading into may cause an exception. Here is the code I wrote:

    public static void main(String[] args) throws IOException{
        FileInputStream is = new FileInputStream("/dev/input/event16");
        byte[] three_bytes = new byte[3];
        byte[] twentyfour_bytes = new byte[24];
        is.read(three_bytes); // fails
        is.read(twentyfour_bytes); // does not fail

    }

My initial experiments suggest that the buffer needs capacity for at least one full input_event struct. But I could not find out why.

The problem is the line is.read(three_bytes); causes the follwoing exception:

Exception in thread "main" java.io.IOException: Invalid argument
        at java.base/java.io.FileInputStream.readBytes(Native Method)
        at java.base/java.io.FileInputStream.read(FileInputStream.java:249)
        at main.Test.main(Test.java:11)

I would like to figure out why the line is.read(three_bytes); throws the exception while is.read(twentyfour_bytes); reads the data as expected

user2887596
  • 641
  • 5
  • 15
  • I'm not sure if I understand you correctly. Do you want to programmatically get the size of `/dev/input/event16`? – zhh Jun 08 '21 at 08:49
  • No, I try to read the content of the file – user2887596 Jun 08 '21 at 08:55
  • It's still not clear to me what you are going to find out. It seems you can read the file with a 24 bytes array, why not just use the larger array? – zhh Jun 08 '21 at 09:03
  • I am curious why I need the larger array. – user2887596 Jun 08 '21 at 09:20
  • I edited the question to make it clearer what I'm asking – user2887596 Jun 08 '21 at 10:03
  • Just wrap a `BufferedInputStream` around the `FileInputStream` and you will eliminate the whole problem. – user207421 Jun 14 '21 at 07:19
  • 1
    Read this: https://stackoverflow.com/questions/39380993/reading-dev-input-js0-from-java –  Jun 14 '21 at 07:51
  • This is hard to debug because the error occurs in the native code of `FileInputStream.readBytes()`, which depends on the implementation. For example, the source code for OpenJDK 8 FileInputStream can be found at `http://hg.openjdk.java.net/jdk8u/jdk8u60/jdk/file/935758609767/src/share/native/java/io/FileInputStream.c` but there is no mention of an illegal argument exception message so it must be in some other file. – Konrad Höffner Jun 14 '21 at 08:16
  • It's in the operating system. *Obviously.* It doesn't allow you to read 3 bytes from that file. Nothing to do with Java whatsoever. – user207421 Jun 14 '21 at 08:36
  • The issue could also be due to the file system: https://stackoverflow.com/questions/780543/java-io-ioexception-invalid-argument –  Jun 14 '21 at 09:07
  • Well, it seems to make sense to read whole `struct input_event` (size 24 bytes, as you said, see also [here](https://thehackerdiary.wordpress.com/2017/04/21/exploring-devinput-1/)). Why would you read only chunks of it? Anyway, can you please confirm that using a buffered input stream, maybe conveniently wrapping a data input stream, solves the issue? – kriegaex Jun 14 '21 at 11:12
  • @QuinncyJones The `/dev` filesystem is provided by the operating system. – user207421 Jun 14 '21 at 23:20
  • Yes, that's clear. The error message `Invalid argument at java.base/java.io.FileInputStream.readBytes(Native Method)` makes it clear that the error message is triggered by the filesystem. When reading from the file system you always have to expect that certain requirements have to be met. As far as I know, reading mouse events can make a difference whether the mouse has a scroll wheel or not. –  Jun 15 '21 at 06:50

1 Answers1

2

I would like to figure out why the line is.read(three_bytes); throws the exception while is.read(twentyfour_bytes); reads the data as expected.

For a start, /dev/input/event16 is not a regular file. It is a device file, and device files often don't behave like regular files.

In this case, the /dev/input/event* device files are for reading events from input devices. When you perform a read syscall on them, they will return one or more complete events. These are binary data whose format is given by the following C struct:

struct input_event {    
    struct timeval time;    
    unsigned short type;
    unsigned short code;
    unsigned int value; 
};

The size of that struct is (presumably) 24 bytes on a typical 64bit Linux system, though.

My initial experiments suggest that the buffer needs capacity for at least one full input_event struct. But I could not find out why.

The behavior of the event devices is documented in Linux Input drivers v1.0, section 5. It states that a read will always give a whole number of input_event structs.

It follows that the read syscall provides a buffer that is less than sizeof(input_event), the kernel cannot return anything. Apparently, this causes the read syscall to fail with errno value EINVAL. (IMO, this is a reasonable design choice, and consistent with the documented meaning of EINVAL.)


So, in Java, when you call read(three_bytes), that will map to a read syscall with a read size of 3 bytes which fails; see above. The syscall failure is signaled to the Java application by throw an IOException.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216