singrdk/base/Windows/mkflash/mkflash.cs

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;
}
}
}