[corlib] Disable a test that is incompatible with how nunitlite runs tests
[mono.git] / mcs / class / corlib / Microsoft.Win32 / UnixRegistryApi.cs
index f8f159ae7d39ee1300fd63a1aa12fc383d53a739..30a035593a8b4f458c2e3a7adea3d6600ca873ce 100644 (file)
 // 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;
@@ -48,6 +49,7 @@ using System.Runtime.InteropServices;
 using System.Reflection;
 using System.Security;
 using System.Threading;
+using Microsoft.Win32.SafeHandles;
 
 namespace Microsoft.Win32 {
 
@@ -91,28 +93,71 @@ 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 ();
                }
 
@@ -166,7 +211,7 @@ namespace Microsoft.Win32 {
                                        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);
@@ -175,27 +220,33 @@ namespace Microsoft.Win32 {
                                        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;
@@ -214,7 +265,7 @@ namespace Microsoft.Win32 {
                                        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);
@@ -232,7 +283,122 @@ namespace Microsoft.Win32 {
                        
                        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)){
@@ -270,6 +436,16 @@ namespace Microsoft.Win32 {
                        }
                }
 
+               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)) {
@@ -308,11 +484,30 @@ namespace Microsoft.Win32 {
                        }
                }
 
+               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;
 
@@ -338,7 +533,9 @@ namespace Microsoft.Win32 {
 
                        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;
@@ -355,12 +552,14 @@ namespace Microsoft.Win32 {
                        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 ();
                }
 
@@ -368,11 +567,53 @@ namespace Microsoft.Win32 {
                {
                        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;
                }
 
                //
@@ -385,59 +626,54 @@ namespace Microsoft.Win32 {
                        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");
                }
@@ -472,12 +708,14 @@ namespace Microsoft.Win32 {
                        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;
                        }
                }
 
@@ -491,7 +729,8 @@ namespace Microsoft.Win32 {
                {
                        AssertNotMarkedForDeletion ();
 
-                       values.Remove (name);
+                       lock (values)
+                               values.Remove (name);
                        SetDirty ();
                }
 
@@ -505,45 +744,47 @@ namespace Microsoft.Win32 {
                        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);
 
@@ -558,23 +799,31 @@ namespace Microsoft.Win32 {
                                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;
                        }
                }
        }
@@ -603,6 +852,11 @@ namespace Microsoft.Win32 {
                        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 ();
@@ -624,6 +878,11 @@ namespace Microsoft.Win32 {
 
                        return result;
                }
+
+               public RegistryKey FromHandle (SafeRegistryHandle handle)
+               {
+                       throw new NotImplementedException ();
+               }
                
                public void Flush (RegistryKey rkey)
                {
@@ -674,7 +933,7 @@ namespace Microsoft.Win32 {
                        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)
@@ -712,24 +971,14 @@ namespace Microsoft.Win32 {
 
                        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)
@@ -746,11 +995,20 @@ namespace Microsoft.Win32 {
                }
 
                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)
@@ -762,9 +1020,14 @@ namespace Microsoft.Win32 {
                        // 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