my_open()

While my_open() is very short, it has a number of crucial points.

Notice how we decide if the resource being opened is a “file” or a “directory”, based only on the pathname length. We can do this “trick” because we know that there are no other directories in this resource manager apart from the main one. If you want to have multiple directories below the mountpoint, you have to do more complicated analysis of the path member of the msg structure. For our simple example, if there's nothing in the pathname, we know it's the directory. Also, notice the extremely simplified pathname validation checking: we simply compare to make sure that there's only one character passed to us, and that the character lies within the range “a” through “z” inclusive. Again, for more complex resource managers, you'd be responsible for parsing the name past the registered mountpoint.

Now, the most important feature! Notice how we used the POSIX-layer default functions to do all the work for us! The iofunc_open_default() function is usually installed in the connect functions table at the same spot that our new my_open() function is now occupying. This means that it takes the identical set of arguments! All we have to do is decide which attributes structure we want to have bound with the OCB that the default function is going to create: either the directory one (in which case we pass attr), or one of the 26 different ones for the 26 different files (in which case we pass an appropriate element out of atoz_attrs). This is key, because the handler that you put in the open slot in the connect functions table acts as the gatekeeper to all further accesses to your resource manager.

static int
my_open (resmgr_context_t *ctp, io_open_t *msg,
         iofunc_attr_t *attr, void *extra)
{
    // an empty path means the directory, is that what we have?
    if (msg -> connect.path [0] == 0) {
        return (iofunc_open_default (ctp, msg, attr, extra));

    // else check if it's a single char 'a' -> 'z'
    } else if (msg -> connect.path [1] == 0 && 
               (msg -> connect.path [0] >= 'a' && 
                msg -> connect.path [0] <= 'z')) {

        // yes, that means it's the file (/dev/atoz/[a-z])
        return (iofunc_open_default (ctp, msg, 
                atoz_attrs + msg -> connect.path [0] - 'a', 
                extra));
    } else {
        return (ENOENT);
    }
}