Writing a Resource Manager -- Chapter 3:Fleshing Out the Skeleton

Chapter 3:Fleshing Out the Skeleton

现在是时候开始为资源管理器的基本骨骼添加一些内容了。我们将查看您可能必须处理的消息类型,如何设置资源管理器的属性,如何添加功能以及您应该考虑的一些安全问题。

Message types

正如我们在Bones of a Resource Manager中看到的那样,您的资源管理器可能需要处理这些类型的消息:

  • 连接消息

  • I/O消息

我们将在后面的章节和章节中对它们进行检查。

QNX Neutrino入门指南包含这些消息处理程序的摘要;请参阅“资源管理器”一章中的“Alphabetical listing of connect and I/O functions”。

Connect messages

客户端发出连接消息以基于路径名执行操作。这可以是在客户端和资源管理器之间建立长期关系的消息(例如,open()),或者它可以是“一次性”事件(例如,rename())的消息。

当你调用resmgr_attach()时,你传递一个指向resmgr_connect_funcs_t结构的指针,该结构定义你的连接函数。此结构在中定义如下:

typedef struct _resmgr_connect_funcs {

unsigned nfuncs;

int (*open) (resmgr_context_t *ctp, io_open_t *msg,

RESMGR_HANDLE_T *handle, void *extra);

int (*unlink) (resmgr_context_t *ctp, io_unlink_t *msg,

RESMGR_HANDLE_T *handle, void *reserved);

int (*rename) (resmgr_context_t *ctp, io_rename_t *msg,

RESMGR_HANDLE_T *handle,

io_rename_extra_t *extra);

int (*mknod) (resmgr_context_t *ctp, io_mknod_t *msg,

RESMGR_HANDLE_T *handle, void *reserved);

int (*readlink) (resmgr_context_t *ctp, io_readlink_t *msg,

RESMGR_HANDLE_T *handle, void *reserved);

int (*link) (resmgr_context_t *ctp, io_link_t *msg,

RESMGR_HANDLE_T *handle,

io_link_extra_t *extra);

int (*unblock) (resmgr_context_t *ctp, io_pulse_t *msg,

RESMGR_HANDLE_T *handle, void *reserved);

int (*mount) (resmgr_context_t *ctp, io_mount_t *msg,

RESMGR_HANDLE_T *handle,

io_mount_extra_t *extra);

} resmgr_connect_funcs_t;

要初始化此结构,请调用iofunc_func_init()以使用指向默认处理程序的指针填充它,然后覆盖资源管理器需要专门处理的任何内容。

resmgr_attach()函数将指针复制到resmgr_connect_funcs_t和resmgr_io_funcs_t结构,而不是结构本身。您应该分配结构,将它们声明为静态,或使它们成为全局变量。如果您的资源管理器针对具有不同处理程序的多个设备,请创建定义处理程序的单独结构。

连接消息都有一个_IO_CONNECT类型;子类型进一步表明发生了什么。参数如下:

  • nfuncs

结构中的函数数量。这允许未来的扩展。

  • open

处理客户端调用open(),fopen(),sopen()等。消息子类型为_IO_CONNECT_COMBINE,_IO_CONNECT_COMBINE_CLOSE或_IO_CONNECT_OPEN。

有关io_open处理程序的更多信息,请参阅本章后面的“Ways of adding functionality to the resource manager”。

  • Unlike

处理客户端调用unlink()。消息子类型为_IO_CONNECT_UNLINK。

  • Rename

处理客户端调用以重命名()。消息子类型为_IO_CONNECT_RENAME。

  • Mknod

处理客户端调用mkdir(),mkfifo()和mknod()。消息子类型是_IO_CONNECT_MKNOD。

  • Readlink

处理客户端调用readlink()。消息子类型为_IO_CONNECT_READLINK。

  • Link

处理客户端调用link()。消息子类型为_IO_CONNECT_LINK。

  • Unblock

处理来自内核的请求,以在连接消息阶段解锁客户端。没有相应的消息;该调用由库合成。

有关io_unblock处理程序的更多信息,请参阅“解除阻止客​​户端和处理中断”一章中的“Handling client unblocking due to signals or timeouts”。

  • mount 

处理对mount()的客户端调用。消息子类型为_IO_CONNECT_MOUNT。

有关io_mount处理程序的更多信息,请参阅“处理其他消息”一章中的“Handling mount()

