diff options
author | Jonas Jelonek <jelonek.jonas@gmail.com> | 2024-08-16 11:36:53 +0000 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2024-09-20 09:44:34 +0200 |
commit | 56ebeeca5349773db0ff8104dbf2f99d10d13f16 (patch) | |
tree | cb654ddda28e50ffb1f6e069da59ed4c9626bcd9 | |
parent | 6ea37c8fa4c4780ff99441ac787a2c9cd38c7bc6 (diff) |
fs: add ioctl() file method
implements ioctl() for a given file handle on Linux.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-rw-r--r-- | lib/fs.c | 132 | ||||
-rw-r--r-- | tests/custom/03_stdlib/40_proto | 1 |
2 files changed, 133 insertions, 0 deletions
@@ -61,6 +61,10 @@ #include <limits.h> #include <fcntl.h> +#if defined(__linux__) +#include <sys/ioctl.h> +#endif + #include "ucode/module.h" #include "ucode/platform.h" @@ -929,6 +933,131 @@ uc_fs_fileno(uc_vm_t *vm, size_t nargs) return uc_fs_fileno_common(vm, nargs, "fs.file"); } +#if defined(__linux__) + +/** + * 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. | + * + * 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 + * 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. + * + * @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 * +uc_fs_ioctl(uc_vm_t *vm, size_t nargs) +{ + FILE *fp = uc_fn_thisval("fs.file"); + uc_value_t *direction = uc_fn_arg(0); + uc_value_t *type = uc_fn_arg(1); + uc_value_t *num = uc_fn_arg(2); + uc_value_t *size = uc_fn_arg(3); + uc_value_t *payload = uc_fn_arg(4); + 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); + + fd = fileno(fp); + if (fd == -1) + err_return(EBADF); + + if (ucv_type(direction) != UC_INTEGER || ucv_type(type) != UC_INTEGER || + ucv_type(num) != UC_INTEGER || ucv_type(size) != UC_INTEGER) + err_return(EINVAL); + + dir = ucv_uint64_get(direction); + sz = ucv_uint64_get(size); + ty = ucv_uint64_get(type); + nr = ucv_uint64_get(num); + + switch (dir) { + case 0: /* ioctl w/o read and write */ + req = _IOC(_IOC_NONE, ty, nr, 0); + break; + case 1: /* ioctl 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) + err_return(ENOMEM); + + freebuf = true; + break; + case 3: /* ioctl read+write */ + req = _IOC((_IOC_READ|_IOC_WRITE), ty, nr, sz); + buf = ucv_string_get(payload); + break; + default: err_return(EINVAL); + } + + ret = ioctl(fd, req, buf); + if (ret < 0) { + if (freebuf) + free(buf); + + err_return(errno); + } + + if (dir >= 2) { + payload = ucv_string_new_length(buf, sz); + if (freebuf) + free(buf); + } else { + payload = ucv_uint64_new(ret); + } + + return payload; +} + +#endif + /** * Opens a file. * @@ -2665,6 +2794,9 @@ static const uc_function_list_t file_fns[] = { { "isatty", uc_fs_isatty }, { "truncate", uc_fs_truncate }, { "lock", uc_fs_lock }, +#if defined(__linux__) + { "ioctl", uc_fs_ioctl }, +#endif }; static const uc_function_list_t dir_fns[] = { diff --git a/tests/custom/03_stdlib/40_proto b/tests/custom/03_stdlib/40_proto index d96d124..0f65910 100644 --- a/tests/custom/03_stdlib/40_proto +++ b/tests/custom/03_stdlib/40_proto @@ -38,6 +38,7 @@ When invoked with two arguments, returns the given value. Hello, World! [ { + "ioctl": "function ioctl(...) { [native code] }", "lock": "function lock(...) { [native code] }", "truncate": "function truncate(...) { [native code] }", "isatty": "function isatty(...) { [native code] }", |