Ventoy Runtime File Injection Process: How the Injection Plugin Modifies Boot Images
The Ventoy runtime file injection process parses JSON configuration into a linked list during boot initialization, matches ISO paths against exact file or parent directory rules to retrieve archive paths, and extracts those archives into the virtual file system before the operating system starts loading.
Ventoy is an open-source boot manager that enables users to boot ISO files directly from USB drives without extracting them. The injection plugin, implemented in the GRUB-based boot modules of the ventoy/Ventoy repository, provides a powerful mechanism to dynamically add files—such as drivers, scripts, or configuration files—into the boot environment without modifying the original ISO image.
Understanding the Injection Plugin Architecture
The runtime file injection system operates through three distinct stages implemented across the GRUB core modules. When the boot loader initializes, the plugin validates configuration data, builds an in-memory lookup structure, and later intercepts the boot process to inject archives into the virtual file system.
The implementation spans several key files in GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/:
ventoy_plugin.chandles configuration parsing and lookup logicventoy_linux.cmanages injection for Linux initrd imagesventoy_windows.chandles Windows boot archive preparationvtoyjump.cperforms the actual extraction on Windows systems
Stage 1: Parsing the JSON Configuration
Configuration Schema
The injection plugin reads rules from the ventoy.json configuration file. Users define injection mappings using either exact file matches or parent directory wildcards:
{
"injection": [
{
"image": "/ISO/Win10.iso",
"archive": "/ISO/injection_win10.zip"
},
{
"parent": "/ISO/Linux/",
"archive": "/ISO/injection_linux.tar.gz"
}
]
}
image: Specifies an exact ISO file path for one-to-one injection mappingparent: Matches any ISO located within the specified directoryarchive: Defines the ZIP or TAR archive containing files to inject
Validation and Linked List Construction
During plugin initialization, ventoy_plugin_injection_check() in ventoy_plugin.c validates the JSON array. The function verifies that referenced image or parent paths exist on the USB device and confirms the archive file is accessible.
For each valid entry, ventoy_plugin_injection_entry() allocates an injection_config structure and prepends it to the global g_injection_head linked list:
typedef struct injection_config {
int type; // injection_type_file or injection_type_parent
int pathlen; // length of isopath string
char isopath[256]; // image or parent path (absolute)
char archive[256]; // archive path to inject
struct injection_config *next;
} injection_config;
static int ventoy_plugin_injection_entry(VTOY_JSON *json, const char *isodisk)
{
/* ... create node ... */
node->type = type;
node->pathlen = grub_snprintf(node->isopath, sizeof(node->isopath), "%s", path);
grub_snprintf(node->archive, sizeof(node->archive), "%s", archive);
/* prepend to global list */
node->next = g_injection_head;
g_injection_head = node;
}
This linked list persists for the entire boot session, enabling O(n) lookup complexity when matching images against rules.
Stage 2: Runtime Archive Lookup
The Lookup Algorithm
When preparing a boot entry, the system calls ventoy_plugin_get_injection() with the ISO path as the argument. This function, defined in ventoy_plugin.c, implements a two-pass matching strategy:
const char *ventoy_plugin_get_injection(const char *isopath)
{
int len = (int)grub_strlen(isopath);
/* 1️⃣ Exact file match */
for (node = g_injection_head; node; node = node->next) {
if (node->type == injection_type_file &&
node->pathlen == len &&
ventoy_strcmp(node->isopath, isopath) == 0)
return node->archive; // exact match
}
/* 2️⃣ Parent‑directory match */
for (node = g_injection_head; node; node = node->next) {
if (node->type == injection_type_parent &&
node->pathlen < len &&
ventoy_plugin_is_parent(node->isopath, node->pathlen, isopath))
return node->archive; // parent match
}
return NULL; // no injection configured
}
Exact vs Parent Directory Matching
The lookup prioritizes exact file matches over parent directory matches. If the first iteration finds a matching image rule with identical path length and string comparison, it returns immediately. Otherwise, the function iterates again seeking parent rules where ventoy_plugin_is_parent() verifies that the ISO path starts with the configured directory and contains no additional path separators beyond the match point.
If a match succeeds, the function returns the archive filename; otherwise, it returns NULL, indicating no injection should occur for that image.
Stage 3: Applying the Injection
Linux Initrd Integration
In ventoy_linux.c, the boot command handler retrieves the injection archive and embeds it into the initrd (initial RAM disk). When ventoy_cmd_linux() processes a Linux boot entry, it calls the lookup function and loads the archive into memory:
/* ventoy_linux.c – handling a Linux boot entry */
static int ventoy_cmd_linux(... ) {
const char *iso_path = args[1]; // e.g. "/ISO/Win10.iso"
const char *inject = ventoy_plugin_get_injection(iso_path);
if (inject) {
/* Open the archive, load it into memory */
file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", iso_root, inject);
injection_size = file->size;
injection_buf = grub_malloc(injection_size);
grub_file_read(file, injection_buf, injection_size);
/* ... later packed into initrd via ventoy_cpio_newc_fill_head ... */
}
/* continue normal boot */
}
The archive contents are appended to the initrd image (around lines 1490-1497 in ventoy_linux.c), making the injected files available in the root file system when the Linux kernel initializes.
Windows Archive Extraction
For Windows boot entries, ventoy_windows.c stores the archive path for later extraction by the vtoyjump helper:
/* ventoy_windows.c – preparing a Windows boot entry */
script = (char *)ventoy_plugin_get_injection(pos);
if (script) {
if (ventoy_check_file_exist("%s%s", ventoy_get_env("vtoy_iso_part"), script)) {
/* Remember the archive name for later decompression */
grub_snprintf(data->injection_archive,
sizeof(data->injection_archive) - 1, "%s", script);
}
}
During the Windows chainload process, vtoyjump.c accesses g_windows_data.injection_archive to locate and decompress the archive:
/* vtoyjump.c – later during chainload */
if (g_windows_data.injection_archive[0]) {
sprintf_s(IsoPath, sizeof(IsoPath), "%C:%s",
VtoyLetter, g_windows_data.injection_archive);
Log("decompress injection archive %s...", IsoPath);
/* actual decompression implementation ... */
}
This approach places the injected files into the Windows file system before the OS begins its initialization sequence.
Summary
- The Ventoy injection plugin enables runtime file modification without rebuilding ISO images by parsing
ventoy.jsonconfiguration into a linked list ofinjection_configstructures stored ing_injection_head. - The lookup mechanism in
ventoy_plugin_get_injection()prioritizes exact file matches over parent directory matches, returning the associated archive path when rules apply. - Linux implementations embed archives directly into the initrd through
ventoy_linux.c, while Windows implementations store archive references for extraction byvtoyjump.cduring the boot chainload process. - Key source files include
ventoy_plugin.cfor configuration management,ventoy_def.hfor API declarations, and OS-specific handlers inventoy_linux.candventoy_windows.c.
Frequently Asked Questions
How does Ventoy prioritize multiple injection rules for the same ISO?
Ventoy evaluates exact file matches before checking parent directory rules. In ventoy_plugin_get_injection(), the function first iterates through g_injection_head seeking injection_type_file entries with identical path lengths and string matches. Only if no exact match exists does it perform a second iteration for injection_type_parent matches, ensuring specific configurations override general directory rules.
What archive formats does the Ventoy injection plugin support?
The plugin supports standard ZIP and TAR archive formats. The source code in ventoy_linux.c and vtoyjump.c handles these archives through standard decompression routines. While the JSON configuration accepts any filename extension, the extraction logic expects archives compatible with the OS-specific boot loader modules.
Can I use runtime file injection with Ventoy without modifying the original ISO?
Yes, this is the primary design goal of the injection plugin. The ventoy_plugin_injection_entry() function stores archive paths separately from the ISO images, and ventoy_plugin_get_injection() resolves these at boot time. The original ISO remains unmodified while the boot process dynamically merges the archive contents into the virtual file system visible to the operating system.
Where does Ventoy store the injection configuration data during the boot process?
Ventoy maintains injection data in a global linked list called g_injection_head. During plugin initialization in ventoy_plugin.c, each valid JSON entry becomes an injection_config node containing the match type (file or parent), path strings, and archive location. This structure persists in memory throughout the GRUB session and is accessed by lookup functions when processing boot entries.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →