462 lines
16 KiB
C#
462 lines
16 KiB
C#
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Microsoft Research Singularity
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
// File: mkflash.cs
|
||
|
//
|
||
|
// Note: Flash image building tool.
|
||
|
//
|
||
|
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Diagnostics;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.IO;
|
||
|
using System.Text;
|
||
|
using System.Threading;
|
||
|
|
||
|
#if SINGULARITY
|
||
|
using Microsoft.Singularity.Channels;
|
||
|
using Microsoft.Singularity.Io;
|
||
|
using Microsoft.Singularity.Directory;
|
||
|
using Microsoft.Singularity;
|
||
|
using Microsoft.Singularity.Applications;
|
||
|
#endif
|
||
|
|
||
|
namespace Microsoft.Singularity.Applications
|
||
|
{
|
||
|
public class ContentFile
|
||
|
{
|
||
|
public ContentFile(string prefix, string path)
|
||
|
{
|
||
|
FileInfo fileInfo = new FileInfo(prefix + path);
|
||
|
|
||
|
this.Path = path;
|
||
|
this.Size = (uint)fileInfo.Length;
|
||
|
|
||
|
DataOffset = 0;
|
||
|
PathOffset = 0;
|
||
|
}
|
||
|
|
||
|
public ContentFile(string path, uint size)
|
||
|
{
|
||
|
this.Path = path;
|
||
|
this.Size = size;
|
||
|
|
||
|
DataOffset = 0;
|
||
|
PathOffset = 0;
|
||
|
}
|
||
|
|
||
|
// FileInfo fileInfo = new FileInfo(originalFilePath);
|
||
|
// using (FileStream fs = fileInfo.OpenRead()) {
|
||
|
public readonly string Path;
|
||
|
public readonly uint Size;
|
||
|
public uint DataOffset;
|
||
|
public uint PathOffset;
|
||
|
|
||
|
private static string ParseParameter(string line, string param)
|
||
|
{
|
||
|
int beg = line.IndexOf(param);
|
||
|
if (beg >= 0) {
|
||
|
string rest = line.Substring(beg + param.Length);
|
||
|
|
||
|
int end = rest.IndexOf(' ');
|
||
|
if (end >= 0) {
|
||
|
return rest.Substring(0, end);
|
||
|
}
|
||
|
return rest;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public static ContentFile[] ReadFilesFromFile(string filePath)
|
||
|
{
|
||
|
ArrayList inFiles = new ArrayList();
|
||
|
|
||
|
try {
|
||
|
using (StreamReader sr = new StreamReader(filePath)) {
|
||
|
string line;
|
||
|
int count = 0;
|
||
|
while ((line = sr.ReadLine()) != null) {
|
||
|
++count;
|
||
|
line = line.Trim();
|
||
|
if (line.Length == 0 || line[0] == '#') {
|
||
|
// Skip blank and comment lines.
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
string path = ParseParameter(line, "Path=");
|
||
|
string sizs = ParseParameter(line, "Size=");
|
||
|
string hash = ParseParameter(line, "Hash-MD5=");
|
||
|
uint size = UInt32.Parse(sizs);
|
||
|
|
||
|
if (path == null || path == "" || size == 0) {
|
||
|
Console.WriteLine("mkflash: Error: {0}#{1} missing path or size.",
|
||
|
filePath, count);
|
||
|
continue;
|
||
|
}
|
||
|
inFiles.Add(new ContentFile(path, size));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (ContentFile[])inFiles.ToArray(typeof(ContentFile));
|
||
|
}
|
||
|
catch (FileNotFoundException) {
|
||
|
Console.WriteLine("File not found {0}\n", filePath);
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class NativeImageBuilder
|
||
|
{
|
||
|
public const uint FLASH_HEAD_SIZE = 32;
|
||
|
public const uint FLASH_FILE_SIZE = 12;
|
||
|
|
||
|
//
|
||
|
// Global flags.
|
||
|
//
|
||
|
public static bool Verbose = false;
|
||
|
|
||
|
// Print the correct command line args for the program
|
||
|
static void Usage()
|
||
|
{
|
||
|
Console.WriteLine(
|
||
|
"Usage:\n" +
|
||
|
" mkflash [options] /flash:<file> /root:<path> /boot:<file>\n" +
|
||
|
"Options:\n" +
|
||
|
" /flash:<file> - Name of target flash image.\n" +
|
||
|
" /root:<path> - Root of file distro.\n" +
|
||
|
" /boot:<file> - Singboot.ini file listing files for image.\n" +
|
||
|
"Summary:\n" +
|
||
|
" Create a flash image image for a distribution.\n" +
|
||
|
"");
|
||
|
}
|
||
|
|
||
|
static bool IsQuote(char c)
|
||
|
{
|
||
|
return (c == '"' || c == '\'');
|
||
|
}
|
||
|
|
||
|
static string RemoveQuotes(string text)
|
||
|
{
|
||
|
int start = 0;
|
||
|
int length = text.Length;
|
||
|
|
||
|
if (length > 0 && IsQuote(text[start + length - 1])) {
|
||
|
length--;
|
||
|
}
|
||
|
if (length > 0 && IsQuote(text[start])) {
|
||
|
start++;
|
||
|
length--;
|
||
|
}
|
||
|
|
||
|
if (start != 0 || length != text.Length) {
|
||
|
return text.Substring(start, length);
|
||
|
}
|
||
|
else {
|
||
|
return text;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool StringIsNullOrEmpty(string str)
|
||
|
{
|
||
|
return (str == null || str.Length == 0);
|
||
|
}
|
||
|
|
||
|
static uint PadToPage(uint value)
|
||
|
{
|
||
|
return (value + 0xfffu) & ~0xfffu;
|
||
|
}
|
||
|
|
||
|
static uint FileRecord(uint file)
|
||
|
{
|
||
|
return FLASH_HEAD_SIZE + FLASH_FILE_SIZE * file;
|
||
|
}
|
||
|
|
||
|
static void WriteUInt32(byte[] buffer, uint offset, uint value)
|
||
|
{
|
||
|
buffer[offset + 0] = (byte)((value & 0x000000ff) >> 0);
|
||
|
buffer[offset + 1] = (byte)((value & 0x0000ff00) >> 8);
|
||
|
buffer[offset + 2] = (byte)((value & 0x00ff0000) >> 16);
|
||
|
buffer[offset + 3] = (byte)((value & 0xff000000) >> 24);
|
||
|
}
|
||
|
|
||
|
static uint WriteString(byte[] buffer, uint offset, String value)
|
||
|
{
|
||
|
for (int i = 0; i < value.Length; i++) {
|
||
|
buffer[offset + i] = (byte)value[i];
|
||
|
}
|
||
|
buffer[offset + value.Length] = 0;
|
||
|
return (uint)value.Length + 1;
|
||
|
}
|
||
|
|
||
|
static uint Copy(byte[] buffer, FileStream ostream, FileStream istream, uint size)
|
||
|
{
|
||
|
uint written = 0;
|
||
|
while (size > buffer.Length) {
|
||
|
istream.Read(buffer, 0, buffer.Length);
|
||
|
ostream.Write(buffer, 0, buffer.Length);
|
||
|
size -= (uint)buffer.Length;
|
||
|
written += (uint)buffer.Length;
|
||
|
}
|
||
|
if (size > 0) {
|
||
|
istream.Read(buffer, 0, (int)size);
|
||
|
for (int pad = (int)PadToPage((uint)size); size < pad; size++) {
|
||
|
buffer[size] = 0xff;
|
||
|
}
|
||
|
ostream.Write(buffer, 0, (int)size);
|
||
|
written += size;
|
||
|
}
|
||
|
return written;
|
||
|
}
|
||
|
|
||
|
static int Main(string[] args)
|
||
|
{
|
||
|
ContentFile[] inFiles = null;
|
||
|
string flashImage = null;
|
||
|
string rootDirectory = null;
|
||
|
|
||
|
// Temporaries for command-line parsing
|
||
|
bool needHelp = (args.Length == 0);
|
||
|
|
||
|
#if !SINGULARITY
|
||
|
for (int i = 0; i < args.Length && !needHelp; i++) {
|
||
|
#else
|
||
|
for (int i = 1; i < args.Length && !needHelp; i++) {
|
||
|
#endif
|
||
|
string arg = (string) args[i];
|
||
|
arg = RemoveQuotes(arg);
|
||
|
|
||
|
if (arg.Length >= 2 && (arg[0] == '-' || arg[0] == '/')) {
|
||
|
string name = null;
|
||
|
string value = null;
|
||
|
int n = arg.IndexOf(':');
|
||
|
if (n > -1) {
|
||
|
name = arg.Substring(1, n - 1).ToLower();
|
||
|
if (n < arg.Length + 1) {
|
||
|
value = arg.Substring(n + 1);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
name = arg.Substring(1).ToLower();
|
||
|
}
|
||
|
if (value != null) {
|
||
|
value = value.Trim();
|
||
|
value = RemoveQuotes(value);
|
||
|
value = value.Trim();
|
||
|
}
|
||
|
|
||
|
bool badArg = false;
|
||
|
switch (name) {
|
||
|
case "h":
|
||
|
case "help":
|
||
|
case "?":
|
||
|
needHelp = true;
|
||
|
break;
|
||
|
|
||
|
case "file":
|
||
|
inFiles = new ContentFile[]
|
||
|
{ new ContentFile(rootDirectory, value) };
|
||
|
break;
|
||
|
|
||
|
case "boot":
|
||
|
case "bootfile":
|
||
|
if (value != null) {
|
||
|
if ((inFiles = ContentFile.ReadFilesFromFile(value)) == null) {
|
||
|
return 3;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "flash":
|
||
|
if (value != null) {
|
||
|
flashImage = value;
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "root":
|
||
|
if (value != null) {
|
||
|
rootDirectory = value.TrimEnd('/', '\\');
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "v":
|
||
|
Verbose = true;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Console.WriteLine("mkflash: Unknown command line argument: {0}", arg);
|
||
|
needHelp = true;
|
||
|
break;
|
||
|
}
|
||
|
if (badArg) {
|
||
|
Console.WriteLine("mkflash: Invalid command line argument: {0}", arg);
|
||
|
needHelp = true;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
Console.WriteLine("mkflash: Invalid command line argument: {0}", arg);
|
||
|
needHelp = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (inFiles == null || inFiles.Length == 0) {
|
||
|
Console.WriteLine("mkflash: /boot:<file> missing from command line.");
|
||
|
needHelp = true;
|
||
|
}
|
||
|
|
||
|
if (rootDirectory == null) {
|
||
|
Console.WriteLine("mkflash: /root:<path> missing from command line.");
|
||
|
needHelp = true;
|
||
|
}
|
||
|
if (flashImage == null) {
|
||
|
Console.WriteLine("mkflash: /flash:<path> missing from command line.");
|
||
|
needHelp = true;
|
||
|
}
|
||
|
|
||
|
if (needHelp) {
|
||
|
Usage();
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// check all input files
|
||
|
if (!Directory.Exists(rootDirectory)) {
|
||
|
Console.WriteLine("mkflash: Error: Root directory '{0}' not found.",
|
||
|
rootDirectory);
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
// check all input files
|
||
|
for (int i = 0; i < inFiles.Length; i++) {
|
||
|
if (!File.Exists(rootDirectory + inFiles[i].Path)) {
|
||
|
Console.WriteLine("mkflash: Error: Application manifest '{0}' not found.",
|
||
|
rootDirectory + inFiles[i].Path);
|
||
|
return 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check that we can create the output file.
|
||
|
byte[] buffer = new byte [256 * 1024];
|
||
|
try {
|
||
|
FileStream stream = new FileStream(flashImage, FileMode.Create);
|
||
|
if (stream == null) {
|
||
|
throw new IOException("Why is this null?");
|
||
|
}
|
||
|
|
||
|
// Write the flash header.
|
||
|
// Save room for extra files.
|
||
|
buffer[0] = (byte)'S';
|
||
|
buffer[1] = (byte)'i';
|
||
|
buffer[2] = (byte)'n';
|
||
|
buffer[3] = (byte)'g';
|
||
|
buffer[4] = (byte)'u';
|
||
|
buffer[5] = (byte)'l';
|
||
|
buffer[6] = (byte)'a';
|
||
|
buffer[7] = (byte)'r';
|
||
|
buffer[8] = (byte)'i';
|
||
|
buffer[9] = (byte)'t';
|
||
|
buffer[10] = (byte)'y';
|
||
|
buffer[11] = (byte)'F';
|
||
|
buffer[12] = (byte)'l';
|
||
|
buffer[13] = (byte)'a';
|
||
|
buffer[14] = (byte)'s';
|
||
|
buffer[15] = (byte)'h';
|
||
|
buffer[16] = (byte)'!';
|
||
|
buffer[17] = 0;
|
||
|
buffer[18] = (byte)FLASH_HEAD_SIZE;
|
||
|
buffer[19] = (byte)FLASH_FILE_SIZE;
|
||
|
WriteUInt32(buffer, 20, 0x1000); // Page size.
|
||
|
WriteUInt32(buffer, 24, unchecked((uint)-1));
|
||
|
WriteUInt32(buffer, 28, unchecked((uint)-1));
|
||
|
uint head = PadToPage(FileRecord((uint)(2 * inFiles.Length)));
|
||
|
|
||
|
for (uint pad = FileRecord((uint)inFiles.Length); pad < head; pad++) {
|
||
|
buffer[pad] = 0xff;
|
||
|
}
|
||
|
|
||
|
// Write the path names and save the path offsets.
|
||
|
uint pathoff = head;
|
||
|
for (int i = 0; i < inFiles.Length; i++) {
|
||
|
inFiles[i].PathOffset = pathoff;
|
||
|
|
||
|
pathoff += WriteString(buffer, inFiles[i].PathOffset, inFiles[i].Path);
|
||
|
}
|
||
|
// Save room for extra path names.
|
||
|
uint data = PadToPage(head + (pathoff - head) * 2);
|
||
|
head = data;
|
||
|
|
||
|
// Calculate the file data offsets.
|
||
|
for (int i = 0; i < inFiles.Length; i++) {
|
||
|
inFiles[i].DataOffset = head;
|
||
|
head += PadToPage(inFiles[i].Size);
|
||
|
}
|
||
|
|
||
|
// Write the file records.
|
||
|
uint offset;
|
||
|
for (uint i = 0; i < inFiles.Length; i++) {
|
||
|
offset = FileRecord(i);
|
||
|
WriteUInt32(buffer, offset + 0, inFiles[i].PathOffset);
|
||
|
WriteUInt32(buffer, offset + 4, inFiles[i].DataOffset);
|
||
|
WriteUInt32(buffer, offset + 8, inFiles[i].Size);
|
||
|
}
|
||
|
|
||
|
// Terminate the list.
|
||
|
offset = FileRecord((uint)(2 * inFiles.Length - 1));
|
||
|
WriteUInt32(buffer, offset + 0, 0);
|
||
|
WriteUInt32(buffer, offset + 4, 0xffffffff);
|
||
|
WriteUInt32(buffer, offset + 8, 0);
|
||
|
|
||
|
if (Verbose) {
|
||
|
Console.WriteLine(" {0,8:x}..{1,8:x}: Header", 0, data - 1);
|
||
|
}
|
||
|
stream.Write(buffer, 0, (int)data);
|
||
|
|
||
|
// Write each file.
|
||
|
for (int i = 0; i < inFiles.Length; i++) {
|
||
|
string file = rootDirectory + inFiles[i].Path;
|
||
|
FileStream istream = new FileStream(file, FileMode.Open);
|
||
|
if (istream == null) {
|
||
|
throw new IOException("Why is this null?");
|
||
|
}
|
||
|
|
||
|
uint written = Copy(buffer, stream, istream, inFiles[i].Size);
|
||
|
if (Verbose) {
|
||
|
Console.WriteLine(" {0,8:x}..{1,8:x}: {2,8:x} {3}",
|
||
|
inFiles[i].DataOffset,
|
||
|
inFiles[i].DataOffset + written - 1,
|
||
|
inFiles[i].PathOffset,
|
||
|
inFiles[i].Path);
|
||
|
}
|
||
|
|
||
|
istream.Close();
|
||
|
}
|
||
|
stream.Close();
|
||
|
}
|
||
|
catch (IOException ex) {
|
||
|
for (Exception current = ex; current != null; current = current.InnerException) {
|
||
|
Console.WriteLine("mkflash: Error: {0}", current.Message);
|
||
|
}
|
||
|
try {
|
||
|
File.Delete(flashImage);
|
||
|
}
|
||
|
catch (Exception) {
|
||
|
}
|
||
|
return 4;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|