如果消息是与open()outcall对应的_IO_CONNECT消息(和变体),则需要为稍后将处理的其他I/O消息建立上下文。该上下文称为OCB(开放控制块);它包含连接消息和后续I/O消息之间所需的任何信息。

基本上,OCB是保存需要按开放存储的信息的好地方。一个例子是文件中的当前位置。每个打开的文件描述符都有自己的文件位置。OCB是按开放分配的。在打开处理期间,您将初始化文件位置;在读写处理过程中,您需要提升文件位置。有关详细信息,请参阅本指南的POSIX层数据结构一章中的The open control block (OCB) structure”部分。

I/O messages

I/O消息是依赖于客户端和资源管理器之间的现有绑定(例如,OCB)的消息。

例如,_IO_READ(来自客户端的read()函数)消息取决于客户端之前通过发出open()并获取文件描述符与资源管理器建立了关联(或上下文)。这个由 open() 调用创建的上下文然后用于处理后续的I/O消息,如_IO_READ。

这个设计有充分的理由。例如,为每个read()请求传递完整路径名是低效的。open() 处理程序还可以执行我们只想执行一次的任务(例如,权限检查),而不是每个I/O消息。此外,当read()从磁盘文件读取4096个字节时,可能还有另外20兆字节仍在等待读取。因此,read()函数需要有一些上下文信息告诉它它正在读取的文件中的位置,读取了多少等等。

resmgr_io_funcs_t 结构(您传递给resmgr_attach()以及连接函数)定义了调用I/O消息的函数。 resmgr_io_funcs_t结构在中定义,如下所示。

typedef struct _resmgr_io_funcs {

unsigned nfuncs;

int (*read) (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb);

int (*write) (resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb);

int (*close_ocb) (resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb);

int (*stat) (resmgr_context_t *ctp, io_stat_t *msg, RESMGR_OCB_T *ocb);

int (*notify) (resmgr_context_t *ctp, io_notify_t *msg, RESMGR_OCB_T *ocb);

int (*devctl) (resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb);

int (*unblock) (resmgr_context_t *ctp, io_pulse_t *msg, RESMGR_OCB_T *ocb);

int (*pathconf) (resmgr_context_t *ctp, io_pathconf_t *msg, RESMGR_OCB_T *ocb);

int (*lseek) (resmgr_context_t *ctp, io_lseek_t *msg, RESMGR_OCB_T *ocb);

int (*chmod) (resmgr_context_t *ctp, io_chmod_t *msg, RESMGR_OCB_T *ocb);

int (*chown) (resmgr_context_t *ctp, io_chown_t *msg, RESMGR_OCB_T *ocb);

int (*utime) (resmgr_context_t *ctp, io_utime_t *msg, RESMGR_OCB_T *ocb);

int (*openfd) (resmgr_context_t *ctp, io_openfd_t *msg, RESMGR_OCB_T *ocb);

int (*fdinfo) (resmgr_context_t *ctp, io_fdinfo_t *msg, RESMGR_OCB_T *ocb);

int (*lock) (resmgr_context_t *ctp, io_lock_t *msg, RESMGR_OCB_T *ocb);

int (*space) (resmgr_context_t *ctp, io_space_t *msg, RESMGR_OCB_T *ocb);

int (*shutdown) (resmgr_context_t *ctp, io_shutdown_t *msg, RESMGR_OCB_T *ocb);

int (*mmap) (resmgr_context_t *ctp, io_mmap_t *msg, RESMGR_OCB_T *ocb);

int (*msg) (resmgr_context_t *ctp, io_msg_t *msg, RESMGR_OCB_T *ocb);

int (*reserved) (resmgr_context_t *ctp, void *msg, RESMGR_OCB_T *ocb);

int (*dup) (resmgr_context_t *ctp, io_dup_t *msg, RESMGR_OCB_T *ocb);

int (*close_dup) (resmgr_context_t *ctp, io_close_t *msg, RESMGR_OCB_T *ocb);

int (*lock_ocb) (resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb);

int (*unlock_ocb) (resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb);

int (*sync) (resmgr_context_t *ctp, io_sync_t *msg, RESMGR_OCB_T *ocb);

int (*power) (resmgr_context_t *ctp, io_power_t *msg, RESMGR_OCB_T *ocb);

int (*acl) (resmgr_context_t *ctp, io_acl_t *msg, RESMGR_OCB_T *ocb);

int (*pause) (resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb);

int (*unpause) (resmgr_context_t *ctp, io_pulse_t *msg, RESMGR_OCB_T *ocb);

int (*read64) (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb);

int (*write64) (resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb);

int (*notify64) (resmgr_context_t *ctp, io_notify_t *msg, RESMGR_OCB_T *ocb);

int (*utime64) (resmgr_context_t *ctp, io_utime_t *msg, RESMGR_OCB_T *ocb);

} resmgr_io_funcs_t;

