In my_read_file(), we see much the same code as we saw in the simple read example above. The only strange thing we're doing is we know there's only one byte of data being returned, so if nbytes is nonzero, then it must be one (and nothing else). So, we can construct the data to be returned to the client by stuffing the character variable string directly. Notice how we used the inode member of the attribute structure as the basis of which data to return. This is a common trick used in resource managers that must deal with multiple resources. Another trick would be to extend the attributes structure (as discussed above in Extending the attributes structure) and have either the data stored there directly or a pointer to it.
static int my_read_file (resmgr_context_t *ctp, io_read_t *msg, iofunc_ocb_t *ocb) { size_t nbytes; size_t nleft; char string; // we don't do any xtypes here... if ((msg -> i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) { return (ENOSYS); } // figure out how many bytes are left nleft = ocb -> attr -> nbytes - ocb -> offset; // and how many we can return to the client nbytes = min (nleft, _IO_READ_GET_NBYTES(msg)); if (nbytes) { // create the output string string = ocb -> attr -> inode - 1 + 'A'; // return it to the client MsgReply (ctp -> rcvid, nbytes, &string + ocb -> offset, nbytes); // update flags and offset ocb -> attr -> flags |= IOFUNC_ATTR_ATIME | IOFUNC_ATTR_DIRTY_TIME; ocb -> offset += nbytes; } else { // nothing to return, indicate End Of File MsgReply (ctp -> rcvid, EOK, NULL, 0); } // already done the reply ourselves return (_RESMGR_NOREPLY); }