using System;
using System.IO;
+using System.Diagnostics;
using System.Text;
using System.Reflection;
using System.Collections;
using System.Globalization;
using System.Runtime.InteropServices;
+using System.Security.Cryptography;
using Mono.Security;
+using Mono.Security.Cryptography;
namespace Mono.Tools {
Help
}
+ private enum VerificationResult
+ {
+ StrongNamed,
+ WeakNamed,
+ DelaySigned,
+ Skipped
+ }
+
private static bool silent;
+ static bool in_bootstrap;
public static int Main (string [] args)
{
continue;
}
+ if (args [i] == "-bootstrap" || args [i] == "/bootstrap") {
+ in_bootstrap = true;
+ continue;
+ }
+
if (command == Command.Unknown) {
command = GetCommand (args [i]);
if (command != Command.Unknown) {
case "/gacdir":
gacdir = args [++i];
continue;
+ case "/nologo":
+ case "-nologo":
+ // we currently don't display a
+ // logo banner, so ignore it
+ // for command-line compatibility
+ // with MS gacutil
+ continue;
}
}
if (name == null)
gacdir = Path.Combine (libdir, "gac");
}
+ LoadConfig (silent);
+
switch (command) {
case Command.Install:
if (name == null) {
WriteLine ("Option " + command_str + " takes 1 argument");
return 1;
}
- if (!Uninstall (name, package, gacdir, libdir))
- Environment.Exit (1);
+ int uninstallCount = 0;
+ int uninstallFailures = 0;
+ Uninstall (name, package, gacdir, libdir, false,
+ ref uninstallCount, ref uninstallFailures);
+ WriteLine ("Assemblies uninstalled = {0}", uninstallCount);
+ WriteLine ("Failures = {0}", uninstallFailures);
+ if (uninstallFailures > 0)
+ return 1;
break;
case Command.UninstallFromList:
if (name == null) {
private static bool Install (bool check_refs, string name, string package,
string gacdir, string link_gacdir, string libdir, string link_libdir)
{
- string failure_msg = "Failure adding assembly to the cache: ";
+ string failure_msg = "Failure adding assembly {0} to the cache: ";
ArrayList resources;
if (!File.Exists (name)) {
- WriteLine (failure_msg + "The system cannot find the file specified.");
- Environment.Exit (1);
+ WriteLine (string.Format (failure_msg, name) + "The system cannot find the file specified.");
+ return false;
}
Assembly assembly = null;
AssemblyName an = null;
- byte [] pub_tok;
try {
assembly = Assembly.LoadFrom (name);
} catch {
- WriteLine (failure_msg + "The file specified is not a valid assembly.");
+ WriteLine (string.Format (failure_msg, name) + "The file specified is not a valid assembly.");
return false;
}
an = assembly.GetName ();
- pub_tok = an.GetPublicKeyToken ();
- if (pub_tok == null || pub_tok.Length == 0) {
- WriteLine (failure_msg + "Attempt to install an assembly without a strong name.");
- return false;
+
+ switch (VerifyStrongName (an, name)) {
+ case VerificationResult.StrongNamed:
+ case VerificationResult.Skipped:
+ break;
+ case VerificationResult.WeakNamed:
+ WriteLine (string.Format (failure_msg, name) + "Attempt to install an assembly without a strong name"
+ + (in_bootstrap ? "(continuing anyway)" : string.Empty));
+ if (!in_bootstrap)
+ return false;
+ break;
+ case VerificationResult.DelaySigned:
+ WriteLine (string.Format (failure_msg, name) + "Strong name cannot be verified for delay-signed assembly"
+ + (in_bootstrap ? "(continuing anyway)" : string.Empty));
+ if (!in_bootstrap)
+ return false;
+ break;
}
resources = new ArrayList ();
if ((res_info.ResourceLocation & ResourceLocation.Embedded) == 0) {
if (!File.Exists (res_info.FileName)) {
- WriteLine (failure_msg + "The system cannot find resource " + res_info.FileName);
+ WriteLine (string.Format (failure_msg, name) + "The system cannot find resource " + res_info.FileName);
return false;
}
}
if (check_refs && !CheckReferencedAssemblies (an)) {
- WriteLine (failure_msg + "Attempt to install an assembly that references non " +
- "strong named assemblies with -check_refs enabled.");
+ WriteLine (string.Format (failure_msg, name) +
+ "Attempt to install an assembly that " +
+ "references non strong named assemblies " +
+ "with -check_refs enabled.");
return false;
}
string [] siblings = { ".config", ".mdb" };
string version_token = an.Version + "_" +
an.CultureInfo.Name.ToLower (CultureInfo.InvariantCulture) + "_" +
- GetStringToken (pub_tok);
+ GetStringToken (an.GetPublicKeyToken ());
string full_path = Path.Combine (Path.Combine (gacdir, an.Name), version_token);
string asmb_file = Path.GetFileName (name);
string asmb_path = Path.Combine (full_path, asmb_file);
+ string asmb_name = assembly.GetName ().Name;
+
+ if (Path.GetFileNameWithoutExtension (asmb_file) != asmb_name) {
+ WriteLine (string.Format (failure_msg, name) +
+ string.Format ("the filename \"{0}\" doesn't match the assembly name \"{1}\"",
+ asmb_file, asmb_name));
+ return false;
+ }
try {
if (Directory.Exists (full_path)) {
}
Directory.CreateDirectory (full_path);
} catch {
- WriteLine (failure_msg + "gac directories could not be created, " +
- "possibly permission issues.");
+ WriteLine (string.Format (failure_msg, name) +
+ "gac directories could not be created, " +
+ "possibly permission issues.");
return false;
}
Copy (name, asmb_path, true);
+ var name_pdb = Path.ChangeExtension (name, ".pdb");
+ if (File.Exists (name_pdb)) {
+ Copy (name_pdb, Path.ChangeExtension (asmb_path, ".pdb"), true);
+ }
+
foreach (string ext in siblings) {
string sibling = String.Concat (name, ext);
if (File.Exists (sibling))
Environment.Exit (1);
}
if (Path.DirectorySeparatorChar == '/') {
- string pkg_path = "../gac/" + an.Name + "/" + version_token + "/" + asmb_file;
+ string pkg_path_abs = Path.Combine (gacdir, Path.Combine (an.Name, Path.Combine (version_token, asmb_file)));
+ string pkg_path = AbsoluteToRelativePath (ref_dir, pkg_path_abs);
symlink (pkg_path, ref_path);
+ var pdb_pkg_path = Path.ChangeExtension (pkg_path, ".pdb");
+ var pdb_ref_path = Path.ChangeExtension (ref_path, ".pdb");
+
+ if (File.Exists (pdb_pkg_path)) {
+ symlink (pdb_pkg_path, pdb_ref_path);
+ } else {
+ try {
+ File.Delete (pdb_ref_path);
+ } catch {
+ // Ignore error, just delete files that should not be there.
+ }
+ }
+
foreach (string ext in siblings) {
string sibling = String.Concat (pkg_path, ext);
string sref = String.Concat (ref_path, ext);
+
if (File.Exists (sibling))
symlink (sibling, sref);
else {
}
}
WriteLine ("Package exported to: {0} -> {1}", ref_path, pkg_path);
- } else {
+ } else {
// string link_path = Path.Combine (Path.Combine (link_gacdir, an.Name), version_token);
//
// We can't use 'link_path' here, since it need not be a valid path at the time 'gacutil'
}
}
- WriteLine ("{0} installed into the gac ({1})", an.Name, gacdir);
+ WriteLine ("Installed {0} into the gac ({1})", name,
+ gacdir);
+
return true;
}
- private static bool Uninstall (string name, string package, string gacdir, string libdir)
+ //from MonoDevelop.Core.FileService
+ unsafe static string AbsoluteToRelativePath (string baseDirectoryPath, string absPath)
+ {
+ if (!Path.IsPathRooted (absPath) || string.IsNullOrEmpty (baseDirectoryPath))
+ return absPath;
+
+ absPath = Path.GetFullPath (absPath);
+ baseDirectoryPath = Path.GetFullPath (baseDirectoryPath).TrimEnd (Path.DirectorySeparatorChar);
+
+ fixed (char* bPtr = baseDirectoryPath, aPtr = absPath) {
+ var bEnd = bPtr + baseDirectoryPath.Length;
+ var aEnd = aPtr + absPath.Length;
+ char* lastStartA = aEnd;
+ char* lastStartB = bEnd;
+
+ int indx = 0;
+ // search common base path
+ var a = aPtr;
+ var b = bPtr;
+ while (a < aEnd) {
+ if (*a != *b)
+ break;
+ if (IsSeparator (*a)) {
+ indx++;
+ lastStartA = a + 1;
+ lastStartB = b;
+ }
+ a++;
+ b++;
+ if (b >= bEnd) {
+ if (a >= aEnd || IsSeparator (*a)) {
+ indx++;
+ lastStartA = a + 1;
+ lastStartB = b;
+ }
+ break;
+ }
+ }
+ if (indx == 0)
+ return absPath;
+
+ if (lastStartA >= aEnd)
+ return ".";
+
+ // handle case a: some/path b: some/path/deeper...
+ if (a >= aEnd) {
+ if (IsSeparator (*b)) {
+ lastStartA = a + 1;
+ lastStartB = b;
+ }
+ }
+
+ // look how many levels to go up into the base path
+ int goUpCount = 0;
+ while (lastStartB < bEnd) {
+ if (IsSeparator (*lastStartB))
+ goUpCount++;
+ lastStartB++;
+ }
+ var size = goUpCount * 2 + goUpCount + aEnd - lastStartA;
+ var result = new char [size];
+ fixed (char* rPtr = result) {
+ // go paths up
+ var r = rPtr;
+ for (int i = 0; i < goUpCount; i++) {
+ *(r++) = '.';
+ *(r++) = '.';
+ *(r++) = Path.DirectorySeparatorChar;
+ }
+ // copy the remaining absulute path
+ while (lastStartA < aEnd)
+ *(r++) = *(lastStartA++);
+ }
+ return new string (result);
+ }
+ }
+
+ static bool IsSeparator (char ch)
+ {
+ return ch == Path.DirectorySeparatorChar || ch == Path.AltDirectorySeparatorChar || ch == Path.VolumeSeparatorChar;
+ }
+
+ private static void Uninstall (string name, string package, string gacdir, string libdir, bool listMode, ref int uninstalled, ref int failures)
{
string [] assembly_pieces = name.Split (new char[] { ',' });
Hashtable asm_info = new Hashtable ();
asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
}
- string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]);
+ string assembly_name = (string) asm_info ["assembly"];
+ string asmdir = Path.Combine (gacdir, assembly_name);
if (!Directory.Exists (asmdir)) {
+ if (listMode) {
+ failures++;
+ WriteLine ("Assembly: " + name);
+ }
WriteLine ("No assemblies found that match: " + name);
- return false;
+ return;
}
string searchString = GetSearchString (asm_info);
string [] directories = Directory.GetDirectories (asmdir, searchString);
- foreach (string dir in directories) {
+ if (directories.Length == 0) {
+ if (listMode) {
+ failures++;
+ WriteLine ("Assembly: " + name);
+ WriteLine ("No assemblies found that match: " + name);
+ }
+ return;
+ }
+
+ for (int i = 0; i < directories.Length; i++) {
+ if (listMode && i > 0)
+ break;
+
+ string dir = directories [i];
+ string extension = null;
+
+ if (File.Exists (Path.Combine (dir, assembly_name + ".dll"))) {
+ extension = ".dll";
+ } else if (File.Exists (Path.Combine (dir, assembly_name + ".exe"))) {
+ extension = ".exe";
+ } else {
+ failures++;
+ WriteLine("Cannot find the assembly: " + assembly_name);
+ continue;
+ }
+
+ string assembly_filename = assembly_name + extension;
+
+ AssemblyName an = AssemblyName.GetAssemblyName (
+ Path.Combine (dir, assembly_filename));
+ WriteLine ("Assembly: " + an.FullName);
+
Directory.Delete (dir, true);
if (package != null) {
string link_dir = Path.Combine (libdir, package);
- string link = Path.Combine (link_dir, (string) asm_info ["assembly"] + ".dll");
- File.Delete (link);
+ string link = Path.Combine (link_dir, assembly_filename);
+
+ try {
+ File.Delete (link);
+ } catch {
+ // The file might not exist, happens with
+ // the debugger on make uninstall
+ }
+
if (Directory.GetFiles (link_dir).Length == 0) {
WriteLine ("Cleaning package directory, it is empty.");
try {
}
}
}
- WriteLine ("Assembly removed from the gac.");
+
+ uninstalled++;
+ WriteLine ("Uninstalled: " + an.FullName);
}
- if(Directory.GetDirectories (asmdir).Length == 0) {
+ if (Directory.GetDirectories (asmdir).Length == 0) {
WriteLine ("Cleaning assembly dir, it is empty");
try {
Directory.Delete (asmdir);
// Workaround: GetFiles does not list Symlinks
}
}
-
- return true;
}
private static bool UninstallSpecific (string name, string package,
return false;
}
- return Uninstall (an.FullName.Replace (" ", String.Empty),
- package, gacdir, libdir);
+ int uninstallCount = 0;
+ int uninstallFailures = 0;
+ Uninstall (an.FullName.Replace (" ", String.Empty),
+ package, gacdir, libdir, true, ref uninstallCount,
+ ref uninstallFailures);
+ WriteLine ("Assemblies uninstalled = {0}", uninstallCount);
+ WriteLine ("Failures = {0}", uninstallFailures);
+ return (uninstallFailures == 0);
}
private static void List (string name, string gacdir)
{
StreamReader s = null;
int processed, failed;
+ string listdir = Path.GetDirectoryName (
+ Path.GetFullPath (list_file));
processed = failed = 0;
string line;
while ((line = s.ReadLine ()) != null) {
- if (!Install (check_refs, line, package, gacdir, link_gacdir,
- libdir, link_libdir))
+ string file = line.Trim ();
+ if (file.Length == 0)
+ continue;
+
+ string assemblyPath = Path.Combine (listdir,
+ file);
+
+ if (!Install (check_refs, assemblyPath, package, gacdir,
+ link_gacdir, libdir, link_libdir))
failed++;
processed++;
-
}
- } catch (IOException ioe) {
+
+ WriteLine ("Assemblies processed = {0}", processed);
+ WriteLine ("Assemblies installed = {0}", processed - failed);
+ WriteLine ("Failures = {0}", failed);
+
+ return (failed == 0);
+ } catch (IOException) {
WriteLine ("Failed to open assemblies list file " + list_file + ".");
return false;
} finally {
if (s != null)
s.Close ();
}
-
- return true;
}
private static bool UninstallFromList (string list_file, string package,
string gacdir, string libdir)
{
StreamReader s = null;
- int processed, failed;
+ int failed, uninstalled;
- processed = failed = 0;
+ failed = uninstalled = 0;
try {
s = new StreamReader (list_file);
string line;
while ((line = s.ReadLine ()) != null) {
- if (!Uninstall (line, package, gacdir, libdir))
- failed++;
- processed++;
+ string name = line.Trim ();
+ if (name.Length == 0)
+ continue;
+ Uninstall (line, package, gacdir, libdir,
+ true, ref uninstalled, ref failed);
}
- } catch (IOException ioe) {
+
+ WriteLine ("Assemblies processed = {0}", uninstalled+failed);
+ WriteLine ("Assemblies uninstalled = {0}", uninstalled);
+ WriteLine ("Failures = {0}", failed);
+
+ return (failed == 0);
+ } catch (IOException) {
WriteLine ("Failed to open assemblies list file " + list_file + ".");
return false;
} finally {
if (s != null)
s.Close ();
}
-
- return true;
}
private static bool CheckReferencedAssemblies (AssemblyName an)
return false;
}
}
- } catch (Exception e) {
+ } catch (Exception e) {
WriteLine (e.ToString ()); // This should be removed pre beta3
return false;
} finally {
string version, culture, token;
version = asm_info ["version"] as string;
- version = (version == null ? "*" : version);
+ version = (version == null ? "*" : version + "*");
culture = asm_info ["culture"] as string;
culture = (culture == null ? "*" : (culture == "neutral") ? String.Empty : culture.ToLower (CultureInfo.InvariantCulture));
token = asm_info ["publickeytoken"] as string;
pieces [2]);
}
+ static bool LoadConfig (bool quiet)
+ {
+ MethodInfo config = typeof (System.Environment).GetMethod ("GetMachineConfigPath",
+ BindingFlags.Static | BindingFlags.NonPublic);
+
+ if (config != null) {
+ string path = (string) config.Invoke (null, null);
+
+ bool exist = File.Exists (path);
+ if (!quiet && !exist)
+ Console.WriteLine ("Couldn't find machine.config");
+
+ StrongNameManager.LoadConfig (path);
+ return exist;
+ } else if (!quiet)
+ Console.WriteLine ("Couldn't resolve machine.config location (corlib issue)");
+
+ // default CSP
+ return false;
+ }
+
+ // modified copy from sn
+ private static VerificationResult VerifyStrongName (AssemblyName an, string assemblyFile)
+ {
+ byte [] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
+ if ((publicKey == null) || (publicKey.Length < 12)) {
+ // no mapping
+ publicKey = an.GetPublicKey ();
+ if ((publicKey == null) || (publicKey.Length < 12))
+ return VerificationResult.WeakNamed;
+ }
+
+ // Note: MustVerify is based on the original token (by design). Public key
+ // remapping won't affect if the assembly is verified or not.
+ if (StrongNameManager.MustVerify (an)) {
+ RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
+ StrongName sn = new StrongName (rsa);
+ if (sn.Verify (assemblyFile)) {
+ return VerificationResult.StrongNamed;
+ } else {
+ return VerificationResult.DelaySigned;
+ }
+ } else {
+ return VerificationResult.Skipped;
+ }
+ }
+
private static bool IsSwitch (string arg)
{
- return (arg [0] == '-' || arg [0] == '/');
+ return (arg [0] == '-' || (arg [0] == '/' && !arg.EndsWith (".dll") && !arg.EndsWith (".exe") && arg.IndexOf ('/', 1) < 0 ) );
}
private static Command GetCommand (string arg)
return sb.ToString ();
}
- private static string CombinePaths (string a, string b)
- {
- string dsc = Path.DirectorySeparatorChar.ToString ();
- string sep = (a.EndsWith (dsc) ? String.Empty : dsc);
- string end = (b.StartsWith (dsc) ? b.Substring (1) : b);
- return String.Concat (a, sep, end);
- }
-
private static string EnsureLib (string dir)
{
DirectoryInfo d = new DirectoryInfo (dir);