// 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";
string file;
bool dirty;
+ static KeyHandler ()
+ {
+ CleanVolatileKeys ();
+ }
+
KeyHandler (RegistryKey rkey, string basedir) : this (rkey, basedir, false)
{
}
if (!Directory.Exists (actual_basedir)) {
try {
Directory.CreateDirectory (actual_basedir);
- } catch (UnauthorizedAccessException){
- throw new SecurityException ("No access to the given key");
+ } catch (UnauthorizedAccessException ex){
+ throw new SecurityException ("No access to the given key", ex);
}
}
Dir = basedir; // This is our identifier.
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 {
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)) {
{
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;
- string [] vals = new string [keys.Count];
- keys.CopyTo (vals, 0);
- return vals;
+ subKeyNames = new string [dirs.Count];
+ int j = 0;
+ foreach (KeyValuePair<string,string> entry in dirs)
+ subKeyNames[j++] = entry.Value;
+
+ 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);
return CreateSubKey (rkey, keyname, true);
}
-#if NET_4_0
public RegistryKey CreateSubKey (RegistryKey rkey, string keyname, RegistryOptions options)
{
return CreateSubKey (rkey, keyname, true, options == RegistryOptions.Volatile);
}
-#endif
public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
{
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)
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, bool is_volatile)
{
KeyHandler self = KeyHandler.Lookup (rkey, true);
- if (self == null)
+ if (self == null){
throw RegistryKey.CreateMarkedForDeletionException ();
+ }
if (KeyHandler.VolatileKeyExists (self.Dir) && !is_volatile)
throw new IOException ("Cannot create a non volatile subkey under a volatile key.");
// 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