// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-#if !NET_2_1
+#if !MOBILE
using System;
using System.Collections;
+using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Reflection;
using System.Security;
using System.Threading;
+using Microsoft.Win32.SafeHandles;
namespace Microsoft.Win32 {
}
}
+ class RegistryKeyComparer : IEqualityComparer {
+ public new bool Equals(object x, object y)
+ {
+ return RegistryKey.IsEquals ((RegistryKey) x, (RegistryKey) y);
+
+ }
+
+ public int GetHashCode(object obj)
+ {
+ var n = ((RegistryKey) obj).Name;
+ if (n == null)
+ return 0;
+ return n.GetHashCode ();
+ }
+ }
+
class KeyHandler
{
- static Hashtable key_to_handler = new Hashtable ();
+ static Hashtable key_to_handler = new Hashtable (new RegistryKeyComparer ());
static Hashtable dir_to_handler = new Hashtable (
new CaseInsensitiveHashCodeProvider (), new CaseInsensitiveComparer ());
+ const string VolatileDirectoryName = "volatile-keys";
+
public string Dir;
+ string ActualDir; // Lets keep this one private.
+ public bool IsVolatile;
Hashtable values;
string file;
bool dirty;
- KeyHandler (RegistryKey rkey, string basedir)
+ static KeyHandler ()
+ {
+ CleanVolatileKeys ();
+ }
+
+ KeyHandler (RegistryKey rkey, string basedir) : this (rkey, basedir, false)
+ {
+ }
+
+ KeyHandler (RegistryKey rkey, string basedir, bool is_volatile)
{
- if (!Directory.Exists (basedir)){
+ // Force ourselved to reuse the key, if any.
+ string volatile_basedir = GetVolatileDir (basedir);
+ string actual_basedir = basedir;
+
+ if (Directory.Exists (basedir))
+ is_volatile = false;
+ else if (Directory.Exists (volatile_basedir)) {
+ actual_basedir = volatile_basedir;
+ is_volatile = true;
+ } else if (is_volatile)
+ actual_basedir = volatile_basedir;
+
+ if (!Directory.Exists (actual_basedir)) {
try {
- Directory.CreateDirectory (basedir);
- } catch (UnauthorizedAccessException){
- throw new SecurityException ("No access to the given key");
+ Directory.CreateDirectory (actual_basedir);
+ } catch (UnauthorizedAccessException ex){
+ throw new SecurityException ("No access to the given key", ex);
}
}
- Dir = basedir;
- file = Path.Combine (Dir, "values.xml");
+ Dir = basedir; // This is our identifier.
+ ActualDir = actual_basedir; // This our actual location.
+ IsVolatile = is_volatile;
+ file = Path.Combine (ActualDir, "values.xml");
Load ();
}
values [name] = Convert.FromBase64String (se.Text);
break;
case "string":
- values [name] = se.Text;
+ values [name] = se.Text == null ? String.Empty : se.Text;
break;
case "expand":
values [name] = new ExpandString (se.Text);
values [name] = Int64.Parse (se.Text);
break;
case "string-array":
- ArrayList sa = new ArrayList ();
+ var sa = new List<string> ();
if (se.Children != null){
foreach (SecurityElement stre in se.Children){
sa.Add (stre.Text);
}
}
- values [name] = sa.ToArray (typeof (string));
+ values [name] = sa.ToArray ();
break;
}
} catch {
// We ignore individual errors in the file.
}
}
-
+
public RegistryKey Ensure (RegistryKey rkey, string extra, bool writable)
+ {
+ return Ensure (rkey, extra, writable, false);
+ }
+
+ // 'is_volatile' is used only if the key hasn't been created already.
+ public RegistryKey Ensure (RegistryKey rkey, string extra, bool writable, bool is_volatile)
{
lock (typeof (KeyHandler)){
string f = Path.Combine (Dir, extra);
KeyHandler kh = (KeyHandler) dir_to_handler [f];
if (kh == null)
- kh = new KeyHandler (rkey, f);
+ kh = new KeyHandler (rkey, f, is_volatile);
RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra), writable);
key_to_handler [rk] = kh;
dir_to_handler [f] = kh;
rk = new RegistryKey (kh, CombineName (rkey,
extra), writable);
key_to_handler [rk] = kh;
- } else if (Directory.Exists (f)) {
+ } else if (Directory.Exists (f) || VolatileKeyExists (f)) {
kh = new KeyHandler (rkey, f);
rk = new RegistryKey (kh, CombineName (rkey, extra),
writable);
return String.Concat (rkey.Name, "\\", extra);
}
-
+
+ static long GetSystemBootTime ()
+ {
+ if (!File.Exists ("/proc/stat"))
+ return -1;
+
+ string btime = null;
+ string line;
+
+ try {
+ using (StreamReader stat_file = new StreamReader ("/proc/stat", Encoding.ASCII)) {
+ while ((line = stat_file.ReadLine ()) != null)
+ if (line.StartsWith ("btime")) {
+ btime = line;
+ break;
+ }
+ }
+ } catch (Exception e) {
+ Console.Error.WriteLine ("While reading system info {0}", e);
+ }
+
+ if (btime == null)
+ return -1;
+
+ int space = btime.IndexOf (' ');
+ long res;
+ if (!Int64.TryParse (btime.Substring (space, btime.Length - space), out res))
+ return -1;
+
+ return res;
+ }
+
+ // The registered boot time it's a simple line containing the last system btime.
+ static long GetRegisteredBootTime (string path)
+ {
+ if (!File.Exists (path))
+ return -1;
+
+ string line = null;
+ try {
+ using (StreamReader reader = new StreamReader (path, Encoding.ASCII))
+ line = reader.ReadLine ();
+ } catch (Exception e) {
+ Console.Error.WriteLine ("While reading registry data at {0}: {1}", path, e);
+ }
+
+ if (line == null)
+ return -1;
+
+ long res;
+ if (!Int64.TryParse (line, out res))
+ return -1;
+
+ return res;
+ }
+
+ static void SaveRegisteredBootTime (string path, long btime)
+ {
+ try {
+ using (StreamWriter writer = new StreamWriter (path, false, Encoding.ASCII))
+ writer.WriteLine (btime.ToString ());
+ } catch (Exception) {
+ /* This can happen when a user process tries to write to MachineStore */
+ //Console.Error.WriteLine ("While saving registry data at {0}: {1}", path, e);
+ }
+ }
+
+ // We save the last boot time in a last-btime file in every root, and we use it
+ // to clean the volatile keys directory in case the system btime changed.
+ static void CleanVolatileKeys ()
+ {
+ long system_btime = GetSystemBootTime ();
+
+ string [] roots = new string [] {
+ UserStore,
+ MachineStore
+ };
+
+ foreach (string root in roots) {
+ if (!Directory.Exists (root))
+ continue;
+
+ string btime_file = Path.Combine (root, "last-btime");
+ string volatile_dir = Path.Combine (root, VolatileDirectoryName);
+
+ if (Directory.Exists (volatile_dir)) {
+ long registered_btime = GetRegisteredBootTime (btime_file);
+ if (system_btime < 0 || registered_btime < 0 || registered_btime != system_btime)
+ Directory.Delete (volatile_dir, true);
+ }
+
+ SaveRegisteredBootTime (btime_file, system_btime);
+ }
+ }
+
+ public static bool VolatileKeyExists (string dir)
+ {
+ lock (typeof (KeyHandler)) {
+ KeyHandler kh = (KeyHandler) dir_to_handler [dir];
+ if (kh != null)
+ return kh.IsVolatile;
+ }
+
+ if (Directory.Exists (dir)) // Non-volatile key exists.
+ return false;
+
+ return Directory.Exists (GetVolatileDir (dir));
+ }
+
+ public static string GetVolatileDir (string dir)
+ {
+ string root = GetRootFromDir (dir);
+ string volatile_dir = dir.Replace (root, Path.Combine (root, VolatileDirectoryName));
+ return volatile_dir;
+ }
+
public static KeyHandler Lookup (RegistryKey rkey, bool createNonExisting)
{
lock (typeof (KeyHandler)){
}
}
+ static string GetRootFromDir (string dir)
+ {
+ if (dir.IndexOf (UserStore) > -1)
+ return UserStore;
+ else if (dir.IndexOf (MachineStore) > -1)
+ return MachineStore;
+
+ throw new Exception ("Could not get root for dir " + dir);
+ }
+
public static void Drop (RegistryKey rkey)
{
lock (typeof (KeyHandler)) {
}
}
+ public static bool Delete (string dir)
+ {
+ if (!Directory.Exists (dir)) {
+ string volatile_dir = GetVolatileDir (dir);
+ if (!Directory.Exists (volatile_dir))
+ return false;
+
+ dir = volatile_dir;
+ }
+
+ Directory.Delete (dir, true);
+ Drop (dir);
+ return true;
+ }
+
public RegistryValueKind GetValueKind (string name)
{
if (name == null)
return RegistryValueKind.Unknown;
- object value = values [name];
+ object value;
+
+ lock (values)
+ value = values [name];
+
if (value == null)
return RegistryValueKind.Unknown;
if (name == null)
name = string.Empty;
- object value = values [name];
+ object value;
+ lock (values)
+ value = values [name];
ExpandString exp = value as ExpandString;
if (exp == null)
return value;
if (name == null)
name = string.Empty;
- // immediately convert non-native registry values to string to avoid
- // returning it unmodified in calls to UnixRegistryApi.GetValue
- if (value is int || value is string || value is byte[] || value is string[])
- values[name] = value;
- else
- values[name] = value.ToString ();
+ lock (values){
+ // immediately convert non-native registry values to string to avoid
+ // returning it unmodified in calls to UnixRegistryApi.GetValue
+ if (value is int || value is string || value is byte[] || value is string[])
+ values[name] = value;
+ else
+ values[name] = value.ToString ();
+ }
SetDirty ();
}
{
AssertNotMarkedForDeletion ();
- ICollection keys = values.Keys;
+ lock (values){
+ ICollection keys = values.Keys;
+
+ string [] vals = new string [keys.Count];
+ keys.CopyTo (vals, 0);
+ return vals;
+ }
+ }
+
+ public int GetSubKeyCount ()
+ {
+ return GetSubKeyNames ().Length;
+ }
+
+ public string [] GetSubKeyNames ()
+ {
+ DirectoryInfo selfDir = new DirectoryInfo (ActualDir);
+ DirectoryInfo[] subDirs = selfDir.GetDirectories ();
+ string[] subKeyNames;
+
+ // for volatile keys (cannot contain non-volatile subkeys) or keys
+ // without *any* presence in the volatile key section, we can do it simple.
+ if (IsVolatile || !Directory.Exists (GetVolatileDir (Dir))) {
+ subKeyNames = new string[subDirs.Length];
+ for (int i = 0; i < subDirs.Length; i++) {
+ DirectoryInfo subDir = subDirs[i];
+ subKeyNames[i] = subDir.Name;
+ }
+ return subKeyNames;
+ }
+
+ // We may have the entries repeated, so keep just one of each one.
+ DirectoryInfo volatileDir = new DirectoryInfo (GetVolatileDir (Dir));
+ DirectoryInfo [] volatileSubDirs = volatileDir.GetDirectories ();
+ Dictionary<string,string> dirs = new Dictionary<string,string> ();
+
+ foreach (DirectoryInfo dir in subDirs)
+ dirs [dir.Name] = dir.Name;
+ foreach (DirectoryInfo volDir in volatileSubDirs)
+ dirs [volDir.Name] = volDir.Name;
+
+ subKeyNames = new string [dirs.Count];
+ int j = 0;
+ foreach (KeyValuePair<string,string> entry in dirs)
+ subKeyNames[j++] = entry.Value;
- string [] vals = new string [keys.Count];
- keys.CopyTo (vals, 0);
- return vals;
+ return subKeyNames;
}
//
if (name == null)
name = string.Empty;
- switch (valueKind){
- case RegistryValueKind.String:
- if (value is string){
- values [name] = value;
- return;
- }
- break;
- case RegistryValueKind.ExpandString:
- if (value is string){
- values [name] = new ExpandString ((string)value);
- return;
- }
- break;
-
- case RegistryValueKind.Binary:
- if (value is byte []){
- values [name] = value;
- return;
- }
- break;
-
- case RegistryValueKind.DWord:
- if (value is long &&
- (((long) value) < Int32.MaxValue) &&
- (((long) value) > Int32.MinValue)){
- values [name] = (int) ((long)value);
- return;
- }
- if (value is int){
- values [name] = value;
- return;
- }
- break;
-
- case RegistryValueKind.MultiString:
- if (value is string []){
- values [name] = value;
- return;
- }
- break;
-
- case RegistryValueKind.QWord:
- if (value is int){
- values [name] = (long) ((int) value);
- return;
- }
- if (value is long){
- values [name] = value;
- return;
+ lock (values){
+ switch (valueKind){
+ case RegistryValueKind.String:
+ if (value is string){
+ values [name] = value;
+ return;
+ }
+ break;
+ case RegistryValueKind.ExpandString:
+ if (value is string){
+ values [name] = new ExpandString ((string)value);
+ return;
+ }
+ break;
+
+ case RegistryValueKind.Binary:
+ if (value is byte []){
+ values [name] = value;
+ return;
+ }
+ break;
+
+ case RegistryValueKind.DWord:
+ try {
+ values [name] = Convert.ToInt32 (value);
+ return;
+ } catch (OverflowException) {
+ break;
+ }
+
+ case RegistryValueKind.MultiString:
+ if (value is string []){
+ values [name] = value;
+ return;
+ }
+ break;
+
+ case RegistryValueKind.QWord:
+ try {
+ values [name] = Convert.ToInt64 (value);
+ return;
+ } catch (OverflowException) {
+ break;
+ }
+
+ default:
+ throw new ArgumentException ("unknown value", "valueKind");
}
- break;
- default:
- throw new ArgumentException ("unknown value", "valueKind");
}
throw new ArgumentException ("Value could not be converted to specified type", "valueKind");
}
if (name == null)
name = string.Empty;
- return values.Contains (name);
+ lock (values)
+ return values.Contains (name);
}
public int ValueCount {
get {
- return values.Keys.Count;
+ lock (values)
+ return values.Keys.Count;
}
}
{
AssertNotMarkedForDeletion ();
- values.Remove (name);
+ lock (values)
+ values.Remove (name);
SetDirty ();
}
if (IsMarkedForDeletion)
return;
- if (!File.Exists (file) && values.Count == 0)
- return;
-
SecurityElement se = new SecurityElement ("values");
-
- // With SecurityElement.Text = value, and SecurityElement.AddAttribute(key, value)
- // the values must be escaped prior to being assigned.
- foreach (DictionaryEntry de in values){
- object val = de.Value;
- SecurityElement value = new SecurityElement ("value");
- value.AddAttribute ("name", SecurityElement.Escape ((string) de.Key));
- if (val is string){
- value.AddAttribute ("type", "string");
- value.Text = SecurityElement.Escape ((string) val);
- } else if (val is int){
- value.AddAttribute ("type", "int");
- value.Text = val.ToString ();
- } else if (val is long) {
- value.AddAttribute ("type", "qword");
- value.Text = val.ToString ();
- } else if (val is byte []){
- value.AddAttribute ("type", "bytearray");
- value.Text = Convert.ToBase64String ((byte[]) val);
- } else if (val is ExpandString){
- value.AddAttribute ("type", "expand");
- value.Text = SecurityElement.Escape (val.ToString ());
- } else if (val is string []){
- value.AddAttribute ("type", "string-array");
-
- foreach (string ss in (string[]) val){
- SecurityElement str = new SecurityElement ("string");
- str.Text = SecurityElement.Escape (ss);
- value.AddChild (str);
+ lock (values){
+ if (!File.Exists (file) && values.Count == 0)
+ return;
+
+ // With SecurityElement.Text = value, and SecurityElement.AddAttribute(key, value)
+ // the values must be escaped prior to being assigned.
+ foreach (DictionaryEntry de in values){
+ object val = de.Value;
+ SecurityElement value = new SecurityElement ("value");
+ value.AddAttribute ("name", SecurityElement.Escape ((string) de.Key));
+
+ if (val is string){
+ value.AddAttribute ("type", "string");
+ value.Text = SecurityElement.Escape ((string) val);
+ } else if (val is int){
+ value.AddAttribute ("type", "int");
+ value.Text = val.ToString ();
+ } else if (val is long) {
+ value.AddAttribute ("type", "qword");
+ value.Text = val.ToString ();
+ } else if (val is byte []){
+ value.AddAttribute ("type", "bytearray");
+ value.Text = Convert.ToBase64String ((byte[]) val);
+ } else if (val is ExpandString){
+ value.AddAttribute ("type", "expand");
+ value.Text = SecurityElement.Escape (val.ToString ());
+ } else if (val is string []){
+ value.AddAttribute ("type", "string-array");
+
+ foreach (string ss in (string[]) val){
+ SecurityElement str = new SecurityElement ("string");
+ str.Text = SecurityElement.Escape (ss);
+ value.AddChild (str);
+ }
}
+ se.AddChild (value);
}
- se.AddChild (value);
}
-
+
using (FileStream fs = File.Create (file)){
StreamWriter sw = new StreamWriter (fs);
throw RegistryKey.CreateMarkedForDeletionException ();
}
+ static string user_store;
+ static string machine_store;
+
private static string UserStore {
get {
- return Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal),
+ if (user_store == null)
+ user_store = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal),
".mono/registry");
+
+ return user_store;
}
}
private static string MachineStore {
get {
- string s;
+ if (machine_store == null) {
+ machine_store = Environment.GetEnvironmentVariable ("MONO_REGISTRY_PATH");
+ if (machine_store == null) {
+ string s = Environment.GetMachineConfigPath ();
+ int p = s.IndexOf ("machine.config");
+ machine_store = Path.Combine (Path.Combine (s.Substring (0, p-1), ".."), "registry");
+ }
+ }
- s = Environment.GetEnvironmentVariable ("MONO_REGISTRY_PATH");
- if (s != null)
- return s;
- s = Environment.GetMachineConfigPath ();
- int p = s.IndexOf ("machine.config");
- return Path.Combine (Path.Combine (s.Substring (0, p-1), ".."), "registry");
+ return machine_store;
}
}
}
return CreateSubKey (rkey, keyname, true);
}
+ public RegistryKey CreateSubKey (RegistryKey rkey, string keyname, RegistryOptions options)
+ {
+ return CreateSubKey (rkey, keyname, true, options == RegistryOptions.Volatile);
+ }
+
public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
{
throw new NotImplementedException ();
return result;
}
+
+ public RegistryKey FromHandle (SafeRegistryHandle handle)
+ {
+ throw new NotImplementedException ();
+ }
public void Flush (RegistryKey rkey)
{
KeyHandler self = KeyHandler.Lookup (rkey, true);
if (self == null)
throw RegistryKey.CreateMarkedForDeletionException ();
- return Directory.GetDirectories (self.Dir).Length;
+ return self.GetSubKeyCount ();
}
public int ValueCount (RegistryKey rkey)
string dir = Path.Combine (self.Dir, ToUnix (keyname));
- if (Directory.Exists (dir)){
- Directory.Delete (dir, true);
- KeyHandler.Drop (dir);
- } else if (throw_if_missing)
+ if (!KeyHandler.Delete (dir) && throw_if_missing)
throw new ArgumentException ("the given value does not exist");
}
public string [] GetSubKeyNames (RegistryKey rkey)
{
KeyHandler self = KeyHandler.Lookup (rkey, true);
- DirectoryInfo selfDir = new DirectoryInfo (self.Dir);
- DirectoryInfo[] subDirs = selfDir.GetDirectories ();
- string[] subKeyNames = new string[subDirs.Length];
- for (int i = 0; i < subDirs.Length; i++) {
- DirectoryInfo subDir = subDirs[i];
- subKeyNames[i] = subDir.Name;
- }
- return subKeyNames;
+ return self.GetSubKeyNames ();
}
public string [] GetValueNames (RegistryKey rkey)
}
private RegistryKey CreateSubKey (RegistryKey rkey, string keyname, bool writable)
+ {
+ return CreateSubKey (rkey, keyname, writable, false);
+ }
+
+ private RegistryKey CreateSubKey (RegistryKey rkey, string keyname, bool writable, bool is_volatile)
{
KeyHandler self = KeyHandler.Lookup (rkey, true);
- if (self == null)
+ if (self == null){
throw RegistryKey.CreateMarkedForDeletionException ();
- return self.Ensure (rkey, ToUnix (keyname), writable);
+ }
+ if (KeyHandler.VolatileKeyExists (self.Dir) && !is_volatile)
+ throw new IOException ("Cannot create a non volatile subkey under a volatile key.");
+
+ return self.Ensure (rkey, ToUnix (keyname), writable, is_volatile);
}
public RegistryValueKind GetValueKind (RegistryKey rkey, string name)
// key was removed since it was opened or it does not exist.
return RegistryValueKind.Unknown;
}
+
+ public IntPtr GetHandle (RegistryKey key)
+ {
+ throw new NotImplementedException ();
+ }
}
}
-#endif // NET_2_1
+#endif // MOBILE