summaryrefslogtreecommitdiffhomepage
path: root/lib/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/fs.c')
-rw-r--r--lib/fs.c95
1 files changed, 48 insertions, 47 deletions
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