#include <limits.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#define ALIGN(x) (((x) +3) & ~3)
#define NUM_ENTS 26
static iofunc_attr_t atoz_attrs[NUM_ENTS];
int main (int argc, char **argv) {
dispatch_t *dpp;
resmgr_attr_t resmgr_attr;
resmgr_context_t *ctp;
resmgr_connect_funcs_t connect_func;
resmgr_io_funcs_t io_func;
iofunc_attr_t attr;
int i;
// Создать структуру диспетчеризации
if ((dpp = dispatch_create()) == NULL) {
perror("Ошибка dispatch_create\n");
exit(EXIT_FAILURE);
}
// Инициализировать структуры данных
memset(&resmgr_attr, 0, sizeof(resmgr_attr));
resmgr_attr.nparts_max = 1;
resmgr_attr.msg_max_size = 2048;
// Назначить обработчики по умолчанию
iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_func,
_RESMGR_IO_NFUNCS, &io_func);
// Создать и инициализировать атрибутную запись для
// каталога...
iofunc_attr_init(&attr, S_IFDIR | 0555, 0, 0);
attr.inode = NUM_ENTS + 1; // 1-26 зарезервированы для
// файлов от «a» до «z»
attr.nbytes = NUM_ENTS; // 26 элементов в каталоге
// ...и для имен от «a» до «z»
for (i = 0; i < NUM_ENTS; i++) {
iofunc_attr_init(&atoz_attrs[i], S_IFREG | 0444, 0, 0);
atoz_attrs[i].inode = i + 1;
atoz_attrs[i].nbytes = 1;
}
// Добавить наши функции; нам интересны только io_open
// и io_read
connect_func.open = my_open;
io_func.read = my_read;
// Зарегистрировать префикс
if (resmgr_attach(dpp, &resmgr_attr, "/dev/atoz",
_FTYPE_ANY, _RESMGR_FLAG_DIR, &connect_func,
&io_func, &attr) == -1) {
perror("Ошибка resmgr_attach\n");
exit(EXIT_FAILURE);
}
// Выделить контекст
ctp = resmgr_context_alloc(dpp);
// Ждать сообщений в вечном цикле
while (1) {
if ((ctp = resmgr_block(ctp)) == NULL) {
perror("Ошибка resmgr_block\n");
exit(EXIT_FAILURE);
}
resmgr_handler(ctp);
}
}
При том, что функция my_open() очень невелика, в ней есть ряд критических мест. Обратите внимание, как мы принимаем решение о том, был ли ресурс открыт как «файл» или как «каталог», на основе только длины имени пути. Мы можем себе это позволить, потому что знаем, что других каталогов, кроме основного, в этом администраторе ресурсов нет. Если вы захотите расположить ниже вашей точки монтирования дополнительные каталоги, вам придется применить более сложный механизм анализа поля path структуры msg. В нашем простом примере, если в имени пути ничего нет, то мы знаем, что это каталог. Также обратите внимание на чрезвычайно упрощенную проверку корректности имени пути: мы просто проверяем, что у нас действительно только один символ, и что он лежит в диапазоне от «a» до «z» включительно. Опять же, в случае более сложного администратора ресурсов вам пришлось бы выполнять синтаксический анализа имени, следующего за зарегистрированной точкой монтирования.
Теперь о наиболее важной особенности. Обратите внимание, как мы использовали для выполнения всей нашей работы функции POSIX-уровня по умолчанию! Функция iofunc_open_default() обычно размещается в таблице функций установления соединения в той же самой ячейке, которую сейчас занимает наша функция my_open(). Это означает, что они принимают одинаковый набор аргументов!
Все, что мы должны сделать — это решить, какую атрибутную запись мы хотим связать с OCB, создаваемым функцией по умолчанию: либо каталоговую (в этом случае мы передаем attr), либо одну из 26 имеющихся у нас файловых (тогда мы передаем соответствующий элемент atoz_attrs). Это ключевой момент, поскольку обработчик, который вы помещаете в слот open в таблице функций установления соединения, действует как «швейцар» по отношению ко всем последующим запросам к вашему администратору ресурса.
static int my_open(resmgr_context_t *ctp, io_open_t *msg,
iofunc_attr_t *attr, void *extra) {
if (msg->connect.path[0] == 0) {
// Каталог (/dev/atoz)
return (iofunc_open_default(ctp, msg, attr, extra));
} else if (msg->connect.path[1] == 0 &&
(msg->connect.path[0] >= 'a' &&
msg->connect.path[0] <= 'z')) { // Файл (/dev/atoz/[a-z])
return
(iofunc_open_default(ctp, msg, atoz_attrs +
msg->connect.path[0] - 'a', extra));
} else {
return (ENOENT);
}
}
В функции my_read(), чтобы решить, какие операции надо выполнить, мы анализируем поле mode атрибутной записи. Если макрос S_ISDIR() говорит, что это каталог, мы вызываем функцию my_read_dir(); если макрос S_ISREG() говорит, что это файл, мы вызываем функцию my_read_file(). (Отметим, что если мы не можем разобрать, что это такое, мы возвращаем клиенту EBADF, указывая ему этим, что что-то здесь не так.)
Приведенный ниже код ничего не знает о наших специальных устройствах, да ему, собственно, и дела до них нет — он просто принимает решение на основе стандартных общеизвестных данных.
static int my_read(resmgr_context_t *ctp, io_read_t *msg,
iofunc_ocb_t *ocb) {
int sts;
// Использовать вспомогательную функцию для проверки
// корректности
if ((sts =
iofunc_read_verify(ctp, msg, ocb, NULL)) != EOK) {
return (sts);
}
// Решить, надо ли читать «файл» или «каталог»
if (S_ISDIR(ocb->attr->mode)) {
return (my_read_dir(ctp, msg, ocb));
} else if (S_ISREG(ocb->attr->mode)) {
return (my_read_file(ctp, msg, ocb));
} else {
return (EBADF);