您可以使用与resmgr_connect_funcs_t结构相同的方式初始化此结构:调用iofunc_func_init()以使用指向默认处理程序的指针填充它,然后覆盖资源管理器需要专门处理的任何内容。此结构还以nfuncs成员开头,该成员指示结构中有多少个函数,以便将来扩展。

resmgr_attach()函数将指针复制到resmgr_connect_funcs_t和resmgr_io_funcs_t结构,而不是结构本身。您应该分配结构,将它们声明为静态,或使它们成为全局变量。如果您的资源管理器针对具有不同处理程序的多个设备,请创建定义处理程序的单独结构。

请注意,I/O函数都具有公共参数列表。第一个条目是资源管理器上下文结构,第二个是消息(其类型与正在处理的消息匹配并包含从客户端发送的参数),最后一个是OCB(包含我们处理客户端时绑定的内容) open()函数)。

您通常必须为以下条目提供处理程序:

Readread64

处理客户端调用read()和readdir()。消息类型是_IO_READ。有关io_read处理程序的更多信息,请参阅“Handling Read and Write Messages”一章中的“Handling the _IO_READ message”。

read64处理程序用于与旧服务器通信。消息类型为_IO_READ64,消息使用先前未使用的成员来指定高32位长度。

Writewrite64

处理客户端调用write(),fwrite()等。消息类型为_IO_WRITE。有关io_write处理程序的更多信息,请参阅“Handling Read and Write Messages”一章中的“Handling the _IO_WRITE message”。

write64处理程序用于与旧服务器通信。消息类型为_IO_WRITE64,消息使用先前未使用的成员来指定高32位长度。

Devctl

处理对devctl()和ioctl()的客户端调用。消息类型是_IO_DEVCTL。有关io_devctl处理程序的更多信息,请参阅“Handling Other Messages”一章中的“Handling devctl() messages”。

 

您通常使用以下默认条目:

close_ocb 

当特定OCB收到最后一个close()时,由库调用。您可以使用此处理程序清除与OCB关联的任何内容。

Stat

处理客户端调用stat(),lstat()和fstat()。消息类型是_IO_STAT。有关io_stat处理程序的更多信息,请参阅“Handling Other Messages”一章中的“Handling stat()

Nitify

处理客户端调用select()和ionotify()。消息类型为_IO_NOTIFY。有关io_notify处理程序的更多信息,请参阅“Handling Other Messages”一章中的“Handling ionotify() and select()”。

Unblock

在I / O消息阶段处理来自内核的请求以解除对客户端的阻止。没有与此相关的消息。有关io_unblock处理程序的更多信息,请参阅“Unblocking Clients and Handling Interrupts”一章中的“Handling client unblocking due to signals or timeouts”。

Pathconf

处理客户端对fpathconf()和pathconf()的调用。消息类型为_IO_PATHCONF。

lseek

处理客户端调用lseek(),fseek()和rewinddir()。消息类型为_IO_LSEEK。有关io_lseek处理程序的更多信息,请参阅“Handling Other Messages”一章中的“Handling lseek()

CHMOD

处理客户端调用chmod()和fchmod()。消息类型是_IO_CHMOD。
CHOWN

处理客户端调用chown()和fchown()。消息类型是_IO_CHOWN。
UTIME

处理客户端调用utime()。消息类型是_IO_UTIME。
openfd

处理对openfd()的客户端调用。消息类型是_IO_OPENFD。
fdinfo

处理对iofdinfo()的客户端调用。消息类型是_IO_FDINFO。
lock

处理客户端调用fcntl(),lockf()和flock()。消息类型是_IO_LOCK。
space

处理客户端对fcntl(),ftruncate()和ltrunc()的调用。消息类型为_IO_SPACE。
shutdown

保留供将来使用。
MMAP

处理客户端调用mmap(),munmap(),mmap_device_io()和mmap_device_memory()。消息类型为_IO_MMAP。

Msg

