@scriptc/cli-darwin-x64 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/THIRD_PARTY_NOTICES +1 -1
- package/dist/bin/scriptc +0 -0
- package/dist/bin/scriptc.json +1 -1
- package/dist/lib/declarations/scriptc-node-fallback.d.ts +158 -31
- package/dist/lib/llvm/bin/scriptc-llvm-codegen +0 -0
- package/dist/lib/llvm/package.json +1 -1
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_assert.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_async.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_async_dyn.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_crypto_async.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_dyn_invoke.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_events.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_exception.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_file_handle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_node_builtin.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_qs.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_symbol.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_url.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_url_params.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_util_compare.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_util_style.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_assert.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_async.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_async_dyn.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_crypto_async.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_dyn_invoke.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_events.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_exception.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_file_handle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_island.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_node_builtin.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_qs.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_symbol.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_url.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_url_params.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_util_compare.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_util_style.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_web.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_assert.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_dyn_invoke.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_exception.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_node_builtin.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_symbol.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_url.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_url_params.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_assert.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_dyn_invoke.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_exception.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_node_builtin.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_symbol.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_url.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_url_params.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_assert.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_dyn_invoke.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_exception.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_node_builtin.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_symbol.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_url.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_url_params.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_assert.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_dyn_invoke.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_exception.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_node_builtin.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_symbol.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_url.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_url_params.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_assert.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_async.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_async_dyn.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_crypto_async.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_dyn_invoke.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_events.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_exception.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_file_handle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_node_builtin.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_qs.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_symbol.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_url.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_url_params.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_util_compare.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_util_style.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_assert.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_async.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_async_dyn.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_crypto_async.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_dyn_invoke.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_events.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_exception.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_file_handle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_island.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_node_builtin.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_qs.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_symbol.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_url.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_url_params.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_util_compare.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_util_style.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_web.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/package.json +1 -1
- package/dist/lib/runtime-darwin-x64/runtime-pack.json +423 -297
- package/dist/lib/runtime-sources/package.json +1 -1
- package/dist/lib/runtime-sources/src/scr_abort.c +5 -0
- package/dist/lib/runtime-sources/src/scr_array.c +5 -0
- package/dist/lib/runtime-sources/src/scr_assert.c +26 -0
- package/dist/lib/runtime-sources/src/scr_async.c +56 -4
- package/dist/lib/runtime-sources/src/scr_async_dyn.c +118 -24
- package/dist/lib/runtime-sources/src/scr_bytes.c +226 -0
- package/dist/lib/runtime-sources/src/scr_bytes_io.c +355 -51
- package/dist/lib/runtime-sources/src/scr_crypto_async.c +22 -2
- package/dist/lib/runtime-sources/src/scr_dyn_invoke.c +181 -43
- package/dist/lib/runtime-sources/src/scr_events.c +9 -0
- package/dist/lib/runtime-sources/src/scr_exception.c +1 -0
- package/dist/lib/runtime-sources/src/scr_fetch.c +45 -1
- package/dist/lib/runtime-sources/src/scr_file_handle.c +42 -0
- package/dist/lib/runtime-sources/src/scr_island.c +2166 -786
- package/dist/lib/runtime-sources/src/scr_island_manifest.h +19 -13
- package/dist/lib/runtime-sources/src/scr_json.c +584 -54
- package/dist/lib/runtime-sources/src/scr_json.test.ts +1 -0
- package/dist/lib/runtime-sources/src/scr_lib.c +1192 -43
- package/dist/lib/runtime-sources/src/scr_map.c +8 -0
- package/dist/lib/runtime-sources/src/scr_node_builtin.c +78 -0
- package/dist/lib/runtime-sources/src/scr_qs.c +10 -0
- package/dist/lib/runtime-sources/src/scr_regex.c +107 -5
- package/dist/lib/runtime-sources/src/scr_runtime.h +136 -9
- package/dist/lib/runtime-sources/src/scr_symbol.c +5 -0
- package/dist/lib/runtime-sources/src/scr_system_errors.h +573 -0
- package/dist/lib/runtime-sources/src/scr_text_decoder_labels.h +12 -12
- package/dist/lib/runtime-sources/src/scr_url.c +547 -64
- package/dist/lib/runtime-sources/src/scr_url_internal.h +6 -0
- package/dist/lib/runtime-sources/src/scr_url_params.c +10 -0
- package/dist/lib/runtime-sources/src/scr_util_compare.c +222 -0
- package/dist/lib/runtime-sources/src/scr_util_style.c +337 -0
- package/dist/lib/runtime-sources/src/scr_web.c +849 -264
- package/package.json +1 -1
|
@@ -27,6 +27,13 @@
|
|
|
27
27
|
#include <stdlib.h>
|
|
28
28
|
#include <string.h>
|
|
29
29
|
#include <sys/stat.h>
|
|
30
|
+
#if defined(__linux__)
|
|
31
|
+
#include <sys/vfs.h>
|
|
32
|
+
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
|
33
|
+
#include <sys/mount.h>
|
|
34
|
+
#elif !defined(_WIN32) && !defined(__wasi__)
|
|
35
|
+
#include <sys/statvfs.h>
|
|
36
|
+
#endif
|
|
30
37
|
#include <time.h>
|
|
31
38
|
|
|
32
39
|
#ifdef _WIN32
|
|
@@ -55,6 +62,8 @@
|
|
|
55
62
|
|
|
56
63
|
static WCHAR *scr_fs_win_wide(const ScrStr *path);
|
|
57
64
|
static int scr_fs_win_errno(DWORD error);
|
|
65
|
+
static bool scr_stats_is_link_tag(DWORD tag);
|
|
66
|
+
static bool scr_stats_link_target(HANDLE h, DWORD tag, double *size_out, ScrStr **text_out);
|
|
58
67
|
|
|
59
68
|
static ScrStr *scr_fs_win_utf8(const WCHAR *text, size_t length) {
|
|
60
69
|
if (length > INT_MAX) { SetLastError(ERROR_FILENAME_EXCED_RANGE); return NULL; }
|
|
@@ -729,6 +738,239 @@ void scr_env_unset(const ScrStr *name) {
|
|
|
729
738
|
#endif
|
|
730
739
|
}
|
|
731
740
|
|
|
741
|
+
static ScrStr *scr_fs_read_file_core(ScrStr *path, bool env_file);
|
|
742
|
+
|
|
743
|
+
/* Node's dotenv parser trims ASCII space/tab/newline and removes every CR,
|
|
744
|
+
* including CR inside quoted values. Quoted values can span lines; only
|
|
745
|
+
* double quotes expand backslash-n. Preserve the pinned parser's ordering
|
|
746
|
+
* of empty-value and export-prefix handling, including its unusual forms. */
|
|
747
|
+
static bool scr_env_space(char c) { return c == ' ' || c == '\t' || c == '\n'; }
|
|
748
|
+
|
|
749
|
+
static void scr_env_trim(const char *text, size_t *start, size_t *end) {
|
|
750
|
+
while (*start < *end && scr_env_space(text[*start])) (*start)++;
|
|
751
|
+
while (*end > *start && scr_env_space(text[*end - 1])) (*end)--;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
static void scr_env_store(ScrDyn *out, const char *text, size_t ks, size_t ke,
|
|
755
|
+
size_t vs, size_t ve, bool expand) {
|
|
756
|
+
ScrJsonBuf value;
|
|
757
|
+
scr_jb_init(&value);
|
|
758
|
+
for (size_t i = vs; i < ve; i++) {
|
|
759
|
+
if (expand && text[i] == '\\' && i + 1 < ve && text[i + 1] == 'n') {
|
|
760
|
+
scr_jb_putc(&value, '\n');
|
|
761
|
+
i++;
|
|
762
|
+
} else scr_jb_putc(&value, text[i]);
|
|
763
|
+
}
|
|
764
|
+
ScrStr *s = scr_jb_finish(&value);
|
|
765
|
+
scr_dyn_obj_set(out, text + ks, ke - ks, scr_dyn_new_str(s));
|
|
766
|
+
scr_str_release(s);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
static int scr_env_entry_order(const void *left, const void *right) {
|
|
770
|
+
const ScrDynEntry *a = *(const ScrDynEntry *const *)left;
|
|
771
|
+
const ScrDynEntry *b = *(const ScrDynEntry *const *)right;
|
|
772
|
+
size_t n = a->key_len < b->key_len ? a->key_len : b->key_len;
|
|
773
|
+
int cmp = memcmp(a->key, b->key, n);
|
|
774
|
+
return cmp ? cmp : (a->key_len > b->key_len) - (a->key_len < b->key_len);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
static ScrDyn *scr_env_parse_content(const ScrStr *input, bool ordinary) {
|
|
778
|
+
char *text = malloc(input->len + 1);
|
|
779
|
+
if (!text) scr_trap("scriptc: out of memory\n");
|
|
780
|
+
size_t end = 0;
|
|
781
|
+
for (size_t i = 0; i < input->len; i++) if (input->data[i] != '\r') text[end++] = input->data[i];
|
|
782
|
+
text[end] = '\0';
|
|
783
|
+
size_t pos = 0;
|
|
784
|
+
scr_env_trim(text, &pos, &end);
|
|
785
|
+
ScrDyn *entries = scr_dyn_new_obj_null_proto();
|
|
786
|
+
while (pos < end) {
|
|
787
|
+
if (text[pos] == '\n' || text[pos] == '#') {
|
|
788
|
+
while (pos < end && text[pos] != '\n') pos++;
|
|
789
|
+
if (pos < end) pos++;
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
size_t equal = pos;
|
|
793
|
+
while (equal < end && text[equal] != '=' && text[equal] != '\n') equal++;
|
|
794
|
+
if (equal == end) break;
|
|
795
|
+
if (text[equal] == '\n') {
|
|
796
|
+
pos = equal + 1;
|
|
797
|
+
scr_env_trim(text, &pos, &end);
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
size_t ks = pos, ke = equal;
|
|
801
|
+
scr_env_trim(text, &ks, &ke);
|
|
802
|
+
pos = equal + 1;
|
|
803
|
+
if (pos == end || text[pos] == '\n') {
|
|
804
|
+
scr_env_store(entries, text, ks, ke, pos, pos, false);
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
scr_env_trim(text, &pos, &end);
|
|
808
|
+
if (ks == ke) continue;
|
|
809
|
+
if (ke - ks >= 7 && memcmp(text + ks, "export ", 7) == 0) {
|
|
810
|
+
ks += 7;
|
|
811
|
+
scr_env_trim(text, &ks, &ke);
|
|
812
|
+
}
|
|
813
|
+
if (pos == end) {
|
|
814
|
+
scr_env_store(entries, text, ks, ke, pos, pos, false);
|
|
815
|
+
break;
|
|
816
|
+
}
|
|
817
|
+
char quote = text[pos];
|
|
818
|
+
if (quote == '\'' || quote == '"' || quote == '`') {
|
|
819
|
+
size_t closing = pos + 1;
|
|
820
|
+
while (closing < end && text[closing] != quote) closing++;
|
|
821
|
+
if (closing < end) {
|
|
822
|
+
scr_env_store(entries, text, ks, ke, pos + 1, closing, quote == '"');
|
|
823
|
+
pos = closing + 1;
|
|
824
|
+
while (pos < end && text[pos] != '\n') pos++;
|
|
825
|
+
if (pos < end) pos++;
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
size_t newline = pos;
|
|
829
|
+
while (newline < end && text[newline] != '\n') newline++;
|
|
830
|
+
scr_env_store(entries, text, ks, ke, pos, newline, false);
|
|
831
|
+
pos = newline < end ? newline + 1 : end;
|
|
832
|
+
} else {
|
|
833
|
+
size_t newline = pos;
|
|
834
|
+
while (newline < end && text[newline] != '\n') newline++;
|
|
835
|
+
size_t vs = pos, ve = pos;
|
|
836
|
+
while (ve < newline && text[ve] != '#') ve++;
|
|
837
|
+
scr_env_trim(text, &vs, &ve);
|
|
838
|
+
scr_env_store(entries, text, ks, ke, vs, ve, false);
|
|
839
|
+
pos = newline < end ? newline + 1 : end;
|
|
840
|
+
}
|
|
841
|
+
scr_env_trim(text, &pos, &end);
|
|
842
|
+
}
|
|
843
|
+
free(text);
|
|
844
|
+
/* Node's std::map orders UTF-8 keys before installing object properties.
|
|
845
|
+
* Ordinary objects ignore string-valued __proto__ assignments; loading
|
|
846
|
+
* the native environment retains that name as an ordinary variable. */
|
|
847
|
+
size_t count = entries->v.obj.len;
|
|
848
|
+
const ScrDynEntry **sorted = count ? malloc(count * sizeof(*sorted)) : NULL;
|
|
849
|
+
if (count && !sorted) scr_trap("scriptc: out of memory\n");
|
|
850
|
+
for (size_t i = 0; i < count; i++) sorted[i] = &entries->v.obj.entries[i];
|
|
851
|
+
if (count > 1) qsort(sorted, count, sizeof(*sorted), scr_env_entry_order);
|
|
852
|
+
ScrDyn *result = ordinary ? scr_dyn_new_obj() : scr_dyn_new_obj_null_proto();
|
|
853
|
+
for (size_t i = 0; i < count; i++) {
|
|
854
|
+
const ScrDynEntry *entry = sorted[i];
|
|
855
|
+
if (ordinary && entry->key_len == 9 && memcmp(entry->key, "__proto__", 9) == 0) continue;
|
|
856
|
+
if (!ordinary) {
|
|
857
|
+
scr_dyn_obj_set(result, entry->key, entry->key_len, scr_dyn_retain(entry->value));
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
/* Convert sorted entries to JS strings after parsing. */
|
|
861
|
+
ScrStr *key = scr_str_from_utf8_lossy((const uint8_t *)entry->key, entry->key_len);
|
|
862
|
+
const ScrStr *raw = entry->value->v.str;
|
|
863
|
+
ScrStr *value = scr_str_from_utf8_lossy((const uint8_t *)raw->data, raw->len);
|
|
864
|
+
scr_dyn_obj_set(result, key->data, key->len, scr_dyn_new_str(value));
|
|
865
|
+
scr_str_release(key);
|
|
866
|
+
scr_str_release(value);
|
|
867
|
+
}
|
|
868
|
+
free(sorted);
|
|
869
|
+
scr_dyn_release(entries);
|
|
870
|
+
return result;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
ScrDyn *scr_util_parse_env(const ScrDyn *content) {
|
|
874
|
+
if (content->kind != SCR_DYN_STR) {
|
|
875
|
+
scr_dyn_arg_type_fail("content", "of type string", content);
|
|
876
|
+
return NULL;
|
|
877
|
+
}
|
|
878
|
+
return scr_env_parse_content(content->v.str, true);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
void scr_fs_invalid_named_path(const ScrDyn *value, const ScrStr *path, const char *name) {
|
|
882
|
+
ScrJsonBuf b;
|
|
883
|
+
scr_jb_init(&b);
|
|
884
|
+
scr_jb_puts(&b, "The argument '");
|
|
885
|
+
scr_jb_puts(&b, name);
|
|
886
|
+
scr_jb_puts(&b, "' must be a string, Uint8Array, or URL without null bytes. Received ");
|
|
887
|
+
if (value->kind == SCR_DYN_BYTES) {
|
|
888
|
+
const ScrBytes *bytes = value->v.bytes;
|
|
889
|
+
if (value->buffer) {
|
|
890
|
+
scr_jb_puts(&b, "<Buffer");
|
|
891
|
+
for (size_t i = 0; i < bytes->len; i++) {
|
|
892
|
+
char hex[4];
|
|
893
|
+
snprintf(hex, sizeof hex, " %02x", bytes->data[i]);
|
|
894
|
+
scr_jb_puts(&b, hex);
|
|
895
|
+
}
|
|
896
|
+
scr_jb_putc(&b, '>');
|
|
897
|
+
} else {
|
|
898
|
+
char length[48];
|
|
899
|
+
snprintf(length, sizeof length, "Uint8Array(%zu) [ ", bytes->len);
|
|
900
|
+
scr_jb_puts(&b, length);
|
|
901
|
+
for (size_t i = 0; i < bytes->len; i++) {
|
|
902
|
+
char number[8];
|
|
903
|
+
snprintf(number, sizeof number, "%s%u", i ? ", " : "", bytes->data[i]);
|
|
904
|
+
scr_jb_puts(&b, number);
|
|
905
|
+
}
|
|
906
|
+
scr_jb_puts(&b, " ]");
|
|
907
|
+
}
|
|
908
|
+
} else {
|
|
909
|
+
char quote = memchr(path->data, '\'', path->len) ? '"' : '\'';
|
|
910
|
+
scr_jb_putc(&b, quote);
|
|
911
|
+
for (size_t i = 0; i < path->len; i++) {
|
|
912
|
+
unsigned char c = (unsigned char)path->data[i];
|
|
913
|
+
if (c == (unsigned char)quote || c == '\\') { scr_jb_putc(&b, '\\'); scr_jb_putc(&b, (char)c); }
|
|
914
|
+
else if (c == '\n') scr_jb_puts(&b, "\\n");
|
|
915
|
+
else if (c == '\r') scr_jb_puts(&b, "\\r");
|
|
916
|
+
else if (c == '\t') scr_jb_puts(&b, "\\t");
|
|
917
|
+
else if (c < 0x20 || c == 0x7f) {
|
|
918
|
+
char escape[5];
|
|
919
|
+
snprintf(escape, sizeof escape, "\\x%02X", c);
|
|
920
|
+
scr_jb_puts(&b, escape);
|
|
921
|
+
} else scr_jb_putc(&b, (char)c);
|
|
922
|
+
}
|
|
923
|
+
scr_jb_putc(&b, quote);
|
|
924
|
+
}
|
|
925
|
+
ScrStr *message = scr_jb_finish(&b);
|
|
926
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, message->data, message->len, "ERR_INVALID_ARG_VALUE");
|
|
927
|
+
scr_str_release(message);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
void scr_fs_invalid_path(const ScrDyn *value, const ScrStr *path) {
|
|
931
|
+
scr_fs_invalid_named_path(value, path, "path");
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
void scr_process_load_env_file(const ScrDyn *value) {
|
|
935
|
+
ScrStr *path;
|
|
936
|
+
if (value->kind == SCR_DYN_UNDEF || value->kind == SCR_DYN_NULL) path = scr_str_new("./.env", 6);
|
|
937
|
+
else if (value->kind == SCR_DYN_STR) path = scr_str_retain(value->v.str);
|
|
938
|
+
else if (scr_dyn_native_url_is(value)) path = scr_url_to_path(value->v.handle.ptr);
|
|
939
|
+
else if (scr_dyn_bytes_is(value, SCR_BYTES_U8)) path = scr_str_new((const char *)value->v.bytes->data, value->v.bytes->len);
|
|
940
|
+
else {
|
|
941
|
+
scr_dyn_arg_type_fail("path", "of type string or an instance of Buffer or URL", value);
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
if (!path) return;
|
|
945
|
+
if (memchr(path->data, 0, path->len)) {
|
|
946
|
+
scr_fs_invalid_path(value, path);
|
|
947
|
+
scr_str_release(path);
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
ScrStr *content = scr_fs_read_file_core(path, true);
|
|
951
|
+
scr_str_release(path);
|
|
952
|
+
if (!content) return;
|
|
953
|
+
ScrDyn *entries = scr_env_parse_content(content, false);
|
|
954
|
+
scr_str_release(content);
|
|
955
|
+
for (size_t i = 0; i < entries->v.obj.len; i++) {
|
|
956
|
+
const ScrDynEntry *entry = &entries->v.obj.entries[i];
|
|
957
|
+
/* Node normalizes each raw sorted entry immediately before checking
|
|
958
|
+
* the environment. Distinct invalid byte keys can normalize alike;
|
|
959
|
+
* the first absent name wins, rather than the last parsed entry. */
|
|
960
|
+
ScrStr *key = scr_str_from_utf8_lossy((const uint8_t *)entry->key, entry->key_len);
|
|
961
|
+
ScrStr *existing = scr_env_get(key);
|
|
962
|
+
if (!existing) {
|
|
963
|
+
const ScrStr *raw = entry->value->v.str;
|
|
964
|
+
ScrStr *value = scr_str_from_utf8_lossy((const uint8_t *)raw->data, raw->len);
|
|
965
|
+
scr_env_set(key, value);
|
|
966
|
+
scr_str_release(value);
|
|
967
|
+
}
|
|
968
|
+
scr_str_release(existing);
|
|
969
|
+
scr_str_release(key);
|
|
970
|
+
}
|
|
971
|
+
scr_dyn_release(entries);
|
|
972
|
+
}
|
|
973
|
+
|
|
732
974
|
/* The whole environment as one fresh string[] of alternating
|
|
733
975
|
* [k0, v0, k1, v1, ...] entries in environ order — the raw material of the
|
|
734
976
|
* compiler's process.env snapshot record (insertion order = environ order,
|
|
@@ -2019,13 +2261,15 @@ void scr_fs_throw(int e, const char *op, const ScrStr *path) {
|
|
|
2019
2261
|
const char *name = scr_errno_name(e, namebuf, sizeof namebuf);
|
|
2020
2262
|
const char *text = scr_errno_text(e);
|
|
2021
2263
|
char pathbuf[PATH_MAX];
|
|
2022
|
-
const char *shown = scr_fs_err_path(path, pathbuf);
|
|
2264
|
+
const char *shown = path ? scr_fs_err_path(path, pathbuf) : "";
|
|
2023
2265
|
size_t cap = strlen(name) + strlen(text) + strlen(op) + strlen(shown) + 8;
|
|
2024
2266
|
char *msg = malloc(cap);
|
|
2025
2267
|
if (!msg) {
|
|
2026
2268
|
scr_trap("scriptc: out of memory\n");
|
|
2027
2269
|
}
|
|
2028
|
-
int len =
|
|
2270
|
+
int len = path
|
|
2271
|
+
? snprintf(msg, cap, "%s: %s, %s '%s'", name, text, op, shown)
|
|
2272
|
+
: snprintf(msg, cap, "%s: %s, %s", name, text, op);
|
|
2029
2273
|
/* A real Error instance (name "Error", message = Node's text) — what a
|
|
2030
2274
|
* typed catch's `e instanceof Error` + `e.message` observes in Node —
|
|
2031
2275
|
* with `code` stamped to the errno name (the exotic-errno fallback
|
|
@@ -2055,7 +2299,7 @@ static FILE *scr_fs_fopen(const ScrStr *path, const char *mode) {
|
|
|
2055
2299
|
#endif
|
|
2056
2300
|
}
|
|
2057
2301
|
|
|
2058
|
-
ScrStr *
|
|
2302
|
+
static ScrStr *scr_fs_read_file_core(ScrStr *path, bool env_file) {
|
|
2059
2303
|
FILE *f = scr_fs_fopen(path, "rb");
|
|
2060
2304
|
if (!f) {
|
|
2061
2305
|
scr_fs_throw(errno, "open", path);
|
|
@@ -2083,7 +2327,16 @@ ScrStr *scr_fs_read_file(ScrStr *path) {
|
|
|
2083
2327
|
int e = errno;
|
|
2084
2328
|
fclose(f);
|
|
2085
2329
|
free(buf);
|
|
2086
|
-
|
|
2330
|
+
if (env_file) {
|
|
2331
|
+
ScrJsonBuf b;
|
|
2332
|
+
scr_jb_init(&b);
|
|
2333
|
+
scr_jb_puts(&b, "Contents of '");
|
|
2334
|
+
scr_jb_put_str(&b, path);
|
|
2335
|
+
scr_jb_puts(&b, "' should be a valid string.");
|
|
2336
|
+
ScrStr *message = scr_jb_finish(&b);
|
|
2337
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, message->data, message->len, "ERR_INVALID_ARG_TYPE");
|
|
2338
|
+
scr_str_release(message);
|
|
2339
|
+
} else scr_fs_throw(e, "read", path);
|
|
2087
2340
|
return NULL;
|
|
2088
2341
|
}
|
|
2089
2342
|
fclose(f);
|
|
@@ -2092,6 +2345,11 @@ ScrStr *scr_fs_read_file(ScrStr *path) {
|
|
|
2092
2345
|
return s;
|
|
2093
2346
|
}
|
|
2094
2347
|
|
|
2348
|
+
ScrStr *scr_fs_read_file(ScrStr *path) {
|
|
2349
|
+
return scr_fs_read_file_core(path, false);
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
|
|
2095
2353
|
static ScrStr *scr_fs_realpath_common(ScrStr *path, const char *op) {
|
|
2096
2354
|
#ifdef _WIN32
|
|
2097
2355
|
WCHAR *wide = scr_fs_win_wide(path);
|
|
@@ -2258,7 +2516,45 @@ void scr_fs_mkdir_mode(ScrStr *path, double mode) {
|
|
|
2258
2516
|
if (scr_sys_mkdir(path->data, (mode_t)mode) != 0) scr_fs_throw(errno, "mkdir", path);
|
|
2259
2517
|
}
|
|
2260
2518
|
|
|
2519
|
+
#ifdef _WIN32
|
|
2520
|
+
/* The CRT's stat follows junctions. Recognize real link reparse records
|
|
2521
|
+
* before unlink/rm dispatch so a tree walk never descends into their targets.
|
|
2522
|
+
* Return 1 for a removed link, 0 for an ordinary entry, -1 on failure. */
|
|
2523
|
+
static int scr_fs_win_remove_link(const char *path, size_t len) {
|
|
2524
|
+
ScrStr *text = scr_str_new(path, len);
|
|
2525
|
+
WCHAR *wide = scr_fs_win_wide(text);
|
|
2526
|
+
scr_str_release(text);
|
|
2527
|
+
if (!wide) { errno = scr_fs_win_errno(GetLastError()); return -1; }
|
|
2528
|
+
DWORD attributes = GetFileAttributesW(wide);
|
|
2529
|
+
if (attributes == INVALID_FILE_ATTRIBUTES || !(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { free(wide); return 0; }
|
|
2530
|
+
HANDLE h = CreateFileW(wide, FILE_READ_ATTRIBUTES,
|
|
2531
|
+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
|
|
2532
|
+
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
|
|
2533
|
+
if (h == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); free(wide); errno = scr_fs_win_errno(error); return -1; }
|
|
2534
|
+
FILE_ATTRIBUTE_TAG_INFO tagged;
|
|
2535
|
+
if (!GetFileInformationByHandleEx(h, FileAttributeTagInfo, &tagged, sizeof tagged)) {
|
|
2536
|
+
DWORD error = GetLastError(); CloseHandle(h); free(wide); errno = scr_fs_win_errno(error); return -1;
|
|
2537
|
+
}
|
|
2538
|
+
double size;
|
|
2539
|
+
bool link = scr_stats_is_link_tag(tagged.ReparseTag);
|
|
2540
|
+
// Mount-point tags also represent mounted volumes. Only junction payloads
|
|
2541
|
+
// are links; keep the existing ordinary-directory behavior for volumes.
|
|
2542
|
+
if (link && tagged.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) link = scr_stats_link_target(h, tagged.ReparseTag, &size, NULL);
|
|
2543
|
+
CloseHandle(h);
|
|
2544
|
+
if (!link) { free(wide); return 0; }
|
|
2545
|
+
BOOL ok = attributes & FILE_ATTRIBUTE_DIRECTORY ? RemoveDirectoryW(wide) : DeleteFileW(wide);
|
|
2546
|
+
DWORD error = ok ? 0 : GetLastError();
|
|
2547
|
+
free(wide);
|
|
2548
|
+
if (!ok) { errno = scr_fs_win_errno(error); return -1; }
|
|
2549
|
+
return 1;
|
|
2550
|
+
}
|
|
2551
|
+
#endif
|
|
2552
|
+
|
|
2261
2553
|
void scr_fs_unlink(ScrStr *path) {
|
|
2554
|
+
#ifdef _WIN32
|
|
2555
|
+
int link = scr_fs_win_remove_link(path->data, path->len);
|
|
2556
|
+
if (link != 0) { if (link < 0) scr_fs_throw(errno, "unlink", path); return; }
|
|
2557
|
+
#endif
|
|
2262
2558
|
if (unlink(path->data) != 0) scr_fs_throw(errno, "unlink", path);
|
|
2263
2559
|
}
|
|
2264
2560
|
|
|
@@ -2353,6 +2649,20 @@ double scr_fs_open_numeric(ScrStr *path, double flags, double mode) {
|
|
|
2353
2649
|
}
|
|
2354
2650
|
|
|
2355
2651
|
static void scr_fs_throw_nopath(int e, const char *op);
|
|
2652
|
+
static bool scr_fs_write_fd_valid(double fd);
|
|
2653
|
+
|
|
2654
|
+
double scr_fs_open_native(ScrStr *path, double flags, double mode) {
|
|
2655
|
+
#ifdef _WIN32
|
|
2656
|
+
WCHAR *wide = scr_fs_win_wide(path);
|
|
2657
|
+
if (!wide) { scr_fs_throw(scr_fs_win_errno(GetLastError()), "open", path); return 0; }
|
|
2658
|
+
int fd = _wopen(wide, (int)flags | O_BINARY, (int)mode);
|
|
2659
|
+
free(wide);
|
|
2660
|
+
#else
|
|
2661
|
+
int fd = open(path->data, (int)flags | O_BINARY, (mode_t)mode);
|
|
2662
|
+
#endif
|
|
2663
|
+
if (fd < 0) { scr_fs_throw(errno, "open", path); return 0; }
|
|
2664
|
+
return (double)fd;
|
|
2665
|
+
}
|
|
2356
2666
|
|
|
2357
2667
|
void scr_fs_fchmod(double fd, double mode) {
|
|
2358
2668
|
#ifdef _WIN32
|
|
@@ -2374,6 +2684,114 @@ void scr_fs_fsync(double fd) {
|
|
|
2374
2684
|
#endif
|
|
2375
2685
|
}
|
|
2376
2686
|
|
|
2687
|
+
void scr_fs_fdatasync(double fd) {
|
|
2688
|
+
if (!scr_fs_write_fd_valid(fd)) return;
|
|
2689
|
+
#if defined(_WIN32)
|
|
2690
|
+
if (_commit((int)fd) != 0) scr_fs_throw_nopath(errno, "fdatasync");
|
|
2691
|
+
#elif defined(__APPLE__)
|
|
2692
|
+
/* macOS/libuv implements fdatasync with fsync. */
|
|
2693
|
+
if (fsync((int)fd) != 0) scr_fs_throw_nopath(errno, "fdatasync");
|
|
2694
|
+
#else
|
|
2695
|
+
if (fdatasync((int)fd) != 0) scr_fs_throw_nopath(errno, "fdatasync");
|
|
2696
|
+
#endif
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
void scr_fs_ftruncate(double fd, double length) {
|
|
2700
|
+
if (!scr_fs_write_fd_valid(fd)) return;
|
|
2701
|
+
char received[48], msg[192];
|
|
2702
|
+
scr_num_received(length, received);
|
|
2703
|
+
if (!(isfinite(length) && trunc(length) == length) ||
|
|
2704
|
+
length < -9007199254740991.0 || length > 9007199254740991.0) {
|
|
2705
|
+
const char *bound = isfinite(length) && trunc(length) == length
|
|
2706
|
+
? ">= -9007199254740991 && <= 9007199254740991" : "an integer";
|
|
2707
|
+
int n = snprintf(msg, sizeof msg,
|
|
2708
|
+
"The value of \"len\" is out of range. It must be %s. Received %s", bound, received);
|
|
2709
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)n, "ERR_OUT_OF_RANGE");
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2712
|
+
if (length < 0) length = 0;
|
|
2713
|
+
#ifdef _WIN32
|
|
2714
|
+
int error = (int)_chsize_s((int)fd, (__int64)length);
|
|
2715
|
+
if (error) scr_fs_throw_nopath(error, "ftruncate");
|
|
2716
|
+
#else
|
|
2717
|
+
if (ftruncate((int)fd, (off_t)length) != 0) scr_fs_throw_nopath(errno, "ftruncate");
|
|
2718
|
+
#endif
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
#ifdef _WIN32
|
|
2722
|
+
static bool scr_fs_set_times(HANDLE handle, double access, double modified) {
|
|
2723
|
+
FILETIME atime, mtime, now;
|
|
2724
|
+
if (isinf(access) || isinf(modified)) GetSystemTimeAsFileTime(&now);
|
|
2725
|
+
double values[2] = { access, modified };
|
|
2726
|
+
FILETIME *files[2] = { &atime, &mtime };
|
|
2727
|
+
for (size_t i = 0; i < 2; i++) {
|
|
2728
|
+
if (isnan(values[i])) { files[i] = NULL; continue; }
|
|
2729
|
+
if (isinf(values[i])) { *files[i] = now; continue; }
|
|
2730
|
+
double ticks = values[i] * 10000000.0 + 116444736000000000.0;
|
|
2731
|
+
if (ticks < -9223372036854775808.0 || ticks >= 9223372036854775808.0) {
|
|
2732
|
+
SetLastError(ERROR_INVALID_PARAMETER);
|
|
2733
|
+
return false;
|
|
2734
|
+
}
|
|
2735
|
+
int64_t time = (int64_t)ticks;
|
|
2736
|
+
files[i]->dwLowDateTime = (DWORD)(uint64_t)time;
|
|
2737
|
+
files[i]->dwHighDateTime = (DWORD)((uint64_t)time >> 32);
|
|
2738
|
+
}
|
|
2739
|
+
return SetFileTime(handle, NULL, files[0], files[1]) != 0;
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
static DWORD scr_fs_set_path_times(WCHAR *path, double atime, double mtime, bool nofollow) {
|
|
2743
|
+
DWORD flags = FILE_FLAG_BACKUP_SEMANTICS | (nofollow ? FILE_FLAG_OPEN_REPARSE_POINT : 0);
|
|
2744
|
+
HANDLE handle = CreateFileW(path, FILE_WRITE_ATTRIBUTES,
|
|
2745
|
+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flags, NULL);
|
|
2746
|
+
if (handle == INVALID_HANDLE_VALUE) return GetLastError();
|
|
2747
|
+
DWORD error = scr_fs_set_times(handle, atime, mtime) ? 0 : GetLastError();
|
|
2748
|
+
CloseHandle(handle);
|
|
2749
|
+
return error;
|
|
2750
|
+
}
|
|
2751
|
+
#else
|
|
2752
|
+
/* Match the pinned libuv conversion: microsecond truncation, normalized
|
|
2753
|
+
* negative fractions, and the special current/unchanged time sentinels. */
|
|
2754
|
+
static bool scr_fs_timespec(double time, struct timespec *out) {
|
|
2755
|
+
if (isinf(time)) { *out = (struct timespec){ 0, UTIME_NOW }; return true; }
|
|
2756
|
+
if (isnan(time)) { *out = (struct timespec){ 0, UTIME_OMIT }; return true; }
|
|
2757
|
+
if (time < -9223372036854775808.0 || time >= 9223372036854775808.0) return false;
|
|
2758
|
+
out->tv_sec = (time_t)time;
|
|
2759
|
+
out->tv_nsec = (long)((time - (double)out->tv_sec) * 1000000000.0);
|
|
2760
|
+
out->tv_nsec -= out->tv_nsec % 1000;
|
|
2761
|
+
if (out->tv_nsec < 0) { out->tv_nsec += 1000000000; out->tv_sec--; }
|
|
2762
|
+
return true;
|
|
2763
|
+
}
|
|
2764
|
+
#endif
|
|
2765
|
+
|
|
2766
|
+
void scr_fs_utimes(ScrStr *path, double atime, double mtime, bool nofollow) {
|
|
2767
|
+
const char *op = nofollow ? "lutime" : "utime";
|
|
2768
|
+
#ifdef _WIN32
|
|
2769
|
+
WCHAR *wide = scr_fs_win_wide(path);
|
|
2770
|
+
if (!wide) { scr_fs_throw(scr_fs_win_errno(GetLastError()), op, path); return; }
|
|
2771
|
+
DWORD error = scr_fs_set_path_times(wide, atime, mtime, nofollow);
|
|
2772
|
+
if (nofollow && (error == ERROR_SYMLINK_NOT_SUPPORTED || error == ERROR_NOT_A_REPARSE_POINT))
|
|
2773
|
+
error = scr_fs_set_path_times(wide, atime, mtime, false);
|
|
2774
|
+
free(wide);
|
|
2775
|
+
if (error) scr_fs_throw(scr_fs_win_errno(error), op, path);
|
|
2776
|
+
#else
|
|
2777
|
+
struct timespec times[2];
|
|
2778
|
+
if (!scr_fs_timespec(atime, ×[0]) || !scr_fs_timespec(mtime, ×[1])) { scr_fs_throw(EINVAL, op, path); return; }
|
|
2779
|
+
if (utimensat(AT_FDCWD, path->data, times, nofollow ? AT_SYMLINK_NOFOLLOW : 0) != 0) scr_fs_throw(errno, op, path);
|
|
2780
|
+
#endif
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2783
|
+
void scr_fs_futimes(double fd, double atime, double mtime) {
|
|
2784
|
+
#ifdef _WIN32
|
|
2785
|
+
HANDLE handle = (HANDLE)_get_osfhandle((int)fd);
|
|
2786
|
+
if (handle == INVALID_HANDLE_VALUE) { scr_fs_throw_nopath(EBADF, "futime"); return; }
|
|
2787
|
+
if (!scr_fs_set_times(handle, atime, mtime)) scr_fs_throw_nopath(scr_fs_win_errno(GetLastError()), "futime");
|
|
2788
|
+
#else
|
|
2789
|
+
struct timespec times[2];
|
|
2790
|
+
if (!scr_fs_timespec(atime, ×[0]) || !scr_fs_timespec(mtime, ×[1])) { scr_fs_throw_nopath(EINVAL, "futime"); return; }
|
|
2791
|
+
if (futimens((int)fd, times) != 0) scr_fs_throw_nopath(errno, "futime");
|
|
2792
|
+
#endif
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2377
2795
|
/* fs.closeSync(fd) — close(2); failure throws Node's path-less fs error
|
|
2378
2796
|
* shape ("EBADF: bad file descriptor, close"). */
|
|
2379
2797
|
|
|
@@ -2771,6 +3189,73 @@ double scr_fs_read_sync(double fd, ScrBytes *buf, double offset, double length,
|
|
|
2771
3189
|
return (double)n;
|
|
2772
3190
|
}
|
|
2773
3191
|
|
|
3192
|
+
/* Gather/scatter one contiguous window around ONE kernel operation. Multiple
|
|
3193
|
+
* scalar calls would change short I/O and pipe atomicity. This portable seam
|
|
3194
|
+
* also preserves positioned I/O on targets without preadv/pwritev. */
|
|
3195
|
+
double scr_fs_read_zero(double fd, double position) {
|
|
3196
|
+
if (!scr_fs_write_fd_valid(fd)) return 0;
|
|
3197
|
+
uint8_t dummy;
|
|
3198
|
+
ssize_t n = position < 0 ? read((int)fd, &dummy, 0)
|
|
3199
|
+
: scr_fs_pread((int)fd, &dummy, 0, position);
|
|
3200
|
+
if (n < 0) scr_fs_throw_nopath(errno, "read");
|
|
3201
|
+
return 0;
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
static double scr_fs_vector_sync(double fd, ScrArr *buffers, double position,
|
|
3205
|
+
bool writing) {
|
|
3206
|
+
size_t count = (size_t)scr_arr_len(buffers);
|
|
3207
|
+
if (writing && count == 0) return 0;
|
|
3208
|
+
if (!scr_fs_write_fd_valid(fd)) return 0;
|
|
3209
|
+
if (count == 0) { scr_fs_throw_nopath(EINVAL, "read"); return 0; }
|
|
3210
|
+
size_t length = 0;
|
|
3211
|
+
for (size_t i = 0; i < count; i++) {
|
|
3212
|
+
ScrBytes *buf = scr_arr_get_ref(buffers, (double)i);
|
|
3213
|
+
size_t n = buf->len;
|
|
3214
|
+
scr_bytes_release(buf);
|
|
3215
|
+
if (n > SIZE_MAX - length) scr_trap("scriptc: vector I/O size overflow\n");
|
|
3216
|
+
length += n;
|
|
3217
|
+
}
|
|
3218
|
+
uint8_t *data = malloc(length ? length : 1);
|
|
3219
|
+
if (!data) scr_trap("scriptc: out of memory\n");
|
|
3220
|
+
if (writing) {
|
|
3221
|
+
size_t at = 0;
|
|
3222
|
+
for (size_t i = 0; i < count; i++) {
|
|
3223
|
+
ScrBytes *buf = scr_arr_get_ref(buffers, (double)i);
|
|
3224
|
+
if (buf->len) memcpy(data + at, buf->data, buf->len);
|
|
3225
|
+
at += buf->len;
|
|
3226
|
+
scr_bytes_release(buf);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
ScrBytes window = {SIZE_MAX, length, SCR_BYTES_U8, data, NULL};
|
|
3230
|
+
position = isfinite(position) && position >= 0 && position <= 9007199254740991.0
|
|
3231
|
+
? trunc(position) : -1;
|
|
3232
|
+
double transferred = writing
|
|
3233
|
+
? scr_fs_write_sync(fd, &window, 0, (double)length, position)
|
|
3234
|
+
: length == 0 ? scr_fs_read_zero(fd, position)
|
|
3235
|
+
: scr_fs_read_sync(fd, &window, 0, (double)length, position);
|
|
3236
|
+
if (!writing && !scr_exc_pending()) {
|
|
3237
|
+
size_t at = 0, remaining = (size_t)transferred;
|
|
3238
|
+
for (size_t i = 0; i < count && remaining; i++) {
|
|
3239
|
+
ScrBytes *buf = scr_arr_get_ref(buffers, (double)i);
|
|
3240
|
+
size_t n = buf->len < remaining ? buf->len : remaining;
|
|
3241
|
+
if (n) memcpy(buf->data, data + at, n);
|
|
3242
|
+
at += n;
|
|
3243
|
+
remaining -= n;
|
|
3244
|
+
scr_bytes_release(buf);
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
free(data);
|
|
3248
|
+
return transferred;
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
double scr_fs_readv_sync(double fd, ScrArr *buffers, double position) {
|
|
3252
|
+
return scr_fs_vector_sync(fd, buffers, position, false);
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
double scr_fs_writev_sync(double fd, ScrArr *buffers, double position) {
|
|
3256
|
+
return scr_fs_vector_sync(fd, buffers, position, true);
|
|
3257
|
+
}
|
|
3258
|
+
|
|
2774
3259
|
void scr_fs_close(double fd) {
|
|
2775
3260
|
if (close((int)fd) != 0) {
|
|
2776
3261
|
int e = errno;
|
|
@@ -2807,18 +3292,15 @@ void scr_fs_link(ScrStr *source, ScrStr *dest) {
|
|
|
2807
3292
|
WCHAR *from = scr_fs_win_wide(source);
|
|
2808
3293
|
WCHAR *to = scr_fs_win_wide(dest);
|
|
2809
3294
|
if (!from || !to) {
|
|
3295
|
+
DWORD error = GetLastError();
|
|
2810
3296
|
free(from); free(to);
|
|
2811
|
-
scr_fs_throw2(
|
|
3297
|
+
scr_fs_throw2(scr_fs_win_errno(error), "link", source, dest);
|
|
2812
3298
|
return;
|
|
2813
3299
|
}
|
|
2814
3300
|
BOOL ok = CreateHardLinkW(to, from, NULL);
|
|
2815
3301
|
DWORD error = ok ? 0 : GetLastError();
|
|
2816
3302
|
free(from); free(to);
|
|
2817
|
-
if (!ok)
|
|
2818
|
-
int code = error == ERROR_ALREADY_EXISTS || error == ERROR_FILE_EXISTS ? EEXIST :
|
|
2819
|
-
error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND ? ENOENT : EACCES;
|
|
2820
|
-
scr_fs_throw2(code, "link", source, dest);
|
|
2821
|
-
}
|
|
3303
|
+
if (!ok) scr_fs_throw2(scr_fs_win_errno(error), "link", source, dest);
|
|
2822
3304
|
#else
|
|
2823
3305
|
if (link(source->data, dest->data) != 0) scr_fs_throw2(errno, "link", source, dest);
|
|
2824
3306
|
#endif
|
|
@@ -2989,6 +3471,10 @@ void scr_fs_rename(ScrStr *oldpath, ScrStr *newpath) {
|
|
|
2989
3471
|
static int scr_rm_unlink(const char *path, size_t len);
|
|
2990
3472
|
|
|
2991
3473
|
void scr_fs_rm(ScrStr *path) {
|
|
3474
|
+
#ifdef _WIN32
|
|
3475
|
+
int link = scr_fs_win_remove_link(path->data, path->len);
|
|
3476
|
+
if (link != 0) { if (link < 0) scr_fs_throw(errno, "unlink", path); return; }
|
|
3477
|
+
#endif
|
|
2992
3478
|
/* Node's rmSync: lstat first (a missing path reports the lstat syscall),
|
|
2993
3479
|
* refuse directories (Node requires `recursive`, which the scriptc
|
|
2994
3480
|
* surface doesn't declare — the message wording diverges from Node's
|
|
@@ -3143,6 +3629,10 @@ static int scr_rm_unlink(const char *path, size_t len) {
|
|
|
3143
3629
|
/* Post-order tree removal for rmSync's recursive form. Stops at (and
|
|
3144
3630
|
* records) the first failure, with the failing path and syscall name. */
|
|
3145
3631
|
static void scr_rm_tree_e(const char *path, size_t len, ScrRmFail *f) {
|
|
3632
|
+
#ifdef _WIN32
|
|
3633
|
+
int link = scr_fs_win_remove_link(path, len);
|
|
3634
|
+
if (link != 0) { if (link < 0) scr_rm_fail_set(f, errno, "unlink", path, len); return; }
|
|
3635
|
+
#endif
|
|
3146
3636
|
struct stat st;
|
|
3147
3637
|
if (lstat(path, &st) != 0) {
|
|
3148
3638
|
scr_rm_fail_set(f, errno, "lstat", path, len);
|
|
@@ -3183,6 +3673,10 @@ static void scr_rm_tree_e(const char *path, size_t len, ScrRmFail *f) {
|
|
|
3183
3673
|
* force's ENOENT swallow, the non-recursive directory rejection, and the
|
|
3184
3674
|
* tree walk — failures recorded in `f`, never thrown. */
|
|
3185
3675
|
static void scr_fs_rm_attempt(ScrStr *path, bool recursive, bool force, ScrRmFail *f) {
|
|
3676
|
+
#ifdef _WIN32
|
|
3677
|
+
int link = scr_fs_win_remove_link(path->data, path->len);
|
|
3678
|
+
if (link != 0) { if (link < 0) scr_rm_fail_set(f, errno, "unlink", path->data, path->len); return; }
|
|
3679
|
+
#endif
|
|
3186
3680
|
struct stat st;
|
|
3187
3681
|
if (lstat(path->data, &st) != 0) {
|
|
3188
3682
|
if (force && errno == ENOENT) return; /* Node: force swallows ENOENT */
|
|
@@ -3636,7 +4130,7 @@ typedef struct {
|
|
|
3636
4130
|
/* Node/libuv reports a Windows link's target-text length as st_size. The
|
|
3637
4131
|
* ordinary handle information does not carry that value, so read the reparse
|
|
3638
4132
|
* payload while the no-follow handle is live. */
|
|
3639
|
-
static bool
|
|
4133
|
+
static bool scr_stats_link_target(HANDLE h, DWORD tag, double *size_out, ScrStr **text_out) {
|
|
3640
4134
|
union {
|
|
3641
4135
|
ScrStatsReparseData align;
|
|
3642
4136
|
unsigned char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
|
@@ -3681,6 +4175,7 @@ static bool scr_stats_link_size(HANDLE h, DWORD tag, double *size_out) {
|
|
|
3681
4175
|
/* WSL's LX symlink payload is a version word followed by UTF-8 bytes. */
|
|
3682
4176
|
if (data->data_len < sizeof(ULONG)) return false;
|
|
3683
4177
|
*size_out = (double)(data->data_len - sizeof(ULONG));
|
|
4178
|
+
if (text_out) *text_out = scr_str_new((const char *)data->body.generic.bytes + sizeof(ULONG), data->data_len - sizeof(ULONG));
|
|
3684
4179
|
return true;
|
|
3685
4180
|
} else {
|
|
3686
4181
|
/* App execution links carry a counted UTF-16 string list; the third
|
|
@@ -3709,6 +4204,10 @@ static bool scr_stats_link_size(HANDLE h, DWORD tag, double *size_out) {
|
|
|
3709
4204
|
double size = scr_stats_utf16_len(target, len);
|
|
3710
4205
|
if (size < 0) return false;
|
|
3711
4206
|
*size_out = size;
|
|
4207
|
+
if (text_out) {
|
|
4208
|
+
*text_out = scr_fs_win_utf8(target, len);
|
|
4209
|
+
if (!*text_out) return false;
|
|
4210
|
+
}
|
|
3712
4211
|
return true;
|
|
3713
4212
|
}
|
|
3714
4213
|
|
|
@@ -3734,6 +4233,10 @@ static bool scr_stats_link_size(HANDLE h, DWORD tag, double *size_out) {
|
|
|
3734
4233
|
double size = scr_stats_utf16_len(target, len);
|
|
3735
4234
|
if (size < 0) return false;
|
|
3736
4235
|
*size_out = size;
|
|
4236
|
+
if (text_out) {
|
|
4237
|
+
*text_out = scr_fs_win_utf8(target, len);
|
|
4238
|
+
if (!*text_out) return false;
|
|
4239
|
+
}
|
|
3737
4240
|
return true;
|
|
3738
4241
|
}
|
|
3739
4242
|
|
|
@@ -3747,8 +4250,21 @@ static ScrStats *scr_stats_of_path(const ScrStr *path, const char *op,
|
|
|
3747
4250
|
HANDLE h = CreateFileW(wide, FILE_READ_ATTRIBUTES,
|
|
3748
4251
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
3749
4252
|
NULL, OPEN_EXISTING, flags, NULL);
|
|
4253
|
+
if (h == INVALID_HANDLE_VALUE) {
|
|
4254
|
+
DWORD error = GetLastError();
|
|
4255
|
+
DWORD attributes = GetFileAttributesW(wide);
|
|
4256
|
+
free(wide);
|
|
4257
|
+
/* CRT stat can report the link entry when its target cannot be opened,
|
|
4258
|
+
* hiding dangling links and Windows file/directory type mismatches.
|
|
4259
|
+
* libuv preserves the failed follow operation for reparse points. */
|
|
4260
|
+
if (!no_follow && attributes != INVALID_FILE_ATTRIBUTES &&
|
|
4261
|
+
(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
|
4262
|
+
scr_fs_throw(scr_fs_win_errno(error), op, path);
|
|
4263
|
+
return NULL;
|
|
4264
|
+
}
|
|
4265
|
+
return scr_stats_crt_fallback(path, op);
|
|
4266
|
+
}
|
|
3750
4267
|
free(wide);
|
|
3751
|
-
if (h == INVALID_HANDLE_VALUE) return scr_stats_crt_fallback(path, op);
|
|
3752
4268
|
|
|
3753
4269
|
DWORD file_type = GetFileType(h);
|
|
3754
4270
|
if (file_type != FILE_TYPE_DISK) {
|
|
@@ -3772,7 +4288,7 @@ static ScrStats *scr_stats_of_path(const ScrStr *path, const char *op,
|
|
|
3772
4288
|
CloseHandle(h);
|
|
3773
4289
|
return scr_stats_of_path(path, op, false);
|
|
3774
4290
|
}
|
|
3775
|
-
if (!
|
|
4291
|
+
if (!scr_stats_link_target(h, tagged.ReparseTag, &link_size, NULL)) {
|
|
3776
4292
|
/* A mount-point tag may name a mounted volume rather than a junction.
|
|
3777
4293
|
* libuv treats those (and malformed/unsupported link payloads) as
|
|
3778
4294
|
* ordinary reparse points and retries with the final component
|
|
@@ -3849,6 +4365,215 @@ static ScrStats *scr_stats_of(const struct stat *st) {
|
|
|
3849
4365
|
}
|
|
3850
4366
|
#endif
|
|
3851
4367
|
|
|
4368
|
+
/* Symbolic link targets are stored verbatim on POSIX. Windows junctions
|
|
4369
|
+
* instead carry an absolute NT path in a bounded mount-point reparse record. */
|
|
4370
|
+
#ifdef _WIN32
|
|
4371
|
+
static DWORD scr_fs_win_junction(const WCHAR *target, const WCHAR *destination) {
|
|
4372
|
+
size_t len = wcslen(target);
|
|
4373
|
+
/* libuv permits drive-absolute junctions, not UNC mount targets. */
|
|
4374
|
+
size_t skip = len >= 4 && !wcsncmp(target, L"\\\\?\\", 4) ? 4 : 0;
|
|
4375
|
+
const WCHAR *plain = target + skip;
|
|
4376
|
+
size_t chars = len - skip;
|
|
4377
|
+
if (chars < 3 || !((plain[0] >= L'A' && plain[0] <= L'Z') || (plain[0] >= L'a' && plain[0] <= L'z')) || plain[1] != L':' || plain[2] != L'\\') return ERROR_INVALID_PARAMETER;
|
|
4378
|
+
size_t fixed = offsetof(ScrStatsReparseData, body.mount.path);
|
|
4379
|
+
size_t total = fixed + (4 + chars + 1 + chars + 1) * sizeof(WCHAR);
|
|
4380
|
+
if (total > MAXIMUM_REPARSE_DATA_BUFFER_SIZE) return ERROR_FILENAME_EXCED_RANGE;
|
|
4381
|
+
ScrStatsReparseData *data = calloc(1, total);
|
|
4382
|
+
if (!data) return ERROR_NOT_ENOUGH_MEMORY;
|
|
4383
|
+
data->tag = IO_REPARSE_TAG_MOUNT_POINT;
|
|
4384
|
+
data->data_len = (USHORT)(total - offsetof(ScrStatsReparseData, body));
|
|
4385
|
+
data->body.mount.substitute_offset = 0;
|
|
4386
|
+
data->body.mount.substitute_len = (USHORT)((4 + chars) * sizeof(WCHAR));
|
|
4387
|
+
data->body.mount.print_offset = (USHORT)((4 + chars + 1) * sizeof(WCHAR));
|
|
4388
|
+
data->body.mount.print_len = (USHORT)(chars * sizeof(WCHAR));
|
|
4389
|
+
memcpy(data->body.mount.path, L"\\??\\", 4 * sizeof(WCHAR));
|
|
4390
|
+
memcpy(data->body.mount.path + 4, plain, chars * sizeof(WCHAR));
|
|
4391
|
+
memcpy(data->body.mount.path + 4 + chars + 1, plain, chars * sizeof(WCHAR));
|
|
4392
|
+
DWORD error = 0, used;
|
|
4393
|
+
bool created = CreateDirectoryW(destination, NULL) != 0;
|
|
4394
|
+
HANDLE h = INVALID_HANDLE_VALUE;
|
|
4395
|
+
if (!created) error = GetLastError();
|
|
4396
|
+
else {
|
|
4397
|
+
h = CreateFileW(destination, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
|
|
4398
|
+
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
|
|
4399
|
+
if (h == INVALID_HANDLE_VALUE) error = GetLastError();
|
|
4400
|
+
else if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, data, (DWORD)total, NULL, 0, &used, NULL)) error = GetLastError();
|
|
4401
|
+
}
|
|
4402
|
+
if (h != INVALID_HANDLE_VALUE) CloseHandle(h);
|
|
4403
|
+
if (error && created) RemoveDirectoryW(destination);
|
|
4404
|
+
free(data);
|
|
4405
|
+
return error;
|
|
4406
|
+
}
|
|
4407
|
+
#endif
|
|
4408
|
+
|
|
4409
|
+
#ifdef _WIN32
|
|
4410
|
+
/* libuv keeps this fallback for later calls once Windows rejects the flag. */
|
|
4411
|
+
static DWORD scr_fs_symlink_usermode_flag = 0x2; /* ALLOW_UNPRIVILEGED_CREATE */
|
|
4412
|
+
#endif
|
|
4413
|
+
|
|
4414
|
+
int scr_fs_symlink_infer(ScrStr *absolute) {
|
|
4415
|
+
#ifdef _WIN32
|
|
4416
|
+
WCHAR *probe = scr_fs_win_wide(absolute);
|
|
4417
|
+
DWORD attributes = probe ? GetFileAttributesW(probe) : INVALID_FILE_ATTRIBUTES;
|
|
4418
|
+
free(probe);
|
|
4419
|
+
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
|
|
4420
|
+
#else
|
|
4421
|
+
(void)absolute;
|
|
4422
|
+
return 0;
|
|
4423
|
+
#endif
|
|
4424
|
+
}
|
|
4425
|
+
|
|
4426
|
+
void scr_fs_symlink(ScrStr *target, ScrStr *destination, int kind) {
|
|
4427
|
+
#ifdef _WIN32
|
|
4428
|
+
ScrArr *parts = scr_arr_new_ref(scr_str_retain_v, scr_str_release_v, NULL, 3);
|
|
4429
|
+
scr_arr_push_ref(parts, scr_str_retain(destination));
|
|
4430
|
+
scr_arr_push_ref(parts, scr_str_new("..", 2));
|
|
4431
|
+
scr_arr_push_ref(parts, scr_str_retain(target));
|
|
4432
|
+
ScrStr *absolute = scr_path_win32_resolve(parts);
|
|
4433
|
+
scr_arr_release(parts);
|
|
4434
|
+
if (kind == -1) kind = scr_fs_symlink_infer(absolute);
|
|
4435
|
+
ScrStr *stored;
|
|
4436
|
+
if (kind == 2) stored = scr_path_win32_to_namespaced_path(absolute);
|
|
4437
|
+
else if (scr_path_win32_is_absolute(target)) stored = scr_path_win32_to_namespaced_path(target);
|
|
4438
|
+
else {
|
|
4439
|
+
char *text = malloc(target->len ? target->len : 1);
|
|
4440
|
+
if (!text) scr_trap("scriptc: out of memory\n");
|
|
4441
|
+
for (size_t i = 0; i < target->len; i++) text[i] = target->data[i] == '/' ? '\\' : target->data[i];
|
|
4442
|
+
stored = scr_str_new(text, target->len);
|
|
4443
|
+
free(text);
|
|
4444
|
+
}
|
|
4445
|
+
scr_str_release(absolute);
|
|
4446
|
+
/* Node's binding namespaces the link path before passing it to libuv. */
|
|
4447
|
+
ScrStr *link_path = scr_path_win32_to_namespaced_path(destination);
|
|
4448
|
+
WCHAR *from = scr_fs_win_wide(stored), *to = scr_fs_win_wide(link_path);
|
|
4449
|
+
DWORD error = !from || !to ? GetLastError() : 0;
|
|
4450
|
+
if (!error && kind == 2) error = scr_fs_win_junction(from, to);
|
|
4451
|
+
else if (!error) {
|
|
4452
|
+
DWORD flags = (kind == 1 ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) | scr_fs_symlink_usermode_flag;
|
|
4453
|
+
if (!CreateSymbolicLinkW(to, from, flags)) {
|
|
4454
|
+
error = GetLastError();
|
|
4455
|
+
if (error == ERROR_INVALID_PARAMETER && scr_fs_symlink_usermode_flag) {
|
|
4456
|
+
scr_fs_symlink_usermode_flag = 0;
|
|
4457
|
+
error = CreateSymbolicLinkW(to, from, flags & ~0x2u) ? 0 : GetLastError();
|
|
4458
|
+
}
|
|
4459
|
+
}
|
|
4460
|
+
}
|
|
4461
|
+
free(from); free(to); scr_str_release(stored); scr_str_release(link_path);
|
|
4462
|
+
if (error) scr_fs_throw2(scr_fs_win_errno(error), "symlink", target, destination);
|
|
4463
|
+
#else
|
|
4464
|
+
(void)kind;
|
|
4465
|
+
if (symlink(target->data, destination->data) != 0) scr_fs_throw2(errno, "symlink", target, destination);
|
|
4466
|
+
#endif
|
|
4467
|
+
}
|
|
4468
|
+
|
|
4469
|
+
/* Keep the unsigned libuv statfs representation before selecting Number or
|
|
4470
|
+
* BigInt. Free-space counts are live snapshots, not cached volume metadata. */
|
|
4471
|
+
ScrDyn *scr_fs_statfs(ScrStr *path, bool bigint) {
|
|
4472
|
+
uint64_t fields[7];
|
|
4473
|
+
#if defined(_WIN32)
|
|
4474
|
+
ScrStr *syscall_path = scr_path_win32_to_namespaced_path(path);
|
|
4475
|
+
WCHAR *wide = scr_fs_win_wide(syscall_path);
|
|
4476
|
+
scr_str_release(syscall_path);
|
|
4477
|
+
if (!wide) { scr_fs_throw(scr_fs_win_errno(GetLastError()), "statfs", path); return NULL; }
|
|
4478
|
+
DWORD sectors, bytes, free_clusters, total_clusters;
|
|
4479
|
+
BOOL ok = GetDiskFreeSpaceW(wide, §ors, &bytes, &free_clusters, &total_clusters);
|
|
4480
|
+
DWORD error = ok ? 0 : GetLastError();
|
|
4481
|
+
if (!ok && error == ERROR_DIRECTORY) {
|
|
4482
|
+
/* libuv retries a file path's parent once, preserving the original path
|
|
4483
|
+
* in errors. GetFullPathName also expands relative and drive paths. */
|
|
4484
|
+
DWORD size = GetFullPathNameW(wide, 0, NULL, NULL);
|
|
4485
|
+
WCHAR *absolute = size ? malloc((size_t)size * sizeof *absolute) : NULL;
|
|
4486
|
+
if (size && !absolute) scr_trap("scriptc: out of memory\n");
|
|
4487
|
+
WCHAR *part = NULL;
|
|
4488
|
+
DWORD length = absolute ? GetFullPathNameW(wide, size, absolute, &part) : 0;
|
|
4489
|
+
if (length && length < size) {
|
|
4490
|
+
if (part) *part = L'\0';
|
|
4491
|
+
ok = GetDiskFreeSpaceW(absolute, §ors, &bytes, &free_clusters, &total_clusters);
|
|
4492
|
+
if (!ok) error = GetLastError();
|
|
4493
|
+
}
|
|
4494
|
+
free(absolute);
|
|
4495
|
+
}
|
|
4496
|
+
free(wide);
|
|
4497
|
+
if (!ok) { scr_fs_throw(scr_fs_win_errno(error), "statfs", path); return NULL; }
|
|
4498
|
+
fields[0] = 0;
|
|
4499
|
+
fields[1] = (DWORD)(bytes * sectors);
|
|
4500
|
+
fields[2] = total_clusters;
|
|
4501
|
+
fields[3] = fields[4] = free_clusters;
|
|
4502
|
+
fields[5] = fields[6] = 0;
|
|
4503
|
+
#elif defined(__wasi__)
|
|
4504
|
+
(void)path; (void)bigint;
|
|
4505
|
+
static const char message[] = "Filesystem capacity queries have no native WASI syscall";
|
|
4506
|
+
scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020");
|
|
4507
|
+
return NULL;
|
|
4508
|
+
#else
|
|
4509
|
+
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
|
|
4510
|
+
struct statfs snapshot;
|
|
4511
|
+
if (statfs(path->data, &snapshot) != 0) { scr_fs_throw(errno, "statfs", path); return NULL; }
|
|
4512
|
+
fields[0] = (uint64_t)snapshot.f_type;
|
|
4513
|
+
#else
|
|
4514
|
+
struct statvfs snapshot;
|
|
4515
|
+
if (statvfs(path->data, &snapshot) != 0) { scr_fs_throw(errno, "statfs", path); return NULL; }
|
|
4516
|
+
fields[0] = 0;
|
|
4517
|
+
#endif
|
|
4518
|
+
fields[1] = (uint64_t)snapshot.f_bsize;
|
|
4519
|
+
fields[2] = (uint64_t)snapshot.f_blocks;
|
|
4520
|
+
fields[3] = (uint64_t)snapshot.f_bfree;
|
|
4521
|
+
fields[4] = (uint64_t)snapshot.f_bavail;
|
|
4522
|
+
fields[5] = (uint64_t)snapshot.f_files;
|
|
4523
|
+
fields[6] = (uint64_t)snapshot.f_ffree;
|
|
4524
|
+
#endif
|
|
4525
|
+
static const char *names[] = { "type", "bsize", "blocks", "bfree", "bavail", "files", "ffree" };
|
|
4526
|
+
ScrDyn *result = scr_dyn_new_obj();
|
|
4527
|
+
for (size_t i = 0; i < 7; i++) {
|
|
4528
|
+
ScrDyn *value;
|
|
4529
|
+
if (bigint) {
|
|
4530
|
+
ScrBigInt *number = scr_bigint_from_u64(fields[i]);
|
|
4531
|
+
value = scr_dyn_new_bigint(number);
|
|
4532
|
+
scr_bigint_release(number);
|
|
4533
|
+
} else value = scr_dyn_new_num((double)fields[i]);
|
|
4534
|
+
scr_dyn_obj_set(result, names[i], strlen(names[i]), value);
|
|
4535
|
+
}
|
|
4536
|
+
return result;
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
ScrBytes *scr_fs_readlink_bytes(ScrStr *path) {
|
|
4540
|
+
#ifdef _WIN32
|
|
4541
|
+
WCHAR *wide = scr_fs_win_wide(path);
|
|
4542
|
+
if (!wide) { scr_fs_throw(scr_fs_win_errno(GetLastError()), "readlink", path); return NULL; }
|
|
4543
|
+
HANDLE h = CreateFileW(wide, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
|
|
4544
|
+
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
4545
|
+
free(wide);
|
|
4546
|
+
if (h == INVALID_HANDLE_VALUE) { scr_fs_throw(scr_fs_win_errno(GetLastError()), "readlink", path); return NULL; }
|
|
4547
|
+
FILE_ATTRIBUTE_TAG_INFO tagged;
|
|
4548
|
+
ScrStr *text = NULL;
|
|
4549
|
+
double size;
|
|
4550
|
+
bool ok = GetFileInformationByHandleEx(h, FileAttributeTagInfo, &tagged, sizeof tagged) &&
|
|
4551
|
+
scr_stats_link_target(h, tagged.ReparseTag, &size, &text);
|
|
4552
|
+
CloseHandle(h);
|
|
4553
|
+
if (!ok || !text) { scr_str_release(text); scr_fs_throw(EINVAL, "readlink", path); return NULL; }
|
|
4554
|
+
ScrBytes *bytes = scr_bytes_from_data((const uint8_t *)text->data, text->len);
|
|
4555
|
+
scr_str_release(text);
|
|
4556
|
+
return bytes;
|
|
4557
|
+
#else
|
|
4558
|
+
size_t capacity = 256;
|
|
4559
|
+
uint8_t *text = NULL;
|
|
4560
|
+
for (;;) {
|
|
4561
|
+
uint8_t *grown = realloc(text, capacity);
|
|
4562
|
+
if (!grown) { free(text); scr_trap("scriptc: out of memory\n"); }
|
|
4563
|
+
text = grown;
|
|
4564
|
+
ssize_t size = readlink(path->data, (char *)text, capacity);
|
|
4565
|
+
if (size < 0) { int error = errno; free(text); scr_fs_throw(error, "readlink", path); return NULL; }
|
|
4566
|
+
if ((size_t)size < capacity) {
|
|
4567
|
+
ScrBytes *bytes = scr_bytes_from_data(text, (size_t)size);
|
|
4568
|
+
free(text);
|
|
4569
|
+
return bytes;
|
|
4570
|
+
}
|
|
4571
|
+
if (capacity > SIZE_MAX / 2) { free(text); scr_fs_throw(ENAMETOOLONG, "readlink", path); return NULL; }
|
|
4572
|
+
capacity *= 2;
|
|
4573
|
+
}
|
|
4574
|
+
#endif
|
|
4575
|
+
}
|
|
4576
|
+
|
|
3852
4577
|
ScrStats *scr_fs_stat(ScrStr *path) {
|
|
3853
4578
|
#ifdef _WIN32
|
|
3854
4579
|
return scr_stats_of_path(path, "stat", false);
|
|
@@ -4256,7 +4981,7 @@ ScrStr *scr_crypto_random_string(double n, ScrStr *enc) {
|
|
|
4256
4981
|
return out;
|
|
4257
4982
|
}
|
|
4258
4983
|
|
|
4259
|
-
/* ── incremental MD5/SHA-1/SHA-
|
|
4984
|
+
/* ── incremental MD5/SHA-1/SHA-2 and HMAC ──────────────────────────
|
|
4260
4985
|
* The first-class Hash/Hmac handles, fused one-shot paths, island bridge,
|
|
4261
4986
|
* and PBKDF2 all share these contexts. Hash.copy() is a context snapshot;
|
|
4262
4987
|
* update never buffers the full input. Differential tests pin every
|
|
@@ -4462,7 +5187,7 @@ static size_t scr_sha1_digest(const unsigned char *data, size_t len, unsigned ch
|
|
|
4462
5187
|
static ScrStr *scr_digest_encode(const unsigned char *d, size_t n, const ScrStr *enc) {
|
|
4463
5188
|
if (enc->len == 3 && memcmp(enc->data, "hex", 3) == 0) {
|
|
4464
5189
|
static const char hex[] = "0123456789abcdef";
|
|
4465
|
-
char buf[
|
|
5190
|
+
char buf[128];
|
|
4466
5191
|
for (size_t i = 0; i < n; i++) {
|
|
4467
5192
|
buf[i * 2] = hex[d[i] >> 4];
|
|
4468
5193
|
buf[i * 2 + 1] = hex[d[i] & 0x0f];
|
|
@@ -4471,7 +5196,7 @@ static ScrStr *scr_digest_encode(const unsigned char *d, size_t n, const ScrStr
|
|
|
4471
5196
|
}
|
|
4472
5197
|
/* base64, standard alphabet, '=' padded — Buffer.toString("base64"). */
|
|
4473
5198
|
static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
4474
|
-
char buf[
|
|
5199
|
+
char buf[88];
|
|
4475
5200
|
size_t o = 0;
|
|
4476
5201
|
for (size_t i = 0; i < n; i += 3) {
|
|
4477
5202
|
unsigned v = (unsigned)d[i] << 16;
|
|
@@ -4586,10 +5311,140 @@ static size_t scr_md5_final(const ScrMd5Ctx *source, unsigned char out[32]) {
|
|
|
4586
5311
|
return 16;
|
|
4587
5312
|
}
|
|
4588
5313
|
|
|
5314
|
+
/* SHA-384/SHA-512, FIPS 180-4. The 128-byte blocks and HMAC pads differ
|
|
5315
|
+
* from SHA-256; the digest dispatcher keeps that distinction explicit. */
|
|
5316
|
+
typedef struct ScrSha512Ctx {
|
|
5317
|
+
uint64_t h[8];
|
|
5318
|
+
uint64_t bytes;
|
|
5319
|
+
unsigned char tail[128];
|
|
5320
|
+
size_t tail_len;
|
|
5321
|
+
} ScrSha512Ctx;
|
|
5322
|
+
|
|
5323
|
+
static const uint64_t scr_sha512_k[80] = {
|
|
5324
|
+
UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), UINT64_C(0xb5c0fbcfec4d3b2f),
|
|
5325
|
+
UINT64_C(0xe9b5dba58189dbbc), UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019),
|
|
5326
|
+
UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), UINT64_C(0xd807aa98a3030242),
|
|
5327
|
+
UINT64_C(0x12835b0145706fbe), UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2),
|
|
5328
|
+
UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), UINT64_C(0x9bdc06a725c71235),
|
|
5329
|
+
UINT64_C(0xc19bf174cf692694), UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3),
|
|
5330
|
+
UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), UINT64_C(0x2de92c6f592b0275),
|
|
5331
|
+
UINT64_C(0x4a7484aa6ea6e483), UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5),
|
|
5332
|
+
UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), UINT64_C(0xb00327c898fb213f),
|
|
5333
|
+
UINT64_C(0xbf597fc7beef0ee4), UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725),
|
|
5334
|
+
UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), UINT64_C(0x27b70a8546d22ffc),
|
|
5335
|
+
UINT64_C(0x2e1b21385c26c926), UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df),
|
|
5336
|
+
UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), UINT64_C(0x81c2c92e47edaee6),
|
|
5337
|
+
UINT64_C(0x92722c851482353b), UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001),
|
|
5338
|
+
UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), UINT64_C(0xd192e819d6ef5218),
|
|
5339
|
+
UINT64_C(0xd69906245565a910), UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8),
|
|
5340
|
+
UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), UINT64_C(0x2748774cdf8eeb99),
|
|
5341
|
+
UINT64_C(0x34b0bcb5e19b48a8), UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb),
|
|
5342
|
+
UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), UINT64_C(0x748f82ee5defb2fc),
|
|
5343
|
+
UINT64_C(0x78a5636f43172f60), UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec),
|
|
5344
|
+
UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), UINT64_C(0xbef9a3f7b2c67915),
|
|
5345
|
+
UINT64_C(0xc67178f2e372532b), UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207),
|
|
5346
|
+
UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), UINT64_C(0x06f067aa72176fba),
|
|
5347
|
+
UINT64_C(0x0a637dc5a2c898a6), UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b),
|
|
5348
|
+
UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), UINT64_C(0x3c9ebe0a15c9bebc),
|
|
5349
|
+
UINT64_C(0x431d67c49c100d4c), UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a),
|
|
5350
|
+
UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817),
|
|
5351
|
+
};
|
|
5352
|
+
|
|
5353
|
+
static uint64_t scr_sha512_rotr(uint64_t x, unsigned n) {
|
|
5354
|
+
return (x >> n) | (x << (64 - n));
|
|
5355
|
+
}
|
|
5356
|
+
|
|
5357
|
+
static void scr_sha512_block(uint64_t h[8], const unsigned char *data) {
|
|
5358
|
+
uint64_t w[80];
|
|
5359
|
+
for (size_t i = 0; i < 16; i++) {
|
|
5360
|
+
w[i] = 0;
|
|
5361
|
+
for (size_t b = 0; b < 8; b++) w[i] = (w[i] << 8) | data[i * 8 + b];
|
|
5362
|
+
}
|
|
5363
|
+
for (size_t i = 16; i < 80; i++) {
|
|
5364
|
+
uint64_t s0 = scr_sha512_rotr(w[i - 15], 1) ^ scr_sha512_rotr(w[i - 15], 8) ^ (w[i - 15] >> 7);
|
|
5365
|
+
uint64_t s1 = scr_sha512_rotr(w[i - 2], 19) ^ scr_sha512_rotr(w[i - 2], 61) ^ (w[i - 2] >> 6);
|
|
5366
|
+
w[i] = w[i - 16] + s0 + w[i - 7] + s1;
|
|
5367
|
+
}
|
|
5368
|
+
uint64_t a = h[0], b = h[1], c = h[2], d = h[3];
|
|
5369
|
+
uint64_t e = h[4], f = h[5], g = h[6], v = h[7];
|
|
5370
|
+
for (size_t i = 0; i < 80; i++) {
|
|
5371
|
+
uint64_t s1 = scr_sha512_rotr(e, 14) ^ scr_sha512_rotr(e, 18) ^ scr_sha512_rotr(e, 41);
|
|
5372
|
+
uint64_t t1 = v + s1 + ((e & f) ^ (~e & g)) + scr_sha512_k[i] + w[i];
|
|
5373
|
+
uint64_t s0 = scr_sha512_rotr(a, 28) ^ scr_sha512_rotr(a, 34) ^ scr_sha512_rotr(a, 39);
|
|
5374
|
+
uint64_t t2 = s0 + ((a & b) ^ (a & c) ^ (b & c));
|
|
5375
|
+
v = g; g = f; f = e; e = d + t1;
|
|
5376
|
+
d = c; c = b; b = a; a = t1 + t2;
|
|
5377
|
+
}
|
|
5378
|
+
h[0] += a; h[1] += b; h[2] += c; h[3] += d;
|
|
5379
|
+
h[4] += e; h[5] += f; h[6] += g; h[7] += v;
|
|
5380
|
+
}
|
|
5381
|
+
|
|
5382
|
+
static void scr_sha512_init(ScrSha512Ctx *ctx, bool sha384) {
|
|
5383
|
+
static const uint64_t initial512[8] = {
|
|
5384
|
+
UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b),
|
|
5385
|
+
UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1),
|
|
5386
|
+
UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f),
|
|
5387
|
+
UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179)};
|
|
5388
|
+
static const uint64_t initial384[8] = {
|
|
5389
|
+
UINT64_C(0xcbbb9d5dc1059ed8), UINT64_C(0x629a292a367cd507),
|
|
5390
|
+
UINT64_C(0x9159015a3070dd17), UINT64_C(0x152fecd8f70e5939),
|
|
5391
|
+
UINT64_C(0x67332667ffc00b31), UINT64_C(0x8eb44a8768581511),
|
|
5392
|
+
UINT64_C(0xdb0c2e0d64f98fa7), UINT64_C(0x47b5481dbefa4fa4)};
|
|
5393
|
+
memcpy(ctx->h, sha384 ? initial384 : initial512, sizeof ctx->h);
|
|
5394
|
+
ctx->bytes = 0;
|
|
5395
|
+
ctx->tail_len = 0;
|
|
5396
|
+
}
|
|
5397
|
+
|
|
5398
|
+
static void scr_sha512_update(ScrSha512Ctx *ctx, const unsigned char *data, size_t len) {
|
|
5399
|
+
ctx->bytes += len;
|
|
5400
|
+
if (ctx->tail_len != 0) {
|
|
5401
|
+
size_t take = 128 - ctx->tail_len;
|
|
5402
|
+
if (take > len) take = len;
|
|
5403
|
+
memcpy(ctx->tail + ctx->tail_len, data, take);
|
|
5404
|
+
ctx->tail_len += take;
|
|
5405
|
+
data += take;
|
|
5406
|
+
len -= take;
|
|
5407
|
+
if (ctx->tail_len == 128) {
|
|
5408
|
+
scr_sha512_block(ctx->h, ctx->tail);
|
|
5409
|
+
ctx->tail_len = 0;
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
while (len >= 128) {
|
|
5413
|
+
scr_sha512_block(ctx->h, data);
|
|
5414
|
+
data += 128;
|
|
5415
|
+
len -= 128;
|
|
5416
|
+
}
|
|
5417
|
+
if (len != 0) {
|
|
5418
|
+
memcpy(ctx->tail, data, len);
|
|
5419
|
+
ctx->tail_len = len;
|
|
5420
|
+
}
|
|
5421
|
+
}
|
|
5422
|
+
|
|
5423
|
+
static size_t scr_sha512_final(const ScrSha512Ctx *source, unsigned char out[64], bool sha384) {
|
|
5424
|
+
ScrSha512Ctx ctx = *source;
|
|
5425
|
+
unsigned char tail[256] = {0};
|
|
5426
|
+
memcpy(tail, ctx.tail, ctx.tail_len);
|
|
5427
|
+
tail[ctx.tail_len] = 0x80;
|
|
5428
|
+
size_t pad = ctx.tail_len < 112 ? 128 : 256;
|
|
5429
|
+
uint64_t low = ctx.bytes << 3, high = ctx.bytes >> 61;
|
|
5430
|
+
for (size_t b = 0; b < 8; b++) {
|
|
5431
|
+
tail[pad - 1 - b] = (unsigned char)(low >> (8 * b));
|
|
5432
|
+
tail[pad - 9 - b] = (unsigned char)(high >> (8 * b));
|
|
5433
|
+
}
|
|
5434
|
+
scr_sha512_block(ctx.h, tail);
|
|
5435
|
+
if (pad == 256) scr_sha512_block(ctx.h, tail + 128);
|
|
5436
|
+
size_t len = sha384 ? 48 : 64;
|
|
5437
|
+
for (size_t i = 0; i < len; i++) out[i] = (unsigned char)(ctx.h[i / 8] >> (56 - 8 * (i % 8)));
|
|
5438
|
+
return len;
|
|
5439
|
+
}
|
|
5440
|
+
|
|
4589
5441
|
typedef enum ScrDigestAlg {
|
|
4590
5442
|
SCR_DIGEST_MD5,
|
|
4591
5443
|
SCR_DIGEST_SHA1,
|
|
5444
|
+
SCR_DIGEST_SHA224,
|
|
4592
5445
|
SCR_DIGEST_SHA256,
|
|
5446
|
+
SCR_DIGEST_SHA384,
|
|
5447
|
+
SCR_DIGEST_SHA512,
|
|
4593
5448
|
} ScrDigestAlg;
|
|
4594
5449
|
|
|
4595
5450
|
typedef struct ScrDigestCtx {
|
|
@@ -4598,6 +5453,7 @@ typedef struct ScrDigestCtx {
|
|
|
4598
5453
|
ScrMd5Ctx md5;
|
|
4599
5454
|
ScrSha1Ctx sha1;
|
|
4600
5455
|
ScrSha256Ctx sha256;
|
|
5456
|
+
ScrSha512Ctx sha512;
|
|
4601
5457
|
} state;
|
|
4602
5458
|
} ScrDigestCtx;
|
|
4603
5459
|
|
|
@@ -4617,7 +5473,10 @@ static bool scr_digest_name_is(const char *name, size_t len, const char *literal
|
|
|
4617
5473
|
static bool scr_digest_alg(const char *name, size_t len, ScrDigestAlg *out) {
|
|
4618
5474
|
if (scr_digest_name_is(name, len, "md5")) *out = SCR_DIGEST_MD5;
|
|
4619
5475
|
else if (scr_digest_name_is(name, len, "sha1")) *out = SCR_DIGEST_SHA1;
|
|
5476
|
+
else if (scr_digest_name_is(name, len, "sha224")) *out = SCR_DIGEST_SHA224;
|
|
4620
5477
|
else if (scr_digest_name_is(name, len, "sha256")) *out = SCR_DIGEST_SHA256;
|
|
5478
|
+
else if (scr_digest_name_is(name, len, "sha384")) *out = SCR_DIGEST_SHA384;
|
|
5479
|
+
else if (scr_digest_name_is(name, len, "sha512")) *out = SCR_DIGEST_SHA512;
|
|
4621
5480
|
else return false;
|
|
4622
5481
|
return true;
|
|
4623
5482
|
}
|
|
@@ -4627,7 +5486,17 @@ static void scr_digest_init(ScrDigestCtx *ctx, ScrDigestAlg alg) {
|
|
|
4627
5486
|
switch (alg) {
|
|
4628
5487
|
case SCR_DIGEST_MD5: scr_md5_init(&ctx->state.md5); break;
|
|
4629
5488
|
case SCR_DIGEST_SHA1: scr_sha1_init(&ctx->state.sha1); break;
|
|
5489
|
+
case SCR_DIGEST_SHA224: {
|
|
5490
|
+
static const uint32_t initial[8] = {
|
|
5491
|
+
0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
|
|
5492
|
+
0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4};
|
|
5493
|
+
scr_sha256_init(&ctx->state.sha256);
|
|
5494
|
+
memcpy(ctx->state.sha256.h, initial, sizeof initial);
|
|
5495
|
+
break;
|
|
5496
|
+
}
|
|
4630
5497
|
case SCR_DIGEST_SHA256: scr_sha256_init(&ctx->state.sha256); break;
|
|
5498
|
+
case SCR_DIGEST_SHA384: scr_sha512_init(&ctx->state.sha512, true); break;
|
|
5499
|
+
case SCR_DIGEST_SHA512: scr_sha512_init(&ctx->state.sha512, false); break;
|
|
4631
5500
|
}
|
|
4632
5501
|
}
|
|
4633
5502
|
|
|
@@ -4635,15 +5504,21 @@ static void scr_digest_update(ScrDigestCtx *ctx, const unsigned char *data, size
|
|
|
4635
5504
|
switch (ctx->alg) {
|
|
4636
5505
|
case SCR_DIGEST_MD5: scr_md5_update(&ctx->state.md5, data, len); break;
|
|
4637
5506
|
case SCR_DIGEST_SHA1: scr_sha1_update(&ctx->state.sha1, data, len); break;
|
|
5507
|
+
case SCR_DIGEST_SHA224:
|
|
4638
5508
|
case SCR_DIGEST_SHA256: scr_sha256_update(&ctx->state.sha256, data, len); break;
|
|
5509
|
+
case SCR_DIGEST_SHA384:
|
|
5510
|
+
case SCR_DIGEST_SHA512: scr_sha512_update(&ctx->state.sha512, data, len); break;
|
|
4639
5511
|
}
|
|
4640
5512
|
}
|
|
4641
5513
|
|
|
4642
|
-
static size_t scr_digest_final(const ScrDigestCtx *ctx, unsigned char out[
|
|
5514
|
+
static size_t scr_digest_final(const ScrDigestCtx *ctx, unsigned char out[64]) {
|
|
4643
5515
|
switch (ctx->alg) {
|
|
4644
5516
|
case SCR_DIGEST_MD5: return scr_md5_final(&ctx->state.md5, out);
|
|
4645
5517
|
case SCR_DIGEST_SHA1: return scr_sha1_final(&ctx->state.sha1, out);
|
|
5518
|
+
case SCR_DIGEST_SHA224: scr_sha256_final(&ctx->state.sha256, out); return 28;
|
|
4646
5519
|
case SCR_DIGEST_SHA256: return scr_sha256_final(&ctx->state.sha256, out);
|
|
5520
|
+
case SCR_DIGEST_SHA384: return scr_sha512_final(&ctx->state.sha512, out, true);
|
|
5521
|
+
case SCR_DIGEST_SHA512: return scr_sha512_final(&ctx->state.sha512, out, false);
|
|
4647
5522
|
}
|
|
4648
5523
|
return 0;
|
|
4649
5524
|
}
|
|
@@ -4655,9 +5530,10 @@ static void scr_crypto_zero(void *ptr, size_t len) {
|
|
|
4655
5530
|
|
|
4656
5531
|
static void scr_hmac_init(ScrDigestCtx *inner, ScrDigestCtx *outer, ScrDigestAlg alg,
|
|
4657
5532
|
const unsigned char *key, size_t keylen) {
|
|
4658
|
-
unsigned char kblock[
|
|
4659
|
-
|
|
4660
|
-
|
|
5533
|
+
unsigned char kblock[128] = {0};
|
|
5534
|
+
size_t block_len = alg == SCR_DIGEST_SHA384 || alg == SCR_DIGEST_SHA512 ? 128 : 64;
|
|
5535
|
+
unsigned char kd[64];
|
|
5536
|
+
if (keylen > block_len) {
|
|
4661
5537
|
ScrDigestCtx key_hash;
|
|
4662
5538
|
scr_digest_init(&key_hash, alg);
|
|
4663
5539
|
scr_digest_update(&key_hash, key, keylen);
|
|
@@ -4668,24 +5544,24 @@ static void scr_hmac_init(ScrDigestCtx *inner, ScrDigestCtx *outer, ScrDigestAlg
|
|
|
4668
5544
|
} else if (keylen != 0) {
|
|
4669
5545
|
memcpy(kblock, key, keylen);
|
|
4670
5546
|
}
|
|
4671
|
-
unsigned char ipad[
|
|
4672
|
-
unsigned char opad[
|
|
4673
|
-
for (size_t i = 0; i <
|
|
5547
|
+
unsigned char ipad[128];
|
|
5548
|
+
unsigned char opad[128];
|
|
5549
|
+
for (size_t i = 0; i < block_len; i++) {
|
|
4674
5550
|
ipad[i] = (unsigned char)(kblock[i] ^ 0x36);
|
|
4675
5551
|
opad[i] = (unsigned char)(kblock[i] ^ 0x5c);
|
|
4676
5552
|
}
|
|
4677
5553
|
scr_digest_init(inner, alg);
|
|
4678
|
-
scr_digest_update(inner, ipad,
|
|
5554
|
+
scr_digest_update(inner, ipad, block_len);
|
|
4679
5555
|
scr_digest_init(outer, alg);
|
|
4680
|
-
scr_digest_update(outer, opad,
|
|
5556
|
+
scr_digest_update(outer, opad, block_len);
|
|
4681
5557
|
scr_crypto_zero(kblock, sizeof kblock);
|
|
4682
|
-
scr_crypto_zero(ipad,
|
|
4683
|
-
scr_crypto_zero(opad,
|
|
5558
|
+
scr_crypto_zero(ipad, block_len);
|
|
5559
|
+
scr_crypto_zero(opad, block_len);
|
|
4684
5560
|
}
|
|
4685
5561
|
|
|
4686
5562
|
static size_t scr_hmac_final(const ScrDigestCtx *inner, const ScrDigestCtx *outer,
|
|
4687
|
-
unsigned char out[
|
|
4688
|
-
unsigned char inner_digest[
|
|
5563
|
+
unsigned char out[64]) {
|
|
5564
|
+
unsigned char inner_digest[64];
|
|
4689
5565
|
size_t inner_len = scr_digest_final(inner, inner_digest);
|
|
4690
5566
|
ScrDigestCtx final_outer = *outer;
|
|
4691
5567
|
scr_digest_update(&final_outer, inner_digest, inner_len);
|
|
@@ -4699,7 +5575,7 @@ static size_t scr_hmac_final(const ScrDigestCtx *inner, const ScrDigestCtx *oute
|
|
|
4699
5575
|
* helpers both use the incremental core. Returns zero for an unknown
|
|
4700
5576
|
* algorithm, preserving the island's unsupported-digest probe. */
|
|
4701
5577
|
size_t scr_crypto_digest_raw(const char *alg, const unsigned char *data, size_t len,
|
|
4702
|
-
unsigned char out[
|
|
5578
|
+
unsigned char out[64]) {
|
|
4703
5579
|
ScrDigestAlg kind;
|
|
4704
5580
|
if (!scr_digest_alg(alg, strlen(alg), &kind)) return 0;
|
|
4705
5581
|
ScrDigestCtx ctx;
|
|
@@ -4709,7 +5585,7 @@ size_t scr_crypto_digest_raw(const char *alg, const unsigned char *data, size_t
|
|
|
4709
5585
|
}
|
|
4710
5586
|
|
|
4711
5587
|
size_t scr_crypto_hmac_raw(const char *alg, const unsigned char *key, size_t keylen,
|
|
4712
|
-
const unsigned char *data, size_t len, unsigned char out[
|
|
5588
|
+
const unsigned char *data, size_t len, unsigned char out[64]) {
|
|
4713
5589
|
ScrDigestAlg kind;
|
|
4714
5590
|
if (!scr_digest_alg(alg, strlen(alg), &kind)) return 0;
|
|
4715
5591
|
ScrDigestCtx inner, outer;
|
|
@@ -4859,7 +5735,7 @@ ScrCryptoHash *scr_crypto_hash_copy(ScrCryptoHash *hash) {
|
|
|
4859
5735
|
return copy;
|
|
4860
5736
|
}
|
|
4861
5737
|
|
|
4862
|
-
static size_t scr_crypto_hash_finish(ScrCryptoHash *hash, unsigned char out[
|
|
5738
|
+
static size_t scr_crypto_hash_finish(ScrCryptoHash *hash, unsigned char out[64]) {
|
|
4863
5739
|
if (hash->finalized) {
|
|
4864
5740
|
if (hash->hmac) return 0; /* Node's repeated Hmac.digest() is empty. */
|
|
4865
5741
|
scr_crypto_hash_finalized();
|
|
@@ -4872,7 +5748,7 @@ static size_t scr_crypto_hash_finish(ScrCryptoHash *hash, unsigned char out[32])
|
|
|
4872
5748
|
}
|
|
4873
5749
|
|
|
4874
5750
|
ScrStr *scr_crypto_hash_digest_string(ScrCryptoHash *hash, ScrStr *enc) {
|
|
4875
|
-
unsigned char digest[
|
|
5751
|
+
unsigned char digest[64];
|
|
4876
5752
|
size_t n = scr_crypto_hash_finish(hash, digest);
|
|
4877
5753
|
if (n == 0) return scr_exc_pending() ? NULL : scr_str_new("", 0);
|
|
4878
5754
|
ScrStr *result = scr_digest_encode(digest, n, enc);
|
|
@@ -4881,7 +5757,7 @@ ScrStr *scr_crypto_hash_digest_string(ScrCryptoHash *hash, ScrStr *enc) {
|
|
|
4881
5757
|
}
|
|
4882
5758
|
|
|
4883
5759
|
ScrBytes *scr_crypto_hash_digest_buffer(ScrCryptoHash *hash) {
|
|
4884
|
-
unsigned char digest[
|
|
5760
|
+
unsigned char digest[64];
|
|
4885
5761
|
size_t n = scr_crypto_hash_finish(hash, digest);
|
|
4886
5762
|
if (n == 0) return scr_exc_pending() ? NULL : scr_bytes_new(SCR_BYTES_U8, 0);
|
|
4887
5763
|
ScrBytes *result = scr_bytes_new(SCR_BYTES_U8, (double)n);
|
|
@@ -4897,7 +5773,7 @@ static ScrStr *scr_hash_digest_raw(const ScrStr *alg, const unsigned char *data,
|
|
|
4897
5773
|
scr_crypto_hash_digest_unsupported(alg);
|
|
4898
5774
|
return NULL;
|
|
4899
5775
|
}
|
|
4900
|
-
unsigned char d[
|
|
5776
|
+
unsigned char d[64];
|
|
4901
5777
|
ScrDigestCtx ctx;
|
|
4902
5778
|
scr_digest_init(&ctx, kind);
|
|
4903
5779
|
scr_digest_update(&ctx, data, len);
|
|
@@ -4934,7 +5810,7 @@ bool scr_crypto_timing_safe_equal(ScrBytes *left, ScrBytes *right) {
|
|
|
4934
5810
|
ScrBytes *scr_crypto_random_fill(ScrBytes *bytes, double offset, double size) {
|
|
4935
5811
|
double length = (double)(bytes->len * scr_bytes_elem_size(bytes->elem));
|
|
4936
5812
|
if (!isfinite(offset) || floor(offset) != offset || offset < 0 || offset > length) {
|
|
4937
|
-
char value[
|
|
5813
|
+
char value[64], msg[160];
|
|
4938
5814
|
size_t value_len = scr_f64_to_str(offset, value);
|
|
4939
5815
|
int n = snprintf(msg, sizeof msg,
|
|
4940
5816
|
"The value of \"offset\" is out of range. It must be >= 0 && <= %.0f. Received %.*s",
|
|
@@ -4944,7 +5820,7 @@ ScrBytes *scr_crypto_random_fill(ScrBytes *bytes, double offset, double size) {
|
|
|
4944
5820
|
}
|
|
4945
5821
|
double available = length - offset;
|
|
4946
5822
|
if (!isfinite(size) || floor(size) != size || size < 0 || size > 2147483647.0) {
|
|
4947
|
-
char value[
|
|
5823
|
+
char value[64], msg[160];
|
|
4948
5824
|
size_t value_len = scr_f64_to_str(size, value);
|
|
4949
5825
|
int n = snprintf(msg, sizeof msg,
|
|
4950
5826
|
"The value of \"size\" is out of range. It must be >= 0 && <= 2147483647. Received %.*s",
|
|
@@ -4953,7 +5829,7 @@ ScrBytes *scr_crypto_random_fill(ScrBytes *bytes, double offset, double size) {
|
|
|
4953
5829
|
return NULL;
|
|
4954
5830
|
}
|
|
4955
5831
|
if (size > available) {
|
|
4956
|
-
char value[
|
|
5832
|
+
char value[64], msg[160];
|
|
4957
5833
|
double total = size + offset;
|
|
4958
5834
|
size_t value_len = scr_f64_to_str(total, value);
|
|
4959
5835
|
int n = snprintf(msg, sizeof msg,
|
|
@@ -4983,7 +5859,7 @@ double scr_crypto_random_int(double min, double max) {
|
|
|
4983
5859
|
return 0;
|
|
4984
5860
|
}
|
|
4985
5861
|
if (max <= min) {
|
|
4986
|
-
char min_text[
|
|
5862
|
+
char min_text[64], max_text[64], msg[192];
|
|
4987
5863
|
size_t min_len = scr_f64_to_str(min, min_text);
|
|
4988
5864
|
size_t max_len = scr_f64_to_str(max, max_text);
|
|
4989
5865
|
int n = snprintf(msg, sizeof msg,
|
|
@@ -5015,7 +5891,7 @@ static ScrBytes *scr_crypto_pbkdf2_raw(const unsigned char *password, size_t pas
|
|
|
5015
5891
|
uint32_t iterations, size_t keylen,
|
|
5016
5892
|
ScrDigestAlg alg) {
|
|
5017
5893
|
ScrDigestCtx empty;
|
|
5018
|
-
unsigned char empty_digest[
|
|
5894
|
+
unsigned char empty_digest[64];
|
|
5019
5895
|
scr_digest_init(&empty, alg);
|
|
5020
5896
|
size_t digest_len = scr_digest_final(&empty, empty_digest);
|
|
5021
5897
|
scr_crypto_zero(empty_digest, sizeof empty_digest);
|
|
@@ -5030,7 +5906,7 @@ static ScrBytes *scr_crypto_pbkdf2_raw(const unsigned char *password, size_t pas
|
|
|
5030
5906
|
block[salt_len + 2] = (unsigned char)(index >> 8);
|
|
5031
5907
|
block[salt_len + 3] = (unsigned char)index;
|
|
5032
5908
|
ScrDigestCtx inner, outer;
|
|
5033
|
-
unsigned char u[
|
|
5909
|
+
unsigned char u[64], accum[64];
|
|
5034
5910
|
scr_hmac_init(&inner, &outer, alg, password, password_len);
|
|
5035
5911
|
scr_digest_update(&inner, block, salt_len + 4);
|
|
5036
5912
|
scr_hmac_final(&inner, &outer, u);
|
|
@@ -5058,7 +5934,7 @@ ScrBytes *scr_crypto_pbkdf2(ScrBytes *password, ScrBytes *salt,
|
|
|
5058
5934
|
double iterations, double keylen, ScrStr *digest) {
|
|
5059
5935
|
if (!isfinite(iterations) || floor(iterations) != iterations ||
|
|
5060
5936
|
iterations < 1 || iterations > 2147483647.0) {
|
|
5061
|
-
char value[
|
|
5937
|
+
char value[64], msg[176];
|
|
5062
5938
|
size_t value_len = scr_f64_to_str(iterations, value);
|
|
5063
5939
|
int n = snprintf(msg, sizeof msg,
|
|
5064
5940
|
"The value of \"iterations\" is out of range. It must be >= 1 && <= 2147483647. Received %.*s",
|
|
@@ -5067,7 +5943,7 @@ ScrBytes *scr_crypto_pbkdf2(ScrBytes *password, ScrBytes *salt,
|
|
|
5067
5943
|
return NULL;
|
|
5068
5944
|
}
|
|
5069
5945
|
if (!isfinite(keylen) || floor(keylen) != keylen || keylen < 0 || keylen > 2147483647.0) {
|
|
5070
|
-
char value[
|
|
5946
|
+
char value[64], msg[176];
|
|
5071
5947
|
size_t value_len = scr_f64_to_str(keylen, value);
|
|
5072
5948
|
int n = snprintf(msg, sizeof msg,
|
|
5073
5949
|
"The value of \"keylen\" is out of range. It must be >= 0 && <= 2147483647. Received %.*s",
|
|
@@ -5086,6 +5962,279 @@ ScrBytes *scr_crypto_pbkdf2(ScrBytes *password, ScrBytes *salt,
|
|
|
5086
5962
|
(size_t)keylen, alg);
|
|
5087
5963
|
}
|
|
5088
5964
|
|
|
5965
|
+
/* RFC 5869 HKDF and RFC 7914 scrypt share the digest core in both
|
|
5966
|
+
* execution tiers. Sensitive scratch storage is cleared before release. */
|
|
5967
|
+
static bool scr_crypto_integer(double value, const char *name, double max) {
|
|
5968
|
+
if (isfinite(value) && floor(value) == value && value >= 0 && value <= max) return true;
|
|
5969
|
+
char received[64], requirement[80], message[256];
|
|
5970
|
+
size_t n = scr_f64_to_str(value, received);
|
|
5971
|
+
received[n] = '\0';
|
|
5972
|
+
if (!isfinite(value) || floor(value) != value) {
|
|
5973
|
+
snprintf(requirement, sizeof requirement, "an integer");
|
|
5974
|
+
} else {
|
|
5975
|
+
snprintf(requirement, sizeof requirement, ">= 0 && <= %.0f", max);
|
|
5976
|
+
if (fabs(value) > 4294967296.0) {
|
|
5977
|
+
char digits[40];
|
|
5978
|
+
snprintf(digits, sizeof digits, "%.0f", value);
|
|
5979
|
+
size_t len = strlen(digits), out = 0, start = digits[0] == '-' ? 1 : 0;
|
|
5980
|
+
for (size_t i = 0; i < len; i++) {
|
|
5981
|
+
if (i > start && (len - i) % 3 == 0) received[out++] = '_';
|
|
5982
|
+
received[out++] = digits[i];
|
|
5983
|
+
}
|
|
5984
|
+
received[out] = '\0';
|
|
5985
|
+
}
|
|
5986
|
+
}
|
|
5987
|
+
int len = snprintf(message, sizeof message,
|
|
5988
|
+
"The value of \"%s\" is out of range. It must be %s. Received %s", name, requirement, received);
|
|
5989
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, message, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
5990
|
+
return false;
|
|
5991
|
+
}
|
|
5992
|
+
|
|
5993
|
+
ScrBytes *scr_crypto_hkdf_bytes(ScrStr *digest, ScrBytes *ikm, ScrBytes *salt,
|
|
5994
|
+
ScrBytes *info, double keylen) {
|
|
5995
|
+
if (!scr_crypto_integer(keylen, "length", 9007199254740991.0)) return NULL;
|
|
5996
|
+
size_t info_len = info->len * scr_bytes_elem_size(info->elem);
|
|
5997
|
+
if (info_len > 1024) {
|
|
5998
|
+
char message[192];
|
|
5999
|
+
int len = snprintf(message, sizeof message,
|
|
6000
|
+
"The value of \"info\" is out of range. It must be must not contain more than 1024 bytes. Received %zu", info_len);
|
|
6001
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, message, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
6002
|
+
return NULL;
|
|
6003
|
+
}
|
|
6004
|
+
ScrDigestAlg alg;
|
|
6005
|
+
if (!scr_digest_alg(digest->data, digest->len, &alg)) {
|
|
6006
|
+
scr_crypto_invalid_digest(digest);
|
|
6007
|
+
return NULL;
|
|
6008
|
+
}
|
|
6009
|
+
ScrDigestCtx inner, outer;
|
|
6010
|
+
unsigned char prk[64], t[64];
|
|
6011
|
+
scr_hmac_init(&inner, &outer, alg, salt->data, salt->len * scr_bytes_elem_size(salt->elem));
|
|
6012
|
+
scr_digest_update(&inner, ikm->data, ikm->len * scr_bytes_elem_size(ikm->elem));
|
|
6013
|
+
size_t hash_len = scr_hmac_final(&inner, &outer, prk);
|
|
6014
|
+
if (keylen > 255 * hash_len) {
|
|
6015
|
+
scr_crypto_zero(prk, sizeof prk);
|
|
6016
|
+
scr_crypto_zero(&inner, sizeof inner);
|
|
6017
|
+
scr_crypto_zero(&outer, sizeof outer);
|
|
6018
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, "Invalid key length", 18, "ERR_CRYPTO_INVALID_KEYLEN");
|
|
6019
|
+
return NULL;
|
|
6020
|
+
}
|
|
6021
|
+
ScrBytes *result = scr_bytes_new(SCR_BYTES_U8, keylen);
|
|
6022
|
+
size_t offset = 0, previous_len = 0;
|
|
6023
|
+
for (unsigned counter = 1; offset < (size_t)keylen; counter++) {
|
|
6024
|
+
scr_hmac_init(&inner, &outer, alg, prk, hash_len);
|
|
6025
|
+
scr_digest_update(&inner, t, previous_len);
|
|
6026
|
+
scr_digest_update(&inner, info->data, info_len);
|
|
6027
|
+
unsigned char byte = (unsigned char)counter;
|
|
6028
|
+
scr_digest_update(&inner, &byte, 1);
|
|
6029
|
+
scr_hmac_final(&inner, &outer, t);
|
|
6030
|
+
size_t take = (size_t)keylen - offset;
|
|
6031
|
+
if (take > hash_len) take = hash_len;
|
|
6032
|
+
memcpy(result->data + offset, t, take);
|
|
6033
|
+
offset += take;
|
|
6034
|
+
previous_len = hash_len;
|
|
6035
|
+
}
|
|
6036
|
+
scr_crypto_zero(prk, sizeof prk);
|
|
6037
|
+
scr_crypto_zero(t, sizeof t);
|
|
6038
|
+
scr_crypto_zero(&inner, sizeof inner);
|
|
6039
|
+
scr_crypto_zero(&outer, sizeof outer);
|
|
6040
|
+
return result;
|
|
6041
|
+
}
|
|
6042
|
+
|
|
6043
|
+
ScrDyn *scr_crypto_hkdf(ScrStr *digest, ScrBytes *ikm, ScrBytes *salt,
|
|
6044
|
+
ScrBytes *info, double keylen) {
|
|
6045
|
+
ScrBytes *bytes = scr_crypto_hkdf_bytes(digest, ikm, salt, info, keylen);
|
|
6046
|
+
if (!bytes) return NULL;
|
|
6047
|
+
if (bytes->len == 0) {
|
|
6048
|
+
scr_bytes_release(bytes);
|
|
6049
|
+
scr_throw_error_msg(SCR_ERR_ERROR, "Deriving bits failed", 20);
|
|
6050
|
+
return NULL;
|
|
6051
|
+
}
|
|
6052
|
+
ScrDyn *result = scr_array_buffer_from_bytes(bytes);
|
|
6053
|
+
scr_bytes_release(bytes);
|
|
6054
|
+
return result;
|
|
6055
|
+
}
|
|
6056
|
+
|
|
6057
|
+
static uint32_t scr_scrypt_rotl(uint32_t x, unsigned n) {
|
|
6058
|
+
return (x << n) | (x >> (32 - n));
|
|
6059
|
+
}
|
|
6060
|
+
|
|
6061
|
+
/* Salsa20/8 core, RFC 7914 section 3. Unsigned arithmetic supplies the
|
|
6062
|
+
* specified reduction modulo 2^32 without signed-overflow UB. */
|
|
6063
|
+
static void scr_scrypt_salsa(uint32_t block[16]) {
|
|
6064
|
+
uint32_t x[16];
|
|
6065
|
+
memcpy(x, block, sizeof x);
|
|
6066
|
+
for (unsigned round = 0; round < 8; round += 2) {
|
|
6067
|
+
#define SCR_SALSA(a, b, c, n) x[a] ^= scr_scrypt_rotl(x[b] + x[c], n)
|
|
6068
|
+
SCR_SALSA(4,0,12,7); SCR_SALSA(8,4,0,9); SCR_SALSA(12,8,4,13); SCR_SALSA(0,12,8,18);
|
|
6069
|
+
SCR_SALSA(9,5,1,7); SCR_SALSA(13,9,5,9); SCR_SALSA(1,13,9,13); SCR_SALSA(5,1,13,18);
|
|
6070
|
+
SCR_SALSA(14,10,6,7); SCR_SALSA(2,14,10,9); SCR_SALSA(6,2,14,13); SCR_SALSA(10,6,2,18);
|
|
6071
|
+
SCR_SALSA(3,15,11,7); SCR_SALSA(7,3,15,9); SCR_SALSA(11,7,3,13); SCR_SALSA(15,11,7,18);
|
|
6072
|
+
SCR_SALSA(1,0,3,7); SCR_SALSA(2,1,0,9); SCR_SALSA(3,2,1,13); SCR_SALSA(0,3,2,18);
|
|
6073
|
+
SCR_SALSA(6,5,4,7); SCR_SALSA(7,6,5,9); SCR_SALSA(4,7,6,13); SCR_SALSA(5,4,7,18);
|
|
6074
|
+
SCR_SALSA(11,10,9,7); SCR_SALSA(8,11,10,9); SCR_SALSA(9,8,11,13); SCR_SALSA(10,9,8,18);
|
|
6075
|
+
SCR_SALSA(12,15,14,7); SCR_SALSA(13,12,15,9); SCR_SALSA(14,13,12,13); SCR_SALSA(15,14,13,18);
|
|
6076
|
+
#undef SCR_SALSA
|
|
6077
|
+
}
|
|
6078
|
+
for (size_t i = 0; i < 16; i++) block[i] += x[i];
|
|
6079
|
+
scr_crypto_zero(x, sizeof x);
|
|
6080
|
+
}
|
|
6081
|
+
|
|
6082
|
+
static void scr_scrypt_blockmix(uint32_t *x, uint32_t *y, size_t r) {
|
|
6083
|
+
uint32_t block[16];
|
|
6084
|
+
memcpy(block, x + (2 * r - 1) * 16, sizeof block);
|
|
6085
|
+
for (size_t i = 0; i < 2 * r; i++) {
|
|
6086
|
+
for (size_t k = 0; k < 16; k++) block[k] ^= x[i * 16 + k];
|
|
6087
|
+
scr_scrypt_salsa(block);
|
|
6088
|
+
size_t dest = (i / 2 + (i % 2) * r) * 16;
|
|
6089
|
+
memcpy(y + dest, block, sizeof block);
|
|
6090
|
+
}
|
|
6091
|
+
memcpy(x, y, 128 * r);
|
|
6092
|
+
scr_crypto_zero(block, sizeof block);
|
|
6093
|
+
}
|
|
6094
|
+
|
|
6095
|
+
static bool scr_scrypt_params(double n, double r, double p, double maxmem, size_t *memory) {
|
|
6096
|
+
uint64_t N = (uint64_t)n, R = (uint64_t)r, P = (uint64_t)p;
|
|
6097
|
+
if (N < 2 || (N & (N - 1)) != 0) {
|
|
6098
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, "Invalid scrypt params", 21, "ERR_CRYPTO_INVALID_SCRYPT_PARAMS");
|
|
6099
|
+
return false;
|
|
6100
|
+
}
|
|
6101
|
+
/* Match Node's OpenSSL 3 scrypt guard, including X/Y working buffers
|
|
6102
|
+
* and the initial PBKDF2 output; check every product before allocation. */
|
|
6103
|
+
bool overflow = R == 0 || P == 0 || P > UINT64_C(0x3fffffff) / R;
|
|
6104
|
+
if (!overflow && R <= 3 && N >= (UINT64_C(1) << (16 * R))) overflow = true;
|
|
6105
|
+
uint64_t b = overflow ? 0 : 128 * R * P;
|
|
6106
|
+
if (b > INT32_MAX || R > UINT64_MAX / 128 / (N + 2)) overflow = true;
|
|
6107
|
+
uint64_t v = overflow ? 0 : 128 * R * (N + 2);
|
|
6108
|
+
if (overflow || b > UINT64_MAX - v || b + v > (uint64_t)maxmem || b + v > SIZE_MAX) {
|
|
6109
|
+
static const char message[] = "Invalid scrypt params: error:030000AC:digital envelope routines::memory limit exceeded";
|
|
6110
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, message, sizeof message - 1, "ERR_CRYPTO_INVALID_SCRYPT_PARAMS");
|
|
6111
|
+
return false;
|
|
6112
|
+
}
|
|
6113
|
+
*memory = (size_t)(b + v);
|
|
6114
|
+
return true;
|
|
6115
|
+
}
|
|
6116
|
+
|
|
6117
|
+
ScrBytes *scr_crypto_scrypt_derive(ScrBytes *password, ScrBytes *salt, double keylen,
|
|
6118
|
+
double n, double r, double p, double maxmem) {
|
|
6119
|
+
if (!scr_crypto_integer(keylen, "keylen", 2147483647.0) ||
|
|
6120
|
+
!scr_crypto_integer(n, "N", 4294967295.0) || !scr_crypto_integer(r, "r", 4294967295.0) ||
|
|
6121
|
+
!scr_crypto_integer(p, "p", 4294967295.0) || !scr_crypto_integer(maxmem, "maxmem", 9007199254740991.0)) return NULL;
|
|
6122
|
+
if (n == 0) n = 16384;
|
|
6123
|
+
if (r == 0) r = 8;
|
|
6124
|
+
if (p == 0) p = 1;
|
|
6125
|
+
if (maxmem == 0) maxmem = 33554432;
|
|
6126
|
+
size_t memory;
|
|
6127
|
+
if (!scr_scrypt_params(n, r, p, maxmem, &memory)) return NULL;
|
|
6128
|
+
if (keylen == 0) return scr_bytes_new(SCR_BYTES_U8, 0);
|
|
6129
|
+
size_t words = 32 * (size_t)r, block_len = words * sizeof(uint32_t);
|
|
6130
|
+
size_t b_len = block_len * (size_t)p;
|
|
6131
|
+
ScrBytes *b = scr_crypto_pbkdf2_raw(password->data, password->len * scr_bytes_elem_size(password->elem),
|
|
6132
|
+
salt->data, salt->len * scr_bytes_elem_size(salt->elem), 1, b_len, SCR_DIGEST_SHA256);
|
|
6133
|
+
uint32_t *work = malloc(memory - b_len);
|
|
6134
|
+
if (!work) { scr_bytes_release(b); scr_trap("scriptc: out of memory\n"); }
|
|
6135
|
+
uint32_t *x = work, *y = x + words, *v = y + words;
|
|
6136
|
+
for (size_t part = 0; part < (size_t)p; part++) {
|
|
6137
|
+
unsigned char *bytes = b->data + part * block_len;
|
|
6138
|
+
for (size_t i = 0; i < words; i++) {
|
|
6139
|
+
x[i] = (uint32_t)bytes[i * 4] | ((uint32_t)bytes[i * 4 + 1] << 8) |
|
|
6140
|
+
((uint32_t)bytes[i * 4 + 2] << 16) | ((uint32_t)bytes[i * 4 + 3] << 24);
|
|
6141
|
+
}
|
|
6142
|
+
for (size_t i = 0; i < (size_t)n; i++) {
|
|
6143
|
+
memcpy(v + i * words, x, block_len);
|
|
6144
|
+
scr_scrypt_blockmix(x, y, (size_t)r);
|
|
6145
|
+
}
|
|
6146
|
+
for (size_t i = 0; i < (size_t)n; i++) {
|
|
6147
|
+
size_t j = (size_t)x[words - 16] & ((size_t)n - 1);
|
|
6148
|
+
for (size_t k = 0; k < words; k++) x[k] ^= v[j * words + k];
|
|
6149
|
+
scr_scrypt_blockmix(x, y, (size_t)r);
|
|
6150
|
+
}
|
|
6151
|
+
for (size_t i = 0; i < words; i++) {
|
|
6152
|
+
for (size_t k = 0; k < 4; k++) bytes[i * 4 + k] = (unsigned char)(x[i] >> (8 * k));
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
ScrBytes *result = scr_crypto_pbkdf2_raw(password->data, password->len * scr_bytes_elem_size(password->elem),
|
|
6156
|
+
b->data, b_len, 1, (size_t)keylen, SCR_DIGEST_SHA256);
|
|
6157
|
+
scr_crypto_zero(b->data, b_len);
|
|
6158
|
+
scr_bytes_release(b);
|
|
6159
|
+
scr_crypto_zero(work, memory - b_len);
|
|
6160
|
+
free(work);
|
|
6161
|
+
return result;
|
|
6162
|
+
}
|
|
6163
|
+
|
|
6164
|
+
static ScrDyn *scr_scrypt_read_option(ScrDyn *options, const char *name) {
|
|
6165
|
+
if (options->kind != SCR_DYN_TYPED_REF) return scr_dyn_obj_read(options, name, strlen(name));
|
|
6166
|
+
ScrDyn *view = scr_dyn_typed_ref_materialize(options);
|
|
6167
|
+
if (!view) return NULL;
|
|
6168
|
+
ScrDyn *value = scr_dyn_obj_read(view, name, strlen(name));
|
|
6169
|
+
scr_dyn_release(view);
|
|
6170
|
+
return value;
|
|
6171
|
+
}
|
|
6172
|
+
|
|
6173
|
+
static bool scr_scrypt_option(ScrDyn *options, const char *name, double max, double *out, bool *present) {
|
|
6174
|
+
ScrDyn *value = scr_scrypt_read_option(options, name);
|
|
6175
|
+
if (scr_exc_pending()) return false;
|
|
6176
|
+
*present = value && value->kind != SCR_DYN_UNDEF;
|
|
6177
|
+
if (*present) {
|
|
6178
|
+
scr_dyn_release(value);
|
|
6179
|
+
value = scr_scrypt_read_option(options, name);
|
|
6180
|
+
if (scr_exc_pending()) return false;
|
|
6181
|
+
if (value->kind != SCR_DYN_NUM) {
|
|
6182
|
+
scr_dyn_arg_type_fail(name, "number", value);
|
|
6183
|
+
scr_dyn_release(value);
|
|
6184
|
+
return false;
|
|
6185
|
+
}
|
|
6186
|
+
*out = value->v.num;
|
|
6187
|
+
bool valid = scr_crypto_integer(*out, name, max);
|
|
6188
|
+
scr_dyn_release(value);
|
|
6189
|
+
return valid;
|
|
6190
|
+
}
|
|
6191
|
+
scr_dyn_release(value);
|
|
6192
|
+
return true;
|
|
6193
|
+
}
|
|
6194
|
+
|
|
6195
|
+
ScrBytes *scr_crypto_scrypt(ScrBytes *password, ScrBytes *salt, double keylen, ScrDyn *options) {
|
|
6196
|
+
if (!scr_crypto_integer(keylen, "keylen", 2147483647.0)) return NULL;
|
|
6197
|
+
double values[4] = {16384, 8, 1, 33554432};
|
|
6198
|
+
if (options && scr_dyn_truthy(options)) {
|
|
6199
|
+
const char *names[3] = {"N", "r", "p"};
|
|
6200
|
+
const char *aliases[3] = {"cost", "blockSize", "parallelization"};
|
|
6201
|
+
for (size_t i = 0; i < 3; i++) {
|
|
6202
|
+
bool primary, alias;
|
|
6203
|
+
if (!scr_scrypt_option(options, names[i], 4294967295.0, &values[i], &primary)) return NULL;
|
|
6204
|
+
// Node reads the alias to test presence before checking the conflict.
|
|
6205
|
+
ScrDyn *alias_value = scr_scrypt_read_option(options, aliases[i]);
|
|
6206
|
+
if (scr_exc_pending()) return NULL;
|
|
6207
|
+
alias = alias_value && alias_value->kind != SCR_DYN_UNDEF;
|
|
6208
|
+
if (primary && alias) {
|
|
6209
|
+
char message[160];
|
|
6210
|
+
int len = snprintf(message, sizeof message, "Option \"%s\" cannot be used in combination with option \"%s\"", names[i], aliases[i]);
|
|
6211
|
+
scr_dyn_release(alias_value);
|
|
6212
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, message, (size_t)len, "ERR_INCOMPATIBLE_OPTION_PAIR");
|
|
6213
|
+
return NULL;
|
|
6214
|
+
}
|
|
6215
|
+
if (alias) {
|
|
6216
|
+
scr_dyn_release(alias_value);
|
|
6217
|
+
alias_value = scr_scrypt_read_option(options, aliases[i]);
|
|
6218
|
+
if (scr_exc_pending()) return NULL;
|
|
6219
|
+
if (alias_value->kind != SCR_DYN_NUM) {
|
|
6220
|
+
scr_dyn_arg_type_fail(aliases[i], "number", alias_value);
|
|
6221
|
+
scr_dyn_release(alias_value);
|
|
6222
|
+
return NULL;
|
|
6223
|
+
}
|
|
6224
|
+
values[i] = alias_value->v.num;
|
|
6225
|
+
if (!scr_crypto_integer(values[i], aliases[i], 4294967295.0)) {
|
|
6226
|
+
scr_dyn_release(alias_value);
|
|
6227
|
+
return NULL;
|
|
6228
|
+
}
|
|
6229
|
+
}
|
|
6230
|
+
scr_dyn_release(alias_value);
|
|
6231
|
+
}
|
|
6232
|
+
bool present;
|
|
6233
|
+
if (!scr_scrypt_option(options, "maxmem", 9007199254740991.0, &values[3], &present)) return NULL;
|
|
6234
|
+
}
|
|
6235
|
+
return scr_crypto_scrypt_derive(password, salt, keylen, values[0], values[1], values[2], values[3]);
|
|
6236
|
+
}
|
|
6237
|
+
|
|
5089
6238
|
/* The composed `new crypto.X509Certificate(data).fingerprint` read, fused
|
|
5090
6239
|
* by the compiler (no certificate handle exists). Node's .fingerprint IS
|
|
5091
6240
|
* the SHA-1 of the certificate's DER bytes, uppercase colon-separated —
|