|
From: Stefan S. <dev...@gm...> - 2012-09-25 07:29:46
|
Hi !
If a userspace program communicates with a driver through ioctls, there
are various ways to exchange information.
For restricted mode ioctls, all necessary information is encoded in the
ioctl number.
This can be seen from the _IOC() macro defined in <asm-generic/ioctl.h>:
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
Thus, if a buffer should be read from the the driver, the ioctl
direction will be _IOC_READ and the size of the buffer is encoded in the
ioctl number as well.
Vice versa, for a write operation the direction would be _IOC_WRITE.
Both of these operations are handled by CUSE:
void (*ioctl) (fuse_req_t req, int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags,
const void *in_buf, size_t in_bufsz, size_t out_bufsz);
For read operations, the data can be returned to the kernel (and thus to
the program doing the ioctl operation on the CUSE device) through one of
the fuse_reply_*() functions where up to out_bufsz bytes can be replied.
For write operations, the data that should be written is availble in
*in_buf with size in_bufsz.
However, there are also ioctls with the direction set to _IOC_NONE.
In this case the argument (void *arg) may have multiple purposes:
1. it is ignored
2. it is a constant
3. it is a pointer to a variable that is read from
4. it is a pointer to a variable that should be written to
Which of the above cases applies is subject to the driver implementation.
Now the burning question is how these kinds of ioctls can be handled
with CUSE.
Case 1 and 2 are not an issue of course, but what about case 3 and 4 ?
Is this supported in CUSE ?
Since no information is encoded in the ioctl number, neither the kernel
nor the CUSE code would know which of the above cases applies.
A possible solution might be best effort based.
That is, the CUSE code in the kernel would always try to interpret the
content of *arg as a pointer.
To avoid invalid memory accesses, it would have to look up the mapped
memory regions of the calling process and if the potential address is
both in a mapped region and aligned properly, the kernel would fetch
the content from the given address and pass it on to the callback function.
Regards,
Stefan
|
|
From: Stefan S. <dev...@gm...> - 2012-09-25 14:20:35
|
Hi ! I looked at some more code and discovered, that there is the ioctl *unrestricted* mode that can be used for this. I still have to write some test code to try everything out, but is it possible to use restricted *and* unrestricted mode in CUSE code ? The idea would be that usually, restricted and "well formed" ioctl requests are handled. In that case, either the read or write direction is encoded in the ioctl command. However, in case no direction is set, I would like to switch to unrestricted mode. Maybe it also works in unrestricted mode ? In that case I could decode the direction and size information by myself based on the ioctl cmd. If the read/write direction is set and the size is encoded accordingly, I could request reads/writes with fuse_reply_ioctl_retry(). And if no direction is set, I can do the same, right ? Regards, Stefan On 09/25/2012 09:29 AM, Stefan Schönleitner wrote: > Hi ! > > If a userspace program communicates with a driver through ioctls, there > are various ways to exchange information. > For restricted mode ioctls, all necessary information is encoded in the > ioctl number. > This can be seen from the _IOC() macro defined in <asm-generic/ioctl.h>: > > #define _IOC(dir,type,nr,size) \ > (((dir) << _IOC_DIRSHIFT) | \ > ((type) << _IOC_TYPESHIFT) | \ > ((nr) << _IOC_NRSHIFT) | \ > ((size) << _IOC_SIZESHIFT)) > > Thus, if a buffer should be read from the the driver, the ioctl > direction will be _IOC_READ and the size of the buffer is encoded in the > ioctl number as well. > Vice versa, for a write operation the direction would be _IOC_WRITE. > Both of these operations are handled by CUSE: > > void (*ioctl) (fuse_req_t req, int cmd, void *arg, > struct fuse_file_info *fi, unsigned int flags, > const void *in_buf, size_t in_bufsz, size_t out_bufsz); > > For read operations, the data can be returned to the kernel (and thus to > the program doing the ioctl operation on the CUSE device) through one of > the fuse_reply_*() functions where up to out_bufsz bytes can be replied. > For write operations, the data that should be written is availble in > *in_buf with size in_bufsz. > > However, there are also ioctls with the direction set to _IOC_NONE. > In this case the argument (void *arg) may have multiple purposes: > > 1. it is ignored > 2. it is a constant > 3. it is a pointer to a variable that is read from > 4. it is a pointer to a variable that should be written to > > Which of the above cases applies is subject to the driver implementation. > > Now the burning question is how these kinds of ioctls can be handled > with CUSE. > Case 1 and 2 are not an issue of course, but what about case 3 and 4 ? > Is this supported in CUSE ? > > Since no information is encoded in the ioctl number, neither the kernel > nor the CUSE code would know which of the above cases applies. > A possible solution might be best effort based. > That is, the CUSE code in the kernel would always try to interpret the > content of *arg as a pointer. > To avoid invalid memory accesses, it would have to look up the mapped > memory regions of the calling process and if the potential address is > both in a mapped region and aligned properly, the kernel would fetch > the content from the given address and pass it on to the callback function. > > > Regards, > Stefan > |
|
From: Miklos S. <mi...@sz...> - 2012-10-16 15:01:37
|
Stefan Schönleitner <dev...@gm...> writes: > Hi ! > > I looked at some more code and discovered, that there is the ioctl > *unrestricted* mode that can be used for this. > > I still have to write some test code to try everything out, but is it > possible to use restricted *and* unrestricted mode in CUSE code ? No, there's no possibility to use both modes at the same time. If you are emulating an existing char device which uses weird ioctls then the only way to do that is using unrestricted mode. If you are making up new ioctls than you should always use well formed ioctls and go with restricted mode. Please never ever create new ioctls that are not well formed, and before creating a new ioctl you should consider using plain read/write or getxattr/setxattr to achieve the same functionality. Thanks, Miklos |