处理手动汇编并通过MsgSend()发送的消息。消息类型是_IO_MSG。有关io_msg处理程序的更多信息,请参阅“处理其他消息”一章中的“处理带外(_IO_MSG)消息”。

Reserved

保留供将来使用。
DUP

处理客户端调用dup(),dup2(),fcntl(),fork(),spawn *()和vfork()。消息类型是_IO_DUP。有关io_dup处理程序的更多信息,请参阅“处理其他消息”一章中的“处理open(),dup()和close()消息”。

Close_dup

处理客户端调用close()和fclose()。消息类型为_IO_CLOSE_DUP。

您几乎永远不会替换默认的close_dup处理程序,因为该库会跟踪多个open(),dup()和

Default message handling

由于资源管理器接收的大量消息处理一组公共属性,因此OS提供了一个iofunc_*()共享库,它允许资源管理器处理stat(),chmod(),chown()等函数, lseek()等自动执行,无需编写其他代码。作为一个额外的好处,这些iofunc_*()默认处理程序实现了消息的POSIX语义,从而卸载了一些工作。

该库包含这些客户端函数的iofunc _*()默认处理程序:

  • chmod()

  • chown()

  • close()

  • devctl()

  • fpathconf()

  • fseek()

  • fstat()

  • lockf()

  • lseek()

  • mmap()

  • open()

  • pathconf()

  • stat()

  • utime()

open(), dup(), and close()

资源管理器共享库自动处理dup()消息。

假设客户端程序执行最终最终执行的代码:

fd = open ("/dev/device", O_RDONLY);

...

fd2 = dup (fd);

...

fd3 = dup (fd);

...

close (fd3);

...

close (fd2);

...

close (fd);

客户端为第一个open()生成一个打开的连接消息,然后为两个dup()调用生成两个_IO_DUP消息。然后,当客户端执行close()调用时,它会生成三个关闭消息。

由于dup()函数生成文件描述符的副本,因此不应为每个文件描述符分配新的上下文信息。当关闭消息到达时,因为没有为每个dup()分配新的上下文,所以每个关闭消息都不会释放内存!(如果确实如此,第一次关闭就会消除上下文。)

资源管理器共享库提供默认处理程序,用于跟踪open(),dup()和close()消息,并仅针对上次关闭(即上例中的第三个io_close消息)执行工作。

Setting resource manager attributes

除了定义连接和I/O函数的结构之外,还将resmgr_attr_t结构传递给resmgr_attach()以指定资源管理器的属性。

resmgr_attr_t结构定义如下:

typedef struct _resmgr_attr {
unsigned flags;
unsigned nparts_max;
size_t msg_max_size;
int (*other_func)(resmgr_context_t *,
void *msg);
unsigned reserved[4];
} resmgr_attr_t;

成员包括:

Flags

允许您更改资源管理器界面的行为。将其设置为0,或以下位的组合(在中定义):

  • RESMGR_FLAG_ATTACH_LOCAL - 设置资源管理器,但不要使用procnto注册其路径。您可以将消息发送到资源管理器的通道(如果您知道在哪里找到它)。

  • RESMGR_FLAG_ATTACH_OTHERFUNC - 此结构的other_func成员指向未处理的I/O消息的函数。

  • RESMGR_FLAG_CROSS_ENDIAN - 服务器处理跨端支持。该框架处理服务器端的所有必要转换;客户不需要做任何事情。

如有必要,您的资源管理器可以通过检查是否在_msg_info结构的flags成员中设置_NTO_MI_ENDIAN_DIFF位来确定消息来自不同字节的客户端,该结构包含在传递给处理程序的resmgr_context_t结构中功能。

  • RESMGR_FLAG_NO_DEFAULT_FUNC - 未实现。

  • RESMGR_FLAG_RCM(QNX Neutrino 6.6或更高版本) - 在处理请求时自动采用客户端的资源约束模式。

还有一些_RESMGR_FLAG_*位(带有前导下划线),但您可以在resmgr_attach()的flags参数中使用它们。

nparts_max

应分配给IOV阵列的组件数。

msg_max_size 

消息缓冲区的大小。

当您开始编写自己的处理函数时,这些成员将非常重要。

如果为nparts_max指定值零,则资源管理器库会将该值压缩到库本身可用的最小值。你为什么要设置IOV数组的大小?正如我们将在“Handling Read and Write Messages”一章的“Getting the resource manager library to do the reply”部分中看到的那样,您可以告诉资源管理器库为我们做出回复。我们可能想给它一个IOV数组,指向包含回复数据的N个缓冲区。但是,既然我们要求library为我们做回复,我们需要使用它的IOV数组,当然需要大到足以指向我们的N个缓冲区。

