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,

        // 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);