зеркало из https://github.com/mono/ikvm-fork.git
518 строки
12 KiB
C#
518 строки
12 KiB
C#
/*
|
|
Copyright (C) 2007-2009 Jeroen Frijters
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Jeroen Frijters
|
|
jeroen@frijters.net
|
|
|
|
*/
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
|
namespace IKVM.Internal
|
|
{
|
|
static class VirtualFileSystem
|
|
{
|
|
internal static readonly string RootPath = JVM.IsUnix ? "/.virtual-ikvm-home/" : @"C:\.virtual-ikvm-home\";
|
|
|
|
internal static bool IsVirtualFS(string path)
|
|
{
|
|
return (path.Length == RootPath.Length - 1 && String.CompareOrdinal(path, 0, RootPath, 0, RootPath.Length - 1) == 0)
|
|
|| String.CompareOrdinal(path, 0, RootPath, 0, RootPath.Length) == 0;
|
|
}
|
|
|
|
#if !FIRST_PASS
|
|
private static VfsDirectory root;
|
|
|
|
private abstract class VfsEntry
|
|
{
|
|
}
|
|
|
|
private abstract class VfsFile : VfsEntry
|
|
{
|
|
internal abstract long Size { get; }
|
|
internal abstract System.IO.Stream Open();
|
|
}
|
|
|
|
private class VfsDirectory : VfsEntry
|
|
{
|
|
private readonly Dictionary<string, VfsEntry> entries = new Dictionary<string,VfsEntry>();
|
|
|
|
internal VfsDirectory AddDirectory(string name)
|
|
{
|
|
VfsDirectory dir = new VfsDirectory();
|
|
Add(name, dir);
|
|
return dir;
|
|
}
|
|
|
|
internal void Add(string name, VfsEntry entry)
|
|
{
|
|
lock (entries)
|
|
{
|
|
entries.Add(name, entry);
|
|
}
|
|
}
|
|
|
|
internal VfsEntry GetEntry(string name)
|
|
{
|
|
VfsEntry entry;
|
|
lock (entries)
|
|
{
|
|
entries.TryGetValue(name, out entry);
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
internal string[] List()
|
|
{
|
|
lock (entries)
|
|
{
|
|
string[] list = new string[entries.Keys.Count];
|
|
entries.Keys.CopyTo(list, 0);
|
|
return list;
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class VfsDummyFile : VfsFile
|
|
{
|
|
internal override long Size
|
|
{
|
|
get { return 0; }
|
|
}
|
|
|
|
internal override System.IO.Stream Open()
|
|
{
|
|
return System.IO.Stream.Null;
|
|
}
|
|
}
|
|
|
|
private sealed class VfsZipEntry : VfsFile
|
|
{
|
|
private java.util.zip.ZipFile zipFile;
|
|
private java.util.zip.ZipEntry entry;
|
|
|
|
internal VfsZipEntry(java.util.zip.ZipFile zipFile, java.util.zip.ZipEntry entry)
|
|
{
|
|
this.zipFile = zipFile;
|
|
this.entry = entry;
|
|
}
|
|
|
|
internal override long Size
|
|
{
|
|
get { return entry.getSize(); }
|
|
}
|
|
|
|
internal override System.IO.Stream Open()
|
|
{
|
|
return new ZipEntryStream(zipFile, entry);
|
|
}
|
|
}
|
|
|
|
private sealed class VfsCacertsEntry : VfsFile
|
|
{
|
|
private byte[] buf;
|
|
|
|
internal override long Size
|
|
{
|
|
get
|
|
{
|
|
Populate();
|
|
return buf.Length;
|
|
}
|
|
}
|
|
|
|
internal override System.IO.Stream Open()
|
|
{
|
|
Populate();
|
|
return new System.IO.MemoryStream(buf, false);
|
|
}
|
|
|
|
private void Populate()
|
|
{
|
|
if (buf == null)
|
|
{
|
|
global::java.security.KeyStore jstore = global::java.security.KeyStore.getInstance("jks");
|
|
jstore.load(null);
|
|
global::java.security.cert.CertificateFactory cf = global::java.security.cert.CertificateFactory.getInstance("X509");
|
|
|
|
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
|
|
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
|
|
foreach (X509Certificate2 cert in store.Certificates)
|
|
{
|
|
if (!cert.HasPrivateKey)
|
|
{
|
|
jstore.setCertificateEntry(cert.Subject, cf.generateCertificate(new global::java.io.ByteArrayInputStream(cert.RawData)));
|
|
}
|
|
}
|
|
store.Close();
|
|
global::java.io.ByteArrayOutputStream baos = new global::java.io.ByteArrayOutputStream();
|
|
jstore.store(baos, new char[0]);
|
|
buf = baos.toByteArray();
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class VfsVfsZipEntry : VfsFile
|
|
{
|
|
internal override long Size
|
|
{
|
|
get
|
|
{
|
|
using (System.IO.Stream stream = Open())
|
|
{
|
|
return stream.Length;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override System.IO.Stream Open()
|
|
{
|
|
//return new System.IO.FileStream("c:\\ikvm\\openjdk\\vfs.zip", System.IO.FileMode.Open);
|
|
return Assembly.GetExecutingAssembly().GetManifestResourceStream("vfs.zip");
|
|
}
|
|
}
|
|
|
|
private abstract class VfsExecutable : VfsFile
|
|
{
|
|
internal override long Size { get { return 0; } }
|
|
|
|
internal override System.IO.Stream Open()
|
|
{
|
|
return System.IO.Stream.Null;
|
|
}
|
|
|
|
internal abstract string GetPath();
|
|
}
|
|
|
|
private sealed class VfsJavaExe : VfsExecutable
|
|
{
|
|
private string path;
|
|
|
|
internal override string GetPath()
|
|
{
|
|
if (path == null)
|
|
{
|
|
path = new System.Uri(Assembly.GetEntryAssembly().CodeBase + "/../ikvm.exe").LocalPath;
|
|
}
|
|
return path;
|
|
}
|
|
}
|
|
|
|
private static void Initialize()
|
|
{
|
|
VfsDirectory root = new VfsDirectory();
|
|
root.AddDirectory("lib").AddDirectory("security").Add("cacerts", new VfsCacertsEntry());
|
|
VfsDirectory bin = new VfsDirectory();
|
|
root.Add("bin", bin);
|
|
AddDummyLibrary(bin, "zip");
|
|
AddDummyLibrary(bin, "awt");
|
|
AddDummyLibrary(bin, "rmi");
|
|
AddDummyLibrary(bin, "w2k_lsa_auth");
|
|
AddDummyLibrary(bin, "jaas_nt");
|
|
AddDummyLibrary(bin, "jaas_unix");
|
|
AddDummyLibrary(bin, "unpack");
|
|
AddDummyLibrary(bin, "net");
|
|
bin.Add("java", new VfsJavaExe());
|
|
bin.Add("javaw", new VfsJavaExe());
|
|
bin.Add("java.exe", new VfsJavaExe());
|
|
bin.Add("javaw.exe", new VfsJavaExe());
|
|
|
|
// this is a weird loop back, the vfs.zip resource is loaded from vfs,
|
|
// because that's the easiest way to construct a ZipFile from a Stream.
|
|
java.util.zip.ZipFile zf = new java.util.zip.ZipFile(RootPath + "vfs.zip");
|
|
java.util.Enumeration e = zf.entries();
|
|
while (e.hasMoreElements())
|
|
{
|
|
AddZipEntry(zf, root, (java.util.zip.ZipEntry)e.nextElement());
|
|
}
|
|
|
|
// make "lib/security/local_policy.jar" point to "lib/security/US_export_policy.jar"
|
|
// to get the unrestricted crypto policy
|
|
VfsDirectory security = (VfsDirectory)((VfsDirectory)root.GetEntry("lib")).GetEntry("security");
|
|
security.Add("local_policy.jar", security.GetEntry("US_export_policy.jar"));
|
|
|
|
Interlocked.CompareExchange(ref VirtualFileSystem.root, root, null);
|
|
}
|
|
|
|
private static void AddDummyLibrary(VfsDirectory dir, string name)
|
|
{
|
|
dir.Add(java.lang.System.mapLibraryName(name), new VfsDummyFile());
|
|
}
|
|
|
|
private static void AddZipEntry(java.util.zip.ZipFile zf, VfsDirectory root, java.util.zip.ZipEntry entry)
|
|
{
|
|
if (entry.isDirectory())
|
|
{
|
|
return;
|
|
}
|
|
string[] path = entry.getName().Split('/');
|
|
VfsDirectory dir = root;
|
|
for (int i = 0; i < path.Length - 1; i++)
|
|
{
|
|
VfsDirectory existing = dir.GetEntry(path[i]) as VfsDirectory;
|
|
if (existing == null)
|
|
{
|
|
existing = dir.AddDirectory(path[i]);
|
|
}
|
|
dir = existing;
|
|
}
|
|
dir.Add(path[path.Length - 1], new VfsZipEntry(zf, entry));
|
|
}
|
|
|
|
private static VfsEntry GetVfsEntry(string name)
|
|
{
|
|
if (root == null)
|
|
{
|
|
if (name == RootPath + "vfs.zip")
|
|
{
|
|
return new VfsVfsZipEntry();
|
|
}
|
|
Initialize();
|
|
}
|
|
if (name.Length <= RootPath.Length)
|
|
{
|
|
return root;
|
|
}
|
|
string[] path = name.Substring(RootPath.Length).Split(java.io.File.separatorChar);
|
|
VfsDirectory dir = root;
|
|
for (int i = 0; i < path.Length - 1; i++)
|
|
{
|
|
dir = dir.GetEntry(path[i]) as VfsDirectory;
|
|
if (dir == null)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
return dir.GetEntry(path[path.Length - 1]);
|
|
}
|
|
|
|
private sealed class ZipEntryStream : System.IO.Stream
|
|
{
|
|
private java.util.zip.ZipFile zipFile;
|
|
private java.util.zip.ZipEntry entry;
|
|
private java.io.InputStream inp;
|
|
private long position;
|
|
|
|
internal ZipEntryStream(java.util.zip.ZipFile zipFile, java.util.zip.ZipEntry entry)
|
|
{
|
|
this.zipFile = zipFile;
|
|
this.entry = entry;
|
|
inp = zipFile.getInputStream(entry);
|
|
}
|
|
|
|
public override bool CanRead
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
public override bool CanWrite
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool CanSeek
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
public override long Length
|
|
{
|
|
get { return entry.getSize(); }
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
// For compatibility with real file i/o, we try to read the requested number
|
|
// of bytes, instead of returning earlier if the underlying InputStream does so.
|
|
int totalRead = 0;
|
|
while (count > 0)
|
|
{
|
|
int read = inp.read(buffer, offset, count);
|
|
if (read <= 0)
|
|
{
|
|
break;
|
|
}
|
|
offset += read;
|
|
count -= read;
|
|
totalRead += read;
|
|
position += read;
|
|
}
|
|
return totalRead;
|
|
}
|
|
|
|
public override long Position
|
|
{
|
|
get
|
|
{
|
|
return position;
|
|
}
|
|
set
|
|
{
|
|
if (value < position)
|
|
{
|
|
if (value < 0)
|
|
{
|
|
throw new System.IO.IOException("Negative seek offset");
|
|
}
|
|
position = 0;
|
|
inp.close();
|
|
inp = zipFile.getInputStream(entry);
|
|
}
|
|
long skip = value - position;
|
|
while (skip > 0)
|
|
{
|
|
long skipped = inp.skip(skip);
|
|
if (skipped == 0)
|
|
{
|
|
if (position != entry.getSize())
|
|
{
|
|
throw new System.IO.IOException("skip failed");
|
|
}
|
|
// we're actually at EOF in the InputStream, but we set the virtual position beyond EOF
|
|
position += skip;
|
|
break;
|
|
}
|
|
position += skipped;
|
|
skip -= skipped;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
}
|
|
|
|
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
|
{
|
|
switch (origin)
|
|
{
|
|
case System.IO.SeekOrigin.Begin:
|
|
Position = offset;
|
|
break;
|
|
case System.IO.SeekOrigin.Current:
|
|
Position += offset;
|
|
break;
|
|
case System.IO.SeekOrigin.End:
|
|
Position = entry.getSize() + offset;
|
|
break;
|
|
}
|
|
return position;
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
public override void SetLength(long value)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
public override void Close()
|
|
{
|
|
base.Close();
|
|
inp.close();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
internal static System.IO.Stream Open(string name, System.IO.FileMode fileMode, System.IO.FileAccess fileAccess)
|
|
{
|
|
#if FIRST_PASS
|
|
return null;
|
|
#else
|
|
if (fileMode != System.IO.FileMode.Open || fileAccess != System.IO.FileAccess.Read)
|
|
{
|
|
throw new System.IO.IOException("vfs is read-only");
|
|
}
|
|
VfsFile entry = GetVfsEntry(name) as VfsFile;
|
|
if (entry == null)
|
|
{
|
|
throw new System.IO.FileNotFoundException("File not found");
|
|
}
|
|
return entry.Open();
|
|
#endif
|
|
}
|
|
|
|
internal static long GetLength(string path)
|
|
{
|
|
#if FIRST_PASS
|
|
return 0;
|
|
#else
|
|
VfsFile entry = GetVfsEntry(path) as VfsFile;
|
|
return entry == null ? 0 : entry.Size;
|
|
#endif
|
|
}
|
|
|
|
internal static bool CheckAccess(string path, int access)
|
|
{
|
|
#if FIRST_PASS
|
|
return false;
|
|
#else
|
|
return access == IKVM.NativeCode.java.io.Win32FileSystem.ACCESS_READ && GetVfsEntry(path) != null;
|
|
#endif
|
|
}
|
|
|
|
internal static int GetBooleanAttributes(string path)
|
|
{
|
|
#if FIRST_PASS
|
|
return 0;
|
|
#else
|
|
VfsEntry entry = GetVfsEntry(path);
|
|
if (entry == null)
|
|
{
|
|
return 0;
|
|
}
|
|
const int BA_EXISTS = 0x01;
|
|
const int BA_REGULAR = 0x02;
|
|
const int BA_DIRECTORY = 0x04;
|
|
return entry is VfsDirectory ? BA_EXISTS | BA_DIRECTORY : BA_EXISTS | BA_REGULAR;
|
|
#endif
|
|
}
|
|
|
|
internal static string MapExecutable(string path)
|
|
{
|
|
#if FIRST_PASS
|
|
return null;
|
|
#else
|
|
VfsExecutable entry = GetVfsEntry(path) as VfsExecutable;
|
|
if (entry == null)
|
|
{
|
|
return path;
|
|
}
|
|
return entry.GetPath();
|
|
#endif
|
|
}
|
|
|
|
internal static string[] List(string path)
|
|
{
|
|
#if FIRST_PASS
|
|
return null;
|
|
#else
|
|
VfsDirectory dir = GetVfsEntry(path) as VfsDirectory;
|
|
return dir == null ? null : dir.List();
|
|
#endif
|
|
}
|
|
}
|
|
}
|