A simple io_read() example

To illustrate how your resource manager might return data to a client, consider a simple resource manager that always returns the constant string "Hello, world!\n". There are a number of issues involved, even in this very simple case:

Matching of the client's data area size to the data being returned
In our case, the resource manager is returning a fixed string of 14 bytes—there is exactly that much data available. This is identical to a read-only file on a disk that contains the string in question; the only real difference is that this "file" is maintained in our C program via the statement:
char    *data_string = "Hello, world!\n";

The client, on the other hand, can issue a read() request of any size—the client could ask for one byte, 14 bytes, or more. The impact of this on the io_read() functionality you're going to provide is that you must be able to match the client's requested data size with what's available.

Handling of the EOF case
A natural fallout of the way you handle the client's data area size considerations is the corner case of dealing with the End-Of-File (EOF) on the fixed string. Once the client has read the final \n character, further attempts by the client to read more data should return EOF.
Maintenance of context information (the lseek() index)
Both the "Data area size considerations" and the "Handling of EOF case" scenarios will require that context be maintained in the OCB passed to your io_read() function, specifically the offset member.
Updating of POSIX stat() information
One final consideration: when data is read from a resource manager, the POSIX access time (atime) variable needs to be updated. This is so that a client stat() function will show that someone has indeed accessed the device.