ocran 1.4.5 → 1.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.txt +74 -0
- data/README.md +296 -9
- data/ext/extconf.rb +5 -1
- data/lib/ocran/aot_toolchain.rb +154 -0
- data/lib/ocran/command_output.rb +8 -1
- data/lib/ocran/compatibility_scan.rb +414 -0
- data/lib/ocran/cosmo_toolchain.rb +29 -7
- data/lib/ocran/dir_builder.rb +88 -38
- data/lib/ocran/direction.rb +232 -33
- data/lib/ocran/ed_resource.rb +113 -0
- data/lib/ocran/ed_resource_builder.rb +199 -0
- data/lib/ocran/file_path_set.rb +9 -5
- data/lib/ocran/gem_spec_queryable.rb +7 -3
- data/lib/ocran/host_config_helper.rb +11 -1
- data/lib/ocran/inno_setup_script_builder.rb +24 -14
- data/lib/ocran/launcher_batch_builder.rb +8 -11
- data/lib/ocran/npm_package.rb +106 -0
- data/lib/ocran/option.rb +433 -35
- data/lib/ocran/pe_imports.rb +96 -0
- data/lib/ocran/pe_resources.rb +226 -0
- data/lib/ocran/refine_pathname.rb +29 -32
- data/lib/ocran/roundhouse_builder.rb +316 -0
- data/lib/ocran/runner.rb +96 -11
- data/lib/ocran/runtime_environment.rb +14 -6
- data/lib/ocran/runtime_sdk.rb +252 -0
- data/lib/ocran/spinel_builder.rb +183 -0
- data/lib/ocran/spinel_compatibility.rb +149 -0
- data/lib/ocran/static_runtime/boot.rb +443 -0
- data/lib/ocran/static_runtime/build_static_ext.rb +86 -0
- data/lib/ocran/static_runtime_builder.rb +814 -0
- data/lib/ocran/stub_builder.rb +70 -25
- data/lib/ocran/version.rb +1 -1
- data/lib/ocran/wasm_builder.rb +570 -0
- data/lib/ocran/wasm_compatibility.rb +198 -0
- data/lib/ocran/windows_command_escaping.rb +21 -0
- data/lib/ocran/zip_payload_builder.rb +18 -9
- data/lib/ocran/zip_writer.rb +48 -9
- data/src/Makefile +18 -2
- data/src/Makefile.mswin +65 -0
- data/src/inst_dir.c +34 -6
- data/src/path_utils.c +144 -0
- data/src/script_info.c +5 -0
- data/src/stub.c +77 -19
- data/src/stub.rc +1 -1
- data/src/system_utils.c +276 -155
- data/src/system_utils.h +46 -3
- data/src/system_utils_posix.c +243 -148
- data/src/unpack.c +9 -0
- metadata +23 -5
data/src/path_utils.c
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Path string helpers shared by the Windows (system_utils.c) and POSIX
|
|
3
|
+
* (system_utils_posix.c) builds. Pure string manipulation: nothing here
|
|
4
|
+
* touches the file system.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#include <stdbool.h>
|
|
8
|
+
#include <stdlib.h>
|
|
9
|
+
#include <string.h>
|
|
10
|
+
#ifdef __COSMOPOLITAN__
|
|
11
|
+
#include <cosmo.h> /* IsWindows() */
|
|
12
|
+
#endif
|
|
13
|
+
#include "error.h"
|
|
14
|
+
#include "system_utils.h"
|
|
15
|
+
|
|
16
|
+
bool IsCleanRelativePath(const char *path)
|
|
17
|
+
{
|
|
18
|
+
if (!path || !*path) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#ifdef _WIN32
|
|
23
|
+
/* Forbid Windows drive specification (e.g. "C:\") */
|
|
24
|
+
if (((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
|
|
25
|
+
&& path[1] == ':'
|
|
26
|
+
&& is_path_separator(path[2])) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
#endif
|
|
30
|
+
|
|
31
|
+
/* Forbid absolute path (leading '/' or '\') */
|
|
32
|
+
if (is_path_separator(*path)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#ifdef __COSMOPOLITAN__
|
|
37
|
+
/* A cosmopolitan stub splits paths at '/' only, but on Windows the
|
|
38
|
+
path ends up with the Win32 API, which splits at '\' as well: a
|
|
39
|
+
name like "..\x" would pass below as a single segment and then
|
|
40
|
+
climb out of the extraction directory. The packer never emits a
|
|
41
|
+
backslash in a name, so refuse all of them there. */
|
|
42
|
+
if (IsWindows() && strchr(path, '\\')) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
#endif
|
|
46
|
+
|
|
47
|
+
/* Validate each path segment */
|
|
48
|
+
const char *p = path;
|
|
49
|
+
while (*p) {
|
|
50
|
+
const char *start = p;
|
|
51
|
+
|
|
52
|
+
/* Advance until next separator or end-of-string */
|
|
53
|
+
while (*p && !is_path_separator(*p)) {
|
|
54
|
+
p++;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
size_t len = p - start;
|
|
58
|
+
|
|
59
|
+
/* Reject empty, "." or ".." segments */
|
|
60
|
+
if (len == 0
|
|
61
|
+
|| (len == 1 && start[0] == '.')
|
|
62
|
+
|| (len == 2 && start[0] == '.' && start[1] == '.')) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Skip over the separator */
|
|
67
|
+
if (*p) {
|
|
68
|
+
p++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Combines two file path components into a single path, handling path separators.
|
|
76
|
+
char *JoinPath(const char *p1, const char *p2)
|
|
77
|
+
{
|
|
78
|
+
if (p1 == NULL || *p1 == '\0') {
|
|
79
|
+
APP_ERROR("p1 is null or empty");
|
|
80
|
+
return NULL;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (p2 == NULL || *p2 == '\0') {
|
|
84
|
+
APP_ERROR("p2 is null or empty");
|
|
85
|
+
return NULL;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
size_t p1_len = strlen(p1);
|
|
89
|
+
if (is_path_separator(p1[p1_len - 1])) { p1_len--; }
|
|
90
|
+
|
|
91
|
+
size_t p2_len = strlen(p2);
|
|
92
|
+
const char *p2_start = p2;
|
|
93
|
+
if (is_path_separator(*p2_start)) { p2_start++; p2_len--; }
|
|
94
|
+
|
|
95
|
+
size_t joined_len = p1_len + 1 + p2_len;
|
|
96
|
+
char *joined_path = calloc(1, joined_len + 1);
|
|
97
|
+
if (!joined_path) {
|
|
98
|
+
APP_ERROR("Failed to allocate buffer for join path");
|
|
99
|
+
return NULL;
|
|
100
|
+
}
|
|
101
|
+
memcpy(joined_path, p1, p1_len);
|
|
102
|
+
joined_path[p1_len] = PATH_SEPARATOR;
|
|
103
|
+
memcpy(joined_path + p1_len + 1, p2_start, p2_len);
|
|
104
|
+
joined_path[joined_len] = '\0';
|
|
105
|
+
|
|
106
|
+
return joined_path;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
char *GetParentPath(const char *path)
|
|
110
|
+
{
|
|
111
|
+
if (!path) {
|
|
112
|
+
APP_ERROR("path is NULL");
|
|
113
|
+
return NULL;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
size_t root = path_root_length(path);
|
|
117
|
+
size_t i = strlen(path);
|
|
118
|
+
|
|
119
|
+
/* Skip any trailing separators */
|
|
120
|
+
while (i > root && is_path_separator(path[i - 1])) {
|
|
121
|
+
i--;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Skip the last segment's characters */
|
|
125
|
+
while (i > root && !is_path_separator(path[i - 1])) {
|
|
126
|
+
i--;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Skip the separators before it, keeping the root ("/" or "C:\") */
|
|
130
|
+
while (i > root && is_path_separator(path[i - 1])) {
|
|
131
|
+
i--;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* i==0 ⇒ empty parent (relative path with a single segment) */
|
|
135
|
+
|
|
136
|
+
char *out = malloc(i + 1);
|
|
137
|
+
if (!out) {
|
|
138
|
+
APP_ERROR("Memory allocation failed for parent path");
|
|
139
|
+
return NULL;
|
|
140
|
+
}
|
|
141
|
+
memcpy(out, path, i);
|
|
142
|
+
out[i] = '\0';
|
|
143
|
+
return out;
|
|
144
|
+
}
|
data/src/script_info.c
CHANGED
|
@@ -183,6 +183,11 @@ bool RunScript(char *argv[], bool is_chdir_to_script_dir,
|
|
|
183
183
|
char **merged_argv = NULL;
|
|
184
184
|
char *script_dir = NULL;
|
|
185
185
|
|
|
186
|
+
if (!script_info) {
|
|
187
|
+
APP_ERROR("Failed to copy the script info");
|
|
188
|
+
goto cleanup;
|
|
189
|
+
}
|
|
190
|
+
|
|
186
191
|
app_name = ExpandInstDirPath(script_info[0]);
|
|
187
192
|
if (!app_name) {
|
|
188
193
|
APP_ERROR("Failed to expand application name to installation directory");
|
data/src/stub.c
CHANGED
|
@@ -18,11 +18,48 @@
|
|
|
18
18
|
#include "script_info.h"
|
|
19
19
|
#include "unpack.h"
|
|
20
20
|
|
|
21
|
+
/* Operation modes from the header of the packed data; 0 until read. */
|
|
22
|
+
static OperationModes OpModes = 0;
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
Deletes the extraction directory when AUTO_CLEAN_INST_DIR is set. Runs
|
|
26
|
+
once, through RunCleanupRoutine(): at the end of main(), or on Windows
|
|
27
|
+
from the console control handler when the console window is closed or
|
|
28
|
+
the system shuts down - possibly on another thread, at any point of
|
|
29
|
+
main().
|
|
30
|
+
*/
|
|
31
|
+
static void delete_extraction_dir(void)
|
|
32
|
+
{
|
|
33
|
+
/* Never delete in RUN_IN_EXE_DIR mode: the "installation directory"
|
|
34
|
+
is the real application directory, not a temporary extraction dir. */
|
|
35
|
+
if (!IsAutoCleanInstDir(OpModes) || IsRunInExeDir(OpModes)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const char *dir = GetInstDir();
|
|
40
|
+
if (!dir) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
DEBUG("Deleting extraction directory: %s", dir);
|
|
45
|
+
if (!DeleteInstDir()) {
|
|
46
|
+
DEBUG("Failed to delete extraction directory");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/* Whether an environment variable asks for something: set, and neither
|
|
52
|
+
empty nor "0". OCRAN_DEBUG=0 used to switch debug output on. */
|
|
53
|
+
static bool IsEnvFlagSet(const char *name)
|
|
54
|
+
{
|
|
55
|
+
const char *value = getenv(name);
|
|
56
|
+
return value != NULL && value[0] != '\0' && strcmp(value, "0") != 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
21
59
|
int main(int argc, char *argv[])
|
|
22
60
|
{
|
|
23
61
|
int status = EXIT_CODE_FAILURE;
|
|
24
62
|
UnpackContext *unpack_ctx = NULL;
|
|
25
|
-
OperationModes op_modes = 0;
|
|
26
63
|
const char *extract_dir = NULL;
|
|
27
64
|
char *image_path = NULL;
|
|
28
65
|
char *exe_dir = NULL;
|
|
@@ -34,6 +71,7 @@ int main(int argc, char *argv[])
|
|
|
34
71
|
Child processes (e.g., Ruby) handle their own signals independently,
|
|
35
72
|
ensuring the parent can finalize cleanup without premature termination.
|
|
36
73
|
*/
|
|
74
|
+
SetCleanupRoutine(delete_extraction_dir);
|
|
37
75
|
if (!InitializeSignalHandling()) {
|
|
38
76
|
FATAL("Failed to initialize system controls");
|
|
39
77
|
goto cleanup;
|
|
@@ -54,17 +92,18 @@ int main(int argc, char *argv[])
|
|
|
54
92
|
}
|
|
55
93
|
|
|
56
94
|
/* Read header of packed data */
|
|
57
|
-
|
|
95
|
+
OpModes = GetOperationModes(unpack_ctx);
|
|
58
96
|
|
|
59
|
-
/* Enable debug mode when the flag is set or OCRAN_DEBUG
|
|
60
|
-
|
|
97
|
+
/* Enable debug mode when the flag is set or OCRAN_DEBUG is set to
|
|
98
|
+
anything but "0" or the empty string. */
|
|
99
|
+
if (IsDebugMode(OpModes) || IsEnvFlagSet("OCRAN_DEBUG")) {
|
|
61
100
|
EnableDebugMode();
|
|
62
101
|
DEBUG("Ocran stub running in debug mode");
|
|
63
102
|
}
|
|
64
103
|
|
|
65
104
|
/* Create extraction directory, or run in place next to the executable
|
|
66
105
|
(installer/wrapper mode, see RUN_IN_EXE_DIR) */
|
|
67
|
-
if (IsRunInExeDir(
|
|
106
|
+
if (IsRunInExeDir(OpModes)) {
|
|
68
107
|
extract_dir = SetInstDirToExeDir();
|
|
69
108
|
if (!extract_dir) {
|
|
70
109
|
FATAL("Failed to resolve the executable directory");
|
|
@@ -72,7 +111,7 @@ int main(int argc, char *argv[])
|
|
|
72
111
|
}
|
|
73
112
|
DEBUG("Running in executable directory: %s", extract_dir);
|
|
74
113
|
} else {
|
|
75
|
-
extract_dir = CreateInstDir(IsExtractToExeDir(
|
|
114
|
+
extract_dir = CreateInstDir(IsExtractToExeDir(OpModes));
|
|
76
115
|
if (!extract_dir) {
|
|
77
116
|
FATAL("Failed to create extraction directory");
|
|
78
117
|
goto cleanup;
|
|
@@ -103,7 +142,7 @@ int main(int argc, char *argv[])
|
|
|
103
142
|
|
|
104
143
|
/* Resolve the executable's directory when the script should start
|
|
105
144
|
with its working directory next to the .exe (--chdir-exe-dir). */
|
|
106
|
-
if (IsChdirToExeDir(
|
|
145
|
+
if (IsChdirToExeDir(OpModes)) {
|
|
107
146
|
exe_dir = GetParentPath(image_path);
|
|
108
147
|
if (!exe_dir) {
|
|
109
148
|
FATAL("Failed to resolve the executable directory");
|
|
@@ -135,10 +174,33 @@ int main(int argc, char *argv[])
|
|
|
135
174
|
and then overwrites it with the external script’s return code.
|
|
136
175
|
*/
|
|
137
176
|
DEBUG("Run application script");
|
|
138
|
-
if (!RunScript(argv, IsChdirBeforeScript(
|
|
177
|
+
if (!RunScript(argv, IsChdirBeforeScript(OpModes), exe_dir, &status)) {
|
|
139
178
|
FATAL("Failed to run script");
|
|
140
179
|
goto cleanup;
|
|
141
180
|
}
|
|
181
|
+
#ifdef _WIN32
|
|
182
|
+
/*
|
|
183
|
+
The loader failed before the interpreter ran a single line, so
|
|
184
|
+
nothing else would say why the application did nothing. Reported
|
|
185
|
+
here rather than in system_utils.c: FATAL is a message box in the
|
|
186
|
+
windowed stub only, and this file is compiled for each variant.
|
|
187
|
+
*/
|
|
188
|
+
switch ((unsigned long)status) {
|
|
189
|
+
case 0xC0000135UL: /* STATUS_DLL_NOT_FOUND */
|
|
190
|
+
FATAL("The Ruby interpreter could not start: a DLL it needs was not "
|
|
191
|
+
"found (0x%08lX). An mswin Ruby also needs the Visual C++ "
|
|
192
|
+
"runtime (vcruntime140.dll).", (unsigned long)status);
|
|
193
|
+
break;
|
|
194
|
+
case 0xC0000138UL: /* STATUS_ORDINAL_NOT_FOUND */
|
|
195
|
+
case 0xC0000139UL: /* STATUS_ENTRYPOINT_NOT_FOUND */
|
|
196
|
+
case 0xC0000142UL: /* STATUS_DLL_INIT_FAILED */
|
|
197
|
+
case 0xC000007BUL: /* STATUS_INVALID_IMAGE_FORMAT */
|
|
198
|
+
FATAL("The Ruby interpreter could not start: a DLL it loads is "
|
|
199
|
+
"incompatible or failed to initialize (0x%08lX)",
|
|
200
|
+
(unsigned long)status);
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
#endif
|
|
142
204
|
/*
|
|
143
205
|
If the script executes successfully, its return code is stored in status.
|
|
144
206
|
*/
|
|
@@ -164,21 +226,17 @@ cleanup:
|
|
|
164
226
|
|
|
165
227
|
FreeScriptInfo();
|
|
166
228
|
|
|
167
|
-
/*
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
/* Never delete in RUN_IN_EXE_DIR mode: the "installation directory"
|
|
171
|
-
is the real application directory, not a temporary extraction dir. */
|
|
172
|
-
if (IsAutoCleanInstDir(op_modes) && !IsRunInExeDir(op_modes)) {
|
|
173
|
-
DEBUG("Deleting extraction directory: %s", extract_dir);
|
|
174
|
-
if (!DeleteInstDir()) {
|
|
175
|
-
DEBUG("Failed to delete extraction directory");
|
|
176
|
-
}
|
|
177
|
-
}
|
|
229
|
+
/* Delete the extraction directory (see delete_extraction_dir), unless
|
|
230
|
+
the console control handler has done so already. */
|
|
231
|
+
RunCleanupRoutine();
|
|
178
232
|
|
|
179
233
|
FreeInstDir();
|
|
180
234
|
extract_dir = NULL;
|
|
181
235
|
|
|
236
|
+
/* A child killed by a signal kills the stub the same way, now that the
|
|
237
|
+
extraction directory is gone. Returns if the signal did not. */
|
|
238
|
+
ReraiseChildSignal();
|
|
239
|
+
|
|
182
240
|
#ifdef __COSMOPOLITAN__
|
|
183
241
|
/* On Windows, Cosmopolitan Libc's exit path encodes the full wait
|
|
184
242
|
status into the process exit code (code << 8) so that cosmo
|
data/src/stub.rc
CHANGED