Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs: improve ioctl implementation #235

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 70 additions & 48 deletions lib/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,17 @@
#include <fcntl.h>

#if defined(__linux__)
#define HAS_IOCTL
#endif

#ifdef HAS_IOCTL
#include <sys/ioctl.h>

#define IOC_DIR_NONE (_IOC_NONE)
#define IOC_DIR_READ (_IOC_READ)
#define IOC_DIR_WRITE (_IOC_WRITE)
#define IOC_DIR_RW (_IOC_READ | _IOC_WRITE)

#endif

#include "ucode/module.h"
Expand Down Expand Up @@ -937,50 +947,50 @@ uc_fs_fileno(uc_vm_t *vm, size_t nargs)
return uc_fs_fileno_common(vm, nargs, "fs.file");
}

#if defined(__linux__)
#ifdef HAS_IOCTL

/**
* Performs an ioctl operation on the file.
*
*
* The direction parameter specifies who is reading and writing,
* from the user's point of view. It can be one of the following values:
*
* | Direction | Description |
* |-----------|-----------------------------------------------------------------------------------------|
* | 0 | NONE - neither userspace nor kernel is writing, ioctl is executed without passing data. |
* | 1 | WRITE - userspace is writing and kernel is reading. |
* | 2 | READ - kernel is writing and userspace is reading. |
* | 3 | READ+WRITE - userspace is writing and kernel is writing back into the data structure. |
*
*
* | Direction | Description |
* |----------------|-----------------------------------------------------------------------------------|
* | IOC_DIR_NONE | neither userspace nor kernel is writing, ioctl is executed without passing data. |
* | IOC_DIR_WRITE | userspace is writing and kernel is reading. |
* | IOC_DIR_READ | kernel is writing and userspace is reading. |
* | IOC_DIR_RW | userspace is writing and kernel is writing back into the data structure. |
*
* The size parameter has a different purpose depending on the direction parameter:
* - direction = 0 -> the size parameter is not used
* - direction = 1 -> size must be the length (in bytes) of argp
* - direction = 2 -> expected length (in bytes) of the data returned by kernel
* - direction = 3 -> size is the length (in bytes) of argp, and the length of the data returned by kernel.
*
* The argp parameter should be the data to be written for direction '1' and '3', otherwise null.
*
* Returns the result of the ioctl operation; for direction '2' and '3' this is a string containing
* - IOC_DIR_NONE -> the size parameter is not used
* - IOC_DIR_WRITE -> size must be the length (in bytes) of argp
* - IOC_DIR_READ -> expected length (in bytes) of the data returned by kernel
* - IOC_DIR_RW -> size is the length (in bytes) of argp, and the length of the data returned by kernel.
*
* The argp parameter should be the data to be written for IOC_DIR_WRITE and IOC_DIR_RW, otherwise null.
*
* Returns the result of the ioctl operation; for IOC_DIR_READ and IOC_DIR_RW this is a string containing
* the data, otherwise a number as return code.
* In case of an error, null is returned and the error code is available via last_error.
*
*
* @function module:fs.file#ioctl
*
*
* @param {number} direction
* The direction of the ioctl operation.
*
* The direction of the ioctl operation. Use constants IOC_DIR_*.
*
* @param {number} type
* ioctl type (see https://www.kernel.org/doc/html/latest/userspace-api/ioctl/ioctl-number.html)
*
*
* @param {number} num
* ioctl sequence number.
*
*
* @param {number} size
* The size of the ioctl operation payload.
*
*
* @param {?string} payload
* The ioctl payload.
*
*
* @returns {?number|?string}
*/
static uc_value_t *
Expand All @@ -992,12 +1002,12 @@ uc_fs_ioctl(uc_vm_t *vm, size_t nargs)
uc_value_t *num = uc_fn_arg(2);
uc_value_t *size = uc_fn_arg(3);
uc_value_t *payload = uc_fn_arg(4);
uc_string_t *mem = NULL;
char *buf = NULL;
unsigned long req = 0;
unsigned int dir, ty, nr;
size_t sz;
int fd, ret;
bool freebuf = false;

if (!fp)
err_return(EBADF);
Expand All @@ -1016,48 +1026,52 @@ uc_fs_ioctl(uc_vm_t *vm, size_t nargs)
nr = ucv_uint64_get(num);

switch (dir) {
case 0: /* ioctl w/o read and write */
req = _IOC(_IOC_NONE, ty, nr, 0);
case IOC_DIR_NONE:
sz = 0;
break;
case 1: /* ioctl write */
case IOC_DIR_WRITE:
if (ucv_type(payload) != UC_STRING)
err_return(EINVAL);

req = _IOC(_IOC_WRITE, ty, nr, sz);
buf = ucv_string_get(payload);
break;
case 2: /* ioctl read */
req = _IOC(_IOC_READ, ty, nr, sz);
buf = xalloc(sz);
if (!buf)
case IOC_DIR_READ:
mem = xalloc(sizeof(uc_string_t) + sz + 1);
if (!mem)
err_return(ENOMEM);

freebuf = true;
mem->header.type = UC_STRING;
mem->header.refcount = 1;
mem->length = sz;
buf = mem->str;

break;
case 3: /* ioctl read+write */
req = _IOC((_IOC_READ|_IOC_WRITE), ty, nr, sz);
buf = ucv_string_get(payload);
case IOC_DIR_RW:
if (ucv_type(payload) != UC_STRING)
err_return(EINVAL);

mem = (uc_string_t *)payload;
buf = mem->str;
sz = mem->length;

break;
default: err_return(EINVAL);
}

req = _IOC(dir, ty, nr, sz);
ret = ioctl(fd, req, buf);
if (ret < 0) {
if (freebuf)
free(buf);
if (dir == IOC_DIR_READ)
free(mem);

err_return(errno);
}

if (dir >= 2) {
payload = ucv_string_new_length(buf, sz);
if (freebuf)
free(buf);
if (mem) {
return &mem->header;
} else {
payload = ucv_uint64_new(ret);
return ucv_uint64_new(ret);
}

return payload;
}

#endif
Expand Down Expand Up @@ -2880,4 +2894,12 @@ void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
ucv_object_add(scope, "stdin", uc_resource_new(file_type, stdin));
ucv_object_add(scope, "stdout", uc_resource_new(file_type, stdout));
ucv_object_add(scope, "stderr", uc_resource_new(file_type, stderr));

#ifdef HAS_IOCTL
#define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x))
ADD_CONST(IOC_DIR_NONE);
ADD_CONST(IOC_DIR_READ);
ADD_CONST(IOC_DIR_WRITE);
ADD_CONST(IOC_DIR_RW);
#endif
}
Loading