Task 629: .RUN File Format
Task 629: .RUN File Format
.RUN File Format Specifications
The .RUN file format is not a standardized binary file format with a fixed specification like ELF or PE. Instead, it is a conventional format used primarily in Linux for self-extracting installers or archives. These files are typically generated using tools like "makeself" (a shell script utility) and consist of a shell script header concatenated with a compressed binary payload (e.g., a tar.gz archive). The header is executable text (starting with a shebang like #!/bin/sh), which handles extraction, integrity checks, and optional post-extraction actions. The payload is appended after the header.
.RUN files are executable (with the execute bit set in the file system), and when run, the script extracts the payload to a temporary directory, verifies checksums if present, and may execute a startup script from the archive. There is no magic number or strict binary header; identification relies on the shebang and script content. The format is flexible, but common in software distributions like NVIDIA drivers or other installers.
1. List of All Properties Intrinsic to This File Format
Based on the structure of makeself-generated .RUN files (the most common implementation), the following properties are embedded in the shell script header. These are set as shell variables and used for extraction logic, integrity verification, and configuration. They are "intrinsic" in that they define the file's behavior and content without relying on external file system metadata (e.g., permissions or timestamps, which are OS-dependent). The properties are parsed from the text header:
- label: Descriptive name or title of the archive (e.g., "NVIDIA Driver Installer").
- targetdir: Directory where the archive should be extracted (default: ".").
- filesize: Expected uncompressed size of the archive in KB (used for disk space checks).
- md5sum: MD5 checksum of the compressed payload for integrity verification.
- sha256sum: SHA256 checksum of the compressed payload for integrity verification.
- compression: Compression method for the payload (e.g., "gz" for gzip, "bz2" for bzip2, "zstd" for Zstandard).
- script: Path to a startup script within the extracted archive to run after extraction.
- scriptargs: Arguments to pass to the startup script.
- encryption: Encryption method if the payload is encrypted (e.g., "gpg" for GPG-encrypted).
- key: Encryption key or passphrase (if encryption is used).
- signature: GPG signature for verification (if signed).
- keep: Flag to keep extracted files after execution (yes/no).
- noexec: Flag to prevent executing the startup script (yes/no).
- quiet: Flag to suppress output during extraction (yes/no).
- nox11: Flag to disable X11 checks (yes/no).
- nodiskspace: Flag to skip disk space checks (yes/no).
- follow: Flag to follow symlinks during extraction (yes/no).
- skip: Line number offset to the start of the binary payload (calculated during creation; key to locating the archive).
These properties are set via shell assignments in the header (e.g., label="My Archive"). The header ends just before the binary payload, and the skip value indicates how many lines to skip to reach the payload.
2. Two Direct Download Links for .RUN Files
- https://us.download.nvidia.com/XFree86/Linux-x86_64/580.82.07/NVIDIA-Linux-x86_64-580.82.07.run (Latest NVIDIA GeForce/Quadro driver for Linux x86_64)
- https://us.download.nvidia.com/XFree86/Linux-x86_64/550.90.07/NVIDIA-Linux-x86_64-550.90.07.run (Previous NVIDIA GeForce/Quadro driver for Linux x86_64)
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .RUN File Dump
This is an HTML snippet with embedded JavaScript that can be embedded in a Ghost blog post (or any HTML page). It allows dragging and dropping a .RUN file, parses the header to extract the properties, and dumps them to the screen. It uses FileReader to read the file as text (assuming the header is ASCII) and parses lines for variable assignments.
4. Python Class for .RUN File Handling
import os
import re
class RunFileHandler:
def __init__(self, filename):
self.filename = filename
self.properties = {}
self.skip = 0
self.payload = b''
def read(self):
with open(self.filename, 'rb') as f:
content = f.read()
# Assume header is text until binary payload; split lines
text_content = content.decode('utf-8', errors='ignore')
lines = text_content.split('\n')
for i, line in enumerate(lines):
line = line.strip()
if line.startswith('skip='):
self.skip = int(re.search(r'skip=(\d+)', line).group(1))
break
match = re.match(r'(\w+)=["\']?(.*?)["\']?$', line)
if match and not line.startswith('#'):
key, value = match.groups()
self.properties[key] = value
# Extract payload starting after skip lines
header_lines = '\n'.join(lines[:self.skip-1]).encode('utf-8')
self.payload = content[len(header_lines) + (self.skip-1):] # Account for newlines
def print_properties(self):
if not self.properties:
print("No properties found. Call read() first.")
return
for key, value in self.properties.items():
print(f"{key}: {value}")
print(f"skip: {self.skip}")
def write(self, new_filename, new_properties=None, new_payload=None):
if new_properties:
self.properties.update(new_properties)
if new_payload:
self.payload = new_payload
header = '#!/bin/sh\n' # Basic shebang
for key, value in self.properties.items():
header += f'{key}="{value}"\n'
header += f'skip={len(header.splitlines()) + 1}\n' # Update skip
# Add extraction logic (simplified)
header += 'tail -n +$skip $0 | gunzip | tar x\nexit 0\n'
with open(new_filename, 'wb') as f:
f.write(header.encode('utf-8'))
f.write(self.payload)
os.chmod(new_filename, 0o755) # Make executable
# Example usage:
# handler = RunFileHandler('example.run')
# handler.read()
# handler.print_properties()
# handler.write('new.run', {'label': 'Updated Label'})
5. Java Class for .RUN File Handling
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.regex.*;
public class RunFileHandler {
private String filename;
private Map<String, String> properties = new HashMap<>();
private int skip = 0;
private byte[] payload;
public RunFileHandler(String filename) {
this.filename = filename;
}
public void read() throws IOException {
byte[] content = Files.readAllBytes(Paths.get(filename));
String textContent = new String(content, "UTF-8"); // Ignore errors for binary
String[] lines = textContent.split("\n");
Pattern assignPattern = Pattern.compile("(\\w+)=[\"']?(.*?)[\"']?$");
for (String line : lines) {
line = line.trim();
if (line.startsWith("skip=")) {
skip = Integer.parseInt(line.split("=")[1].trim());
break;
}
Matcher matcher = assignPattern.matcher(line);
if (matcher.matches() && !line.startsWith("#")) {
properties.put(matcher.group(1), matcher.group(2));
}
}
// Extract payload
String headerText = String.join("\n", Arrays.copyOf(lines, skip - 1));
int headerLength = headerText.getBytes("UTF-8").length + (skip - 1); // Newlines
payload = Arrays.copyOfRange(content, headerLength, content.length);
}
public void printProperties() {
if (properties.isEmpty()) {
System.out.println("No properties found. Call read() first.");
return;
}
for (Map.Entry<String, String> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
System.out.println("skip: " + skip);
}
public void write(String newFilename, Map<String, String> newProperties, byte[] newPayload) throws IOException {
if (newProperties != null) {
properties.putAll(newProperties);
}
if (newPayload != null) {
payload = newPayload;
}
StringBuilder header = new StringBuilder("#!/bin/sh\n");
for (Map.Entry<String, String> entry : properties.entrySet()) {
header.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"\n");
}
String headerStr = header.toString();
int newSkip = headerStr.split("\n").length + 1;
headerStr += "skip=" + newSkip + "\n";
headerStr += "tail -n +$skip $0 | gunzip | tar x\nexit 0\n"; // Simplified extraction
Files.write(Paths.get(newFilename), headerStr.getBytes("UTF-8"));
try (FileOutputStream fos = new FileOutputStream(newFilename, true)) {
fos.write(payload);
}
new File(newFilename).setExecutable(true);
}
// Example usage:
// public static void main(String[] args) throws IOException {
// RunFileHandler handler = new RunFileHandler("example.run");
// handler.read();
// handler.printProperties();
// handler.write("new.run", Map.of("label", "Updated Label"), null);
// }
}
6. JavaScript Class for .RUN File Handling
This is a Node.js class (requires fs module). Run with node script.js.
const fs = require('fs');
class RunFileHandler {
constructor(filename) {
this.filename = filename;
this.properties = {};
this.skip = 0;
this.payload = Buffer.alloc(0);
}
read() {
const content = fs.readFileSync(this.filename);
const textContent = content.toString('utf8'); // Full as text, ignore binary errors
const lines = textContent.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('skip=')) {
this.skip = parseInt(line.split('=')[1]);
break;
}
const match = line.match(/(\w+)=["']?(.*?)["']?$/);
if (match && !line.startsWith('#')) {
this.properties[match[1]] = match[2];
}
}
const headerLines = lines.slice(0, this.skip - 1).join('\n');
const headerLength = Buffer.from(headerLines, 'utf8').length + (this.skip - 1); // Newlines
this.payload = content.slice(headerLength);
}
printProperties() {
if (Object.keys(this.properties).length === 0) {
console.log('No properties found. Call read() first.');
return;
}
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
console.log(`skip: ${this.skip}`);
}
write(newFilename, newProperties = {}, newPayload = null) {
if (Object.keys(newProperties).length > 0) {
Object.assign(this.properties, newProperties);
}
if (newPayload) {
this.payload = newPayload;
}
let header = '#!/bin/sh\n';
for (const [key, value] of Object.entries(this.properties)) {
header += `${key}="${value}"\n`;
}
const tempHeaderLines = header.split('\n').length + 1; // For skip and extraction
header += `skip=${tempHeaderLines + 2}\n`; // Adjust for added lines
header += 'tail -n +$skip $0 | gunzip | tar x\nexit 0\n';
fs.writeFileSync(newFilename, header, 'utf8');
fs.appendFileSync(newFilename, this.payload);
fs.chmodSync(newFilename, 0o755);
}
}
// Example usage:
// const handler = new RunFileHandler('example.run');
// handler.read();
// handler.printProperties();
// handler.write('new.run', { label: 'Updated Label' });
7. C++ Class for .RUN File Handling
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <regex>
#include <vector>
#include <sys/stat.h> // For chmod
class RunFileHandler {
private:
std::string filename;
std::map<std::string, std::string> properties;
int skip = 0;
std::vector<char> payload;
public:
RunFileHandler(const std::string& fn) : filename(fn) {}
void read() {
std::ifstream file(filename, std::ios::binary);
if (!file) {
std::cerr << "Failed to open file." << std::endl;
return;
}
std::vector<char> content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
std::string textContent(content.begin(), content.end());
std::vector<std::string> lines;
std::string line;
for (char c : textContent) {
if (c == '\n') {
lines.push_back(line);
line.clear();
} else {
line += c;
}
}
if (!line.empty()) lines.push_back(line);
std::regex assignRegex(R"((\w+)=["']?(.*?)["']?$)");
std::smatch match;
for (const auto& ln : lines) {
std::string trimmed = ln;
// Trim whitespace (simplified)
trimmed.erase(0, trimmed.find_first_not_of(" \t"));
trimmed.erase(trimmed.find_last_not_of(" \t") + 1);
if (trimmed.rfind("skip=", 0) == 0) {
skip = std::stoi(trimmed.substr(5));
break;
}
if (std::regex_match(trimmed, match, assignRegex) && trimmed[0] != '#') {
properties[match[1]] = match[2];
}
}
std::string headerText;
for (int i = 0; i < skip - 1; ++i) {
headerText += lines[i] + "\n";
}
size_t headerLength = headerText.size();
payload.assign(content.begin() + headerLength, content.end());
}
void printProperties() const {
if (properties.empty()) {
std::cout << "No properties found. Call read() first." << std::endl;
return;
}
for (const auto& pair : properties) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
std::cout << "skip: " << skip << std::endl;
}
void write(const std::string& newFilename, const std::map<std::string, std::string>& newProperties = {}, const std::vector<char>& newPayload = {}) {
for (const auto& pair : newProperties) {
properties[pair.first] = pair.second;
}
if (!newPayload.empty()) {
payload = newPayload;
}
std::string header = "#!/bin/sh\n";
for (const auto& pair : properties) {
header += pair.first + "=\"" + pair.second + "\"\n";
}
std::vector<std::string> headerLines = split(header, '\n');
int newSkip = headerLines.size() + 2; // For skip and extraction lines
header += "skip=" + std::to_string(newSkip) + "\n";
header += "tail -n +$skip $0 | gunzip | tar x\nexit 0\n";
std::ofstream outFile(newFilename, std::ios::binary);
outFile << header;
outFile.write(payload.data(), payload.size());
outFile.close();
chmod(newFilename.c_str(), 0755);
}
private:
std::vector<std::string> split(const std::string& s, char delimiter) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
};
// Example usage:
// int main() {
// RunFileHandler handler("example.run");
// handler.read();
// handler.printProperties();
// handler.write("new.run");
// return 0;
// }