other_func

允许您指定在资源管理器获取其不理解的I/O消息的情况下调用的例程。

一般情况下,我们不建议您使用此成员。对于私有或自定义消息,您应该使用_IO_DEVCTL或_IO_MSG处理程序,如处理其他消息章节中所述。如果要接收脉冲,请使用pulse_attach()。

要附加other_func,必须在此结构的flags成员中设置RESMGR_FLAG_ATTACH_OTHERFUNC位。

如果资源管理器库得到一条不知道如何处理的I/O消息,它将调用other_func成员指定的例程,如果非NULL。(如果它为NULL,资源管理器库将向客户端返回ENOSYS,有效地说明它不知道此消息的含义。)

如果您在客户端和资源管理器之间指定了某种形式的自定义消息传递,则可以为other_func指定非NULL值,尽管推荐的方法是devctl()函数调用(客户端)和_IO_DEVCTL消息handler(服务器)或MsgSend*()函数调用(客户端)和_IO_MSG消息处理程序(服务器)。

对于非I/O消息类型,您应该使用message_attach()函数,该函数附加调度句柄的消息范围。当收到类型在该范围内的消息时,dispatch_block()函数调用用户提供的函数,该函数负责执行任何特定工作,例如回复客户端。

Ways of adding functionality to the resource manager

您可以通过以下基本方式向正在编写的资源管理器添加功能:

  • 使用您自己封装的默认函数

  • 使用你自己的辅助函数

  • 自己编写整个函数

前两个几乎完全相同,因为默认函数本身并没有那么多 - 它们依赖于POSIX辅助函数。 第三种方法有优点和缺点。

Using the default functions

由于默认函数(例如,iofunc_open_default())可以直接安装在跳转表中,因此没有理由不能将它们嵌入到您自己的函数中。

这是一个如何使用您自己的io_open处理程序执行此操作的示例:

int main (int argc, char **argv)
{

/* install all of the default functions */
iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_funcs);
/* take over the open function */
connect_funcs.open = io_open;

}

 

int io_open (resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, void *extra)
{
return (iofunc_open_default (ctp, msg, handle, extra));
}

显然,这只是一个增量步骤,它允许您在消息从客户端到达时在您的io_open处理程序中获得控制权。 您可能希望在默认函数执行之前或之后执行某些操作:

/* example of doing something before */
extern int accepting_opens_now;
int io_open (resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, void *extra)
{
if (!accepting_opens_now) {
return (EBUSY);
}


/*

* at this point, we're okay to let the open happen,
* so let the default function do the "work".
*/
return (iofunc_open_default (ctp, msg, handle, extra));
}

要么:

/* example of doing something after */
int io_open (resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, void *extra)
{
int sts;
/*
* have the default function do the checking
* and the work for us
*/
sts = iofunc_open_default (ctp, msg, handle, extra);
/*
* if the default function says it's okay to let the open
* happen, we want to log the request
*/
if (sts == EOK) {
log_open_request (ctp, msg);
}
return (sts);
}

毫无疑问,您可以在标准默认POSIX处理程序之前和之后执行某些操作。
这种方法的主要优点是您可以轻松添加标准默认POSIX处理程序的功能。

Using the helper functions

默认函数使用辅助函数。这些函数不能直接放在连接或I / O跳转表中,但它们确实执行大部分工作。

这是iofunc_chmod_default()函数的源代码:

int iofunc_chmod_default (resmgr_context_t *ctp, io_chmod_t *msg, iofunc_ocb_t *ocb)
{
return (iofunc_chmod (ctp, msg, ocb, ocb -> attr));
}

注意iofunc_chmod()辅助函数如何为iofunc_chmod_default()默认处理程序执行所有工作。这是简单功能的典型特征。

iofunc_stat_default()默认处理程序是一个更有趣的案例:

int iofunc_stat_default(resmgr_context_t *ctp, io_stat_t *msg, iofunc_ocb_t *ocb) {
unsigned len;
/* Update stale time fields (ctime, mtime, atime) this OCB. */
(void)iofunc_time_update(ocb->attr);
unsigned format = msg->i.format;
int const status = iofunc_stat_format(ctp, ocb->attr, &format, &msg->o, &len);
if(status != EOK) {
return(status);
}
_RESMGR_STATUS(ctp, (long)format);
return _RESMGR_PTR(ctp, &msg->o, len);
}

此处理程序调用两个辅助例程。首先,它调用iofunc_time_update()以确保所有时间字段(atime,ctime和mtime)都是最新的。然后它调用iofunc_stat_format()来构建回复。然后,默认处理程序将状态(通过MsgReply返回)设置为状态信息的形式。最后,默认函数在ctp结构中构建一个指针并返回-1,以向资源管理器库指示它应该将一个部分从ctp-> iov结构返回给客户端。

最复杂的处理由iofunc_open_default()处理程序完成:

int iofunc_open_default (resmgr_context_t *ctp, io_open_t *msg, iofunc_attr_t *attr, void *extra)
{
int status;
iofunc_attr_lock (attr);
if ((status = iofunc_open (ctp, msg, attr, 0, 0)) != EOK) {
iofunc_attr_unlock (attr);
return (status);
}
if ((status = iofunc_ocb_attach (ctp, msg, 0, attr, 0)) != EOK) {
iofunc_attr_unlock (attr);
return (status);
}
iofunc_attr_unlock (attr);
return (EOK);
}

这个处理程序调用四个辅助函数:

  1. 它调用iofunc_attr_lock()来锁定属性结构,以便它具有对它的独占访问权(它将更新像计数器这样的东西,所以我们需要确保没有其他人同时这样做)。

  2. 然后它调用辅助函数iofunc_open(),它执行权限的实际验证。

  3. 接下来,它调用iofunc_ocb_attach()将OCB绑定到此请求,以便稍后它将自动传递给所有I/O函数。

  4. 最后,它调用iofunc_attr_unlock()来释放属性结构上的锁。

Writing the entire function yourself

有时,默认函数对您的特定资源管理器没有帮助。

例如,iofunc_read_default()和iofunc_write_default()函数实现/ dev / null; 他们完成了返回0字节(EOF)或吞下所有消息字节(分别)的所有工作。 您需要在这些处理程序中执行某些操作(除非您的资源管理器不支持_IO_READ或_IO_WRITE消息)。

请注意,即使在这种情况下,仍然可以使用辅助函数,例如iofunc_read_verify()和iofunc_write_verify()。

下面是典型文件系统的示例框架(伪代码),用于说明处理文件打开请求时需要采取的步骤:

if the open request is for a path (i.e., multiple

directory levels)

call iofunc_client_info_ext to get information

about client

for each directory component

call iofunc_check_access to check execute

permission for access

/*

recall that execute permission on a

directory is really the "search"

permission for that directory

*/

next

/*

at this point you have verified access

to the target

*/

endif

 

if O_CREAT is set and the file doesn't exist

call iofunc_open, passing the attribute of the

parent as dattr

if the iofunc_open succeeds,

do the work to create the new inode,

or whatever

endif

else

call iofunc_open, passing the attr of the file

and NULL for dattr

endif

 

/*

at this point, check for things like o_trunc,

etc. -- things that you have to do for the attr

*/

 

call iofunc_ocb_attach

return EOK

对于设备(即resmgr_attach()未指定受管资源是目录),以下步骤适用:

/*

at startup time (i.e., in the main() of the

resource manager)

*/

call iofunc_attr_init to initialize an attribute

structure

 

/* in the io_open message handler: */

call iofunc_open, passing in the attribute of the

device and NULL for dattr

 

call iofunc_ocb_attach

return EOK

资源管理器对open()请求的响应并不总是肯定或没有答案。 可以返回一条连接消息,指示服务器需要采取其他一些操作。 例如,如果在表示到某个其他路径的符号链接的路径上发生打开,则服务器可以使用_IO_SET_CONNECT_RET()宏和_IO_CONNECT_RET_LINK值进行响应。

例如,仅重定向路径名的开放处理程序可能类似于:

io_open(resmgr_context_t *ctp, io_open_t *msg,

iofunc_attr_t *dattr, void *extra) {

char *newpath;

 

/* Do all the error/access checking ... */

 

/* Lookup the redirected path and store

the new path in 'newpath' */

newpath = get_a_new_path(msg->connect.path);

 

_IO_SET_CONNECT_RET(ctp, _IO_CONNECT_RET_LINK);

len = strlen(newpath) + 1;

 

msg->link_reply.eflag = msg->connect.eflag;

msg->link_reply.nentries = 0;

msg->link_reply.path_len = len;

strcpy((char *)(msg->link_reply + 1), newpath);

 

len += sizeof(msg->link_reply);

 

return(_RESMGR_PTR(ctp, &msg->link_reply, len));

}

在此示例中,我们使用宏_IO_SET_CONNECT_RET()(在中定义)将ctp-> status字段设置为_IO_CONNECT_RET_LINK。 此值向资源管理器框架指示返回值实际上不是简单的返回代码,而是要处理的新请求。

此新请求的路径紧跟在link_reply结构之后,并且路径长度为path_len。 代码的最后几行只是填充了带有回复消息的IOV(以及要查询的新路径)并返回到资源管理器框架。

Security

资源管理器通常是一个特权进程,因此您应该小心不要让客户强迫它耗尽资源或破坏系统。
在设计资源管理器时,应考虑以下事项:

  • 资源管理器在路径名空间中的条目的权限

您将这些权限指定为iofunc_attr_init()的参数。通常,没有“正确”的权限集可供使用;您应该根据您希望其他进程和用户能够对您的资源管理器执行的操作来限制它们。

  • 以root身份运行

资源管理器通常需要由root启动才能连接到路径名空间,但最好使用procmgr_ability()来保留资源管理器所需的功能,然后以非root用户身份运行。有关更多信息,请参阅QNX Neutrino程序员指南中的“进程权限”。

  • 请求代表客户分配资源

如果资源管理器不是一个需要没有任何资源约束阈值的关键进程,它可以简单地在约束模式下运行(请参阅QNX Neutrino程序员指南中的“资源约束阈值”)。

如果资源管理器是一个关键进程,它应该保持PROCMGR_AID_RCONSTRAINT能力(请参阅procmgr_ability()),但是它需要确保受约束的客户端不使用它来分配超过当前定义的阈值的资源。除非资源管理器正在管理资源本身,否则合规性通常意味着在处理请求时采用客户端的约束模式,采用以下方式之一:

  • 自动,通过在resmgr_attr_t结构中设置RESMGR_FLAG_RCM(请参阅QNX Neutrino C库参考中的resmgr_attach()条目):

    resmgr_attr.flags |= RESMGR_FLAG_RCM;
    resmgr_attach(dpp, &resmgr_attr, name, _FTYPE_ANY, 0, &connect_funcs,
    &io_funcs, &io_attr))

  • 手动,通过检查_msg_info结构的flags成员中的_NTO_MI_CONSTRAINED,可以通过调用MsgReceive()或MsgInfo()获得。如果设置此位,则从受约束的客户端接收消息;当资源管理器代表这样的客户端分配资源时,它应该使用带有_NTO_TCTL_RCM_GET_AND_SET命令的ThreadCtl()来约束自己:

int value = 1; // 1 to constrain, 0 to remove constraint
ThreadCtl(_NTO_TCTL_RCM_GET_AND_SET, &value); /* swaps current state with value */
/* Handle the request... */
ThreadCtl(_NTO_TCTL_RCM_GET_AND_SET, &value); /* restores original state */

当资源管理器作为受约束进程运行或约束其中一个线程时,资源分配请求在仍有可用资源时失败。它应该像处理由资源完全耗尽而导致的故障一样处理这些故障,通常是通过向客户端返回错误。如果资源管理器可以继续处理消息,则应该这样做,以保证整体系统的稳定性。

Checking a client's abilities

您可以通过调用ConnectClientInfoAble()或iofunc_client_info_able()来确保客户端具有适当的功能。这两个都是一个能力列表的论据;如果客户端没有所有必需的能力,这些函数在_client_info结构的flags成员中设置_NTO_CI_UNABLE。

如果您调用了其中一个函数,则iofunc_check_access()会在设置了_NTO_CI_UNABLE时返回EACCES。

您的资源管理器可以通过调用procmgr_ability_create()来创建自定义功能;客户端可以通过调用procmgr_ability_lookup()获取它们的标识符,然后在切换到非root用户ID之前调用procmgr_ability()来保留它们。有关更多信息,请参阅QNX Neutrino程序员指南中的“创建功能”。当您检查客户端的能力时,您可以包括PROCMGR_AID_ *能力和自定义能力的组合。

资源管理器库创建以下自定义功能:Writing a Resource Manager -- Chapter 3:Fleshing Out the Skeleton_第1张图片

你可能感兴趣的:(QNX)