From 18a2ffaf51fa2e0ead83fae62af87f0d7fc57a7a Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 11 Oct 2024 10:36:26 +0200 Subject: fs: ioctl: export constants for direction values Exports IOC_DIR_* constants to use for the direction parameter instead of plain integer values. The constants are based on the target's _IOC_* definitions. Signed-off-by: Jonas Jelonek --- lib/fs.c | 88 ++++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 35 deletions(-) (limited to 'lib') diff --git a/lib/fs.c b/lib/fs.c index 3572ff2..7cd5f8b 100644 --- a/lib/fs.c +++ b/lib/fs.c @@ -62,7 +62,17 @@ #include #if defined(__linux__) +#define HAS_IOCTL +#endif + +#ifdef HAS_IOCTL #include + +#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" @@ -935,50 +945,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 * @@ -1014,26 +1024,26 @@ 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: + req = _IOC(IOC_DIR_NONE, ty, nr, 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); + req = _IOC(IOC_DIR_WRITE, ty, nr, sz); buf = ucv_string_get(payload); break; - case 2: /* ioctl read */ - req = _IOC(_IOC_READ, ty, nr, sz); + case IOC_DIR_READ: + req = _IOC(IOC_DIR_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); + case IOC_DIR_RW: + req = _IOC(IOC_DIR_RW, ty, nr, sz); buf = ucv_string_get(payload); break; default: err_return(EINVAL); @@ -1047,7 +1057,7 @@ uc_fs_ioctl(uc_vm_t *vm, size_t nargs) err_return(errno); } - if (dir >= 2) { + if (dir == IOC_DIR_READ || dir == IOC_DIR_RW) { payload = ucv_string_new_length(buf, sz); if (freebuf) free(buf); @@ -2879,4 +2889,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 } -- cgit v1.2.3 From 0ba75bea925ddb9e79240f0831f3674d7cc6f7a9 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 11 Oct 2024 13:21:29 +0200 Subject: fs: ioctl: improve ioctl read to avoid allocating twice Allocate memory for 'uc_string_t' with the proper size upfront and use it's internal buffer as ioctl buffer. This avoids redundant 'ucv_string_new_length' calls and thus allocating memory a second time. Also simplify the signature of the function, using a single parameter for handling size and input buffer values. Signed-off-by: Jonas Jelonek [simplify arguments, adjust documentation, prevent in-place string modifications] Signed-off-by: Jo-Philipp Wich --- lib/fs.c | 95 ++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 48 insertions(+), 47 deletions(-) (limited to 'lib') diff --git a/lib/fs.c b/lib/fs.c index 7cd5f8b..3b9f731 100644 --- a/lib/fs.c +++ b/lib/fs.c @@ -960,17 +960,12 @@ uc_fs_fileno(uc_vm_t *vm, size_t nargs) * | 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: - * - 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. + * 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. * - * 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. + * In case of an error, null is returned and error details are available via + * {@link module:fs#error|error()}. * * @function module:fs.file#ioctl * @@ -978,16 +973,17 @@ uc_fs_fileno(uc_vm_t *vm, size_t nargs) * 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) + * The 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. + * The ioctl sequence number. * - * @param {?string} payload - * The ioctl payload. + * @param {number|string} [value] + * The value to pass to the ioctl system call. For `IOC_DIR_NONE`, this argument + * is ignored. With `IOC_DIR_READ`, the value should be a positive integer + * specifying the number of bytes to expect from the kernel. For the other + * directions, `IOC_DIR_WRITE` and `IOC_DIR_RW`, that value parameter must be a + * string, serving as buffer for the data to send. * * @returns {?number|?string} */ @@ -998,14 +994,13 @@ uc_fs_ioctl(uc_vm_t *vm, size_t nargs) 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); + uc_value_t *value = uc_fn_arg(3); + uc_value_t *mem = NULL; char *buf = NULL; unsigned long req = 0; unsigned int dir, ty, nr; - size_t sz; + size_t sz = 0; int fd, ret; - bool freebuf = false; if (!fp) err_return(EBADF); @@ -1015,57 +1010,63 @@ uc_fs_ioctl(uc_vm_t *vm, size_t nargs) err_return(EBADF); if (ucv_type(direction) != UC_INTEGER || ucv_type(type) != UC_INTEGER || - ucv_type(num) != UC_INTEGER || ucv_type(size) != UC_INTEGER) + ucv_type(num) != 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 IOC_DIR_NONE: - req = _IOC(IOC_DIR_NONE, ty, nr, 0); break; + case IOC_DIR_WRITE: - if (ucv_type(payload) != UC_STRING) + if (ucv_type(value) != UC_STRING) err_return(EINVAL); - req = _IOC(IOC_DIR_WRITE, ty, nr, sz); - buf = ucv_string_get(payload); + sz = ucv_string_length(value); + buf = ucv_string_get(value); break; + case IOC_DIR_READ: - req = _IOC(IOC_DIR_READ, ty, nr, sz); - buf = xalloc(sz); - if (!buf) - err_return(ENOMEM); + if (ucv_type(value) != UC_INTEGER) + err_return(EINVAL); - freebuf = true; + sz = ucv_to_unsigned(value); + + if (errno != 0) + err_return(errno); + + mem = xalloc(sizeof(uc_string_t) + sz + 1); + mem->type = UC_STRING; + mem->refcount = 1; + buf = ucv_string_get(mem); + ((uc_string_t *)mem)->length = sz; break; + case IOC_DIR_RW: - req = _IOC(IOC_DIR_RW, ty, nr, sz); - buf = ucv_string_get(payload); + if (ucv_type(value) != UC_STRING) + err_return(EINVAL); + + sz = ucv_string_length(value); + mem = ucv_string_new_length(ucv_string_get(value), sz); + buf = ucv_string_get(mem); break; - default: err_return(EINVAL); + + default: + err_return(EINVAL); } + req = _IOC(dir, ty, nr, sz); ret = ioctl(fd, req, buf); - if (ret < 0) { - if (freebuf) - free(buf); + if (ret < 0) { + ucv_put(mem); err_return(errno); } - if (dir == IOC_DIR_READ || dir == IOC_DIR_RW) { - payload = ucv_string_new_length(buf, sz); - if (freebuf) - free(buf); - } else { - payload = ucv_uint64_new(ret); - } - - return payload; + return mem ? mem : ucv_uint64_new(ret); } #endif -- cgit v1.2.3