openwrt-mirror/package/utils/ucode/patches/121-fs-add-read_nb-method-for-non-blocking-reads.patch
Hauke Mehrtens 985394bf26 ucode: update to Git HEAD (2025-11-07)
e8a7290e55c0 socket: fix `recv()` incorrectly reporting unrelated errors
ddde611fb9d4 socket: fix convertion of hw addresses to ucode strings
924ccc95be32 vm: make sure uc_vm_insn_to_name() always returns a value
754590d26f23 lexer: fix parsing \xHH and \0OOO escape sequences
623f550e579a fs: add dup2() function
6c9385a99edd fs: add mkdtemp() method for creating temporary directories
ea579046a619 fs: reset errno to zero in get_fd()

The removed patches are integrated upstream.

Fixes: https://github.com/jow-/ucode/issues/332
Fixes: https://github.com/jow-/ucode/issues/337
Link: https://github.com/openwrt/openwrt/pull/20718
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2025-11-11 21:27:39 +01:00

138 lines
3.1 KiB
Diff

From: Felix Fietkau <nbd@nbd.name>
Date: Wed, 8 Oct 2025 23:03:05 +0200
Subject: [PATCH] fs: add read_nb() method for non-blocking reads
Add file handle method for reading from non-blocking file descriptors.
Designed for use with uloop, bypasses stdio buffering, handles EAGAIN/EINTR.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/lib/fs.c
+++ b/lib/fs.c
@@ -675,6 +675,116 @@ uc_fs_read(uc_vm_t *vm, size_t nargs)
}
/**
+ * Reads data from a non-blocking file descriptor.
+ *
+ * This function is designed for use with uloop file descriptor monitoring.
+ * When called from within a uloop handle callback (after ULOOP_READ event),
+ * it reads available data from the non-blocking file descriptor.
+ *
+ * Performs a single read() operation directly on the file descriptor,
+ * bypassing stdio buffering. Properly handles EAGAIN and EINTR errors.
+ *
+ * Returns a string containing the data read, up to the specified limit.
+ *
+ * Returns an empty string if no data is available (EAGAIN/EWOULDBLOCK).
+ *
+ * Returns `null` if an error occurred.
+ *
+ * @function module:fs.file#read_nb
+ *
+ * @param {number} [limit=4096]
+ * Maximum number of bytes to read. Defaults to 4096 if not specified.
+ *
+ * @returns {?string}
+ *
+ * @example
+ * import * as uloop from 'uloop';
+ * import { fdopen } from 'fs';
+ *
+ * uloop.init();
+ *
+ * let sock = connect_socket(...);
+ * let fp = fdopen(sock, "r");
+ *
+ * uloop.handle(fp, (events) => {
+ * if (events & uloop.ULOOP_READ) {
+ * let data = fp.read_nb();
+ * if (data === null) {
+ * print("Error reading\n");
+ * } else if (length(data) > 0) {
+ * print("Received: ", data, "\n");
+ * }
+ * }
+ * }, uloop.ULOOP_READ);
+ *
+ * uloop.run();
+ */
+static uc_value_t *
+uc_fs_read_nb(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *limit_val = uc_fn_arg(0);
+ FILE **fp = uc_fn_this("fs.file");
+ char *buf = NULL;
+ ssize_t n_read;
+ uc_value_t *rv;
+ size_t limit = 4096;
+ int fd;
+
+ if (!fp || !*fp)
+ err_return(EBADF);
+
+ if (limit_val) {
+ int64_t limit_arg;
+
+ if (ucv_type(limit_val) != UC_INTEGER)
+ err_return(EINVAL);
+
+ limit_arg = ucv_int64_get(limit_val);
+
+ if (limit_arg <= 0)
+ return NULL;
+
+ limit = (size_t)limit_arg;
+ }
+
+ fd = fileno(*fp);
+
+ if (fd == -1)
+ err_return(errno);
+
+ buf = malloc(limit);
+
+ if (!buf)
+ err_return(ENOMEM);
+
+ while (true) {
+ n_read = read(fd, buf, limit);
+
+ if (n_read >= 0)
+ break;
+
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ free(buf);
+ return ucv_string_new_length("", 0);
+ }
+
+ free(buf);
+ err_return(errno);
+ }
+
+ if (!n_read)
+ return NULL;
+
+ rv = ucv_string_new_length(buf, (size_t)n_read);
+ free(buf);
+
+ return rv;
+}
+
+/**
* Writes a chunk of data to the file handle.
*
* In case the given data is not a string, it is converted to a string before
@@ -2991,6 +3101,7 @@ static const uc_function_list_t proc_fns
static const uc_function_list_t file_fns[] = {
{ "read", uc_fs_read },
+ { "read_nb", uc_fs_read_nb },
{ "write", uc_fs_write },
{ "seek", uc_fs_seek },
{ "tell", uc_fs_tell },