PageRenderTime 72ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/corlib/Microsoft.Win32/UnixRegistryApi.cs

https://bitbucket.org/danipen/mono
C# | 1004 lines | 935 code | 28 blank | 41 comment | 42 complexity | dd6a4f977dd5a804bde7069f5f356781 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //
  2. // Microsoft.Win32/UnixRegistryApi.cs
  3. //
  4. // Authors:
  5. // Miguel de Icaza (miguel@gnome.org)
  6. // Gert Driesen (drieseng@users.sourceforge.net)
  7. //
  8. // (C) 2005, 2006 Novell, Inc (http://www.novell.com)
  9. //
  10. // MISSING:
  11. // It would be useful if we do case-insensitive expansion of variables,
  12. // the registry is very windows specific, so we probably should default to
  13. // those semantics in expanding environment variables, for example %path%
  14. //
  15. // We should use an ordered collection for storing the values (instead of
  16. // a Hashtable).
  17. //
  18. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  19. //
  20. // Permission is hereby granted, free of charge, to any person obtaining
  21. // a copy of this software and associated documentation files (the
  22. // "Software"), to deal in the Software without restriction, including
  23. // without limitation the rights to use, copy, modify, merge, publish,
  24. // distribute, sublicense, and/or sell copies of the Software, and to
  25. // permit persons to whom the Software is furnished to do so, subject to
  26. // the following conditions:
  27. //
  28. // The above copyright notice and this permission notice shall be
  29. // included in all copies or substantial portions of the Software.
  30. //
  31. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  32. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  33. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  34. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  35. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  36. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  37. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  38. //
  39. #if !NET_2_1
  40. using System;
  41. using System.Collections;
  42. using System.Collections.Generic;
  43. using System.Globalization;
  44. using System.IO;
  45. using System.Text;
  46. using System.Runtime.InteropServices;
  47. using System.Reflection;
  48. using System.Security;
  49. using System.Threading;
  50. using Microsoft.Win32.SafeHandles;
  51. namespace Microsoft.Win32 {
  52. class ExpandString {
  53. string value;
  54. public ExpandString (string s)
  55. {
  56. value = s;
  57. }
  58. public override string ToString ()
  59. {
  60. return value;
  61. }
  62. public string Expand ()
  63. {
  64. StringBuilder sb = new StringBuilder ();
  65. for (int i = 0; i < value.Length; i++){
  66. if (value [i] == '%'){
  67. int j = i + 1;
  68. for (; j < value.Length; j++){
  69. if (value [j] == '%'){
  70. string key = value.Substring (i + 1, j - i - 1);
  71. sb.Append (Environment.GetEnvironmentVariable (key));
  72. i += j;
  73. break;
  74. }
  75. }
  76. if (j == value.Length){
  77. sb.Append ('%');
  78. }
  79. } else {
  80. sb.Append (value [i]);
  81. }
  82. }
  83. return sb.ToString ();
  84. }
  85. }
  86. class KeyHandler
  87. {
  88. static Hashtable key_to_handler = new Hashtable ();
  89. static Hashtable dir_to_handler = new Hashtable (
  90. new CaseInsensitiveHashCodeProvider (), new CaseInsensitiveComparer ());
  91. const string VolatileDirectoryName = "volatile-keys";
  92. public string Dir;
  93. string ActualDir; // Lets keep this one private.
  94. public bool IsVolatile;
  95. Hashtable values;
  96. string file;
  97. bool dirty;
  98. static KeyHandler ()
  99. {
  100. CleanVolatileKeys ();
  101. }
  102. KeyHandler (RegistryKey rkey, string basedir) : this (rkey, basedir, false)
  103. {
  104. }
  105. KeyHandler (RegistryKey rkey, string basedir, bool is_volatile)
  106. {
  107. // Force ourselved to reuse the key, if any.
  108. string volatile_basedir = GetVolatileDir (basedir);
  109. string actual_basedir = basedir;
  110. if (Directory.Exists (basedir))
  111. is_volatile = false;
  112. else if (Directory.Exists (volatile_basedir)) {
  113. actual_basedir = volatile_basedir;
  114. is_volatile = true;
  115. } else if (is_volatile)
  116. actual_basedir = volatile_basedir;
  117. if (!Directory.Exists (actual_basedir)) {
  118. try {
  119. Directory.CreateDirectory (actual_basedir);
  120. } catch (UnauthorizedAccessException){
  121. throw new SecurityException ("No access to the given key");
  122. }
  123. }
  124. Dir = basedir; // This is our identifier.
  125. ActualDir = actual_basedir; // This our actual location.
  126. IsVolatile = is_volatile;
  127. file = Path.Combine (ActualDir, "values.xml");
  128. Load ();
  129. }
  130. public void Load ()
  131. {
  132. values = new Hashtable ();
  133. if (!File.Exists (file))
  134. return;
  135. try {
  136. using (FileStream fs = File.OpenRead (file)){
  137. StreamReader r = new StreamReader (fs);
  138. string xml = r.ReadToEnd ();
  139. if (xml.Length == 0)
  140. return;
  141. SecurityElement tree = SecurityElement.FromString (xml);
  142. if (tree.Tag == "values" && tree.Children != null){
  143. foreach (SecurityElement value in tree.Children){
  144. if (value.Tag == "value"){
  145. LoadKey (value);
  146. }
  147. }
  148. }
  149. }
  150. } catch (UnauthorizedAccessException){
  151. values.Clear ();
  152. throw new SecurityException ("No access to the given key");
  153. } catch (Exception e){
  154. Console.Error.WriteLine ("While loading registry key at {0}: {1}", file, e);
  155. values.Clear ();
  156. }
  157. }
  158. void LoadKey (SecurityElement se)
  159. {
  160. Hashtable h = se.Attributes;
  161. try {
  162. string name = (string) h ["name"];
  163. if (name == null)
  164. return;
  165. string type = (string) h ["type"];
  166. if (type == null)
  167. return;
  168. switch (type){
  169. case "int":
  170. values [name] = Int32.Parse (se.Text);
  171. break;
  172. case "bytearray":
  173. values [name] = Convert.FromBase64String (se.Text);
  174. break;
  175. case "string":
  176. values [name] = se.Text == null ? String.Empty : se.Text;
  177. break;
  178. case "expand":
  179. values [name] = new ExpandString (se.Text);
  180. break;
  181. case "qword":
  182. values [name] = Int64.Parse (se.Text);
  183. break;
  184. case "string-array":
  185. var sa = new List<string> ();
  186. if (se.Children != null){
  187. foreach (SecurityElement stre in se.Children){
  188. sa.Add (stre.Text);
  189. }
  190. }
  191. values [name] = sa.ToArray ();
  192. break;
  193. }
  194. } catch {
  195. // We ignore individual errors in the file.
  196. }
  197. }
  198. public RegistryKey Ensure (RegistryKey rkey, string extra, bool writable)
  199. {
  200. return Ensure (rkey, extra, writable, false);
  201. }
  202. // 'is_volatile' is used only if the key hasn't been created already.
  203. public RegistryKey Ensure (RegistryKey rkey, string extra, bool writable, bool is_volatile)
  204. {
  205. lock (typeof (KeyHandler)){
  206. string f = Path.Combine (Dir, extra);
  207. KeyHandler kh = (KeyHandler) dir_to_handler [f];
  208. if (kh == null)
  209. kh = new KeyHandler (rkey, f, is_volatile);
  210. RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra), writable);
  211. key_to_handler [rk] = kh;
  212. dir_to_handler [f] = kh;
  213. return rk;
  214. }
  215. }
  216. public RegistryKey Probe (RegistryKey rkey, string extra, bool writable)
  217. {
  218. RegistryKey rk = null;
  219. lock (typeof (KeyHandler)){
  220. string f = Path.Combine (Dir, extra);
  221. KeyHandler kh = (KeyHandler) dir_to_handler [f];
  222. if (kh != null) {
  223. rk = new RegistryKey (kh, CombineName (rkey,
  224. extra), writable);
  225. key_to_handler [rk] = kh;
  226. } else if (Directory.Exists (f) || VolatileKeyExists (f)) {
  227. kh = new KeyHandler (rkey, f);
  228. rk = new RegistryKey (kh, CombineName (rkey, extra),
  229. writable);
  230. dir_to_handler [f] = kh;
  231. key_to_handler [rk] = kh;
  232. }
  233. return rk;
  234. }
  235. }
  236. static string CombineName (RegistryKey rkey, string extra)
  237. {
  238. if (extra.IndexOf ('/') != -1)
  239. extra = extra.Replace ('/', '\\');
  240. return String.Concat (rkey.Name, "\\", extra);
  241. }
  242. static long GetSystemBootTime ()
  243. {
  244. if (!File.Exists ("/proc/stat"))
  245. return -1;
  246. string btime = null;
  247. string line;
  248. try {
  249. using (StreamReader stat_file = new StreamReader ("/proc/stat", Encoding.ASCII)) {
  250. while ((line = stat_file.ReadLine ()) != null)
  251. if (line.StartsWith ("btime")) {
  252. btime = line;
  253. break;
  254. }
  255. }
  256. } catch (Exception e) {
  257. Console.Error.WriteLine ("While reading system info {0}", e);
  258. }
  259. if (btime == null)
  260. return -1;
  261. int space = btime.IndexOf (' ');
  262. long res;
  263. if (!Int64.TryParse (btime.Substring (space, btime.Length - space), out res))
  264. return -1;
  265. return res;
  266. }
  267. // The registered boot time it's a simple line containing the last system btime.
  268. static long GetRegisteredBootTime (string path)
  269. {
  270. if (!File.Exists (path))
  271. return -1;
  272. string line = null;
  273. try {
  274. using (StreamReader reader = new StreamReader (path, Encoding.ASCII))
  275. line = reader.ReadLine ();
  276. } catch (Exception e) {
  277. Console.Error.WriteLine ("While reading registry data at {0}: {1}", path, e);
  278. }
  279. if (line == null)
  280. return -1;
  281. long res;
  282. if (!Int64.TryParse (line, out res))
  283. return -1;
  284. return res;
  285. }
  286. static void SaveRegisteredBootTime (string path, long btime)
  287. {
  288. try {
  289. using (StreamWriter writer = new StreamWriter (path, false, Encoding.ASCII))
  290. writer.WriteLine (btime.ToString ());
  291. } catch (Exception e) {
  292. Console.Error.WriteLine ("While saving registry data at {0}: {1}", path, e);
  293. }
  294. }
  295. // We save the last boot time in a last-btime file in every root, and we use it
  296. // to clean the volatile keys directory in case the system btime changed.
  297. static void CleanVolatileKeys ()
  298. {
  299. long system_btime = GetSystemBootTime ();
  300. string [] roots = new string [] {
  301. UserStore,
  302. MachineStore
  303. };
  304. foreach (string root in roots) {
  305. if (!Directory.Exists (root))
  306. continue;
  307. string btime_file = Path.Combine (root, "last-btime");
  308. string volatile_dir = Path.Combine (root, VolatileDirectoryName);
  309. if (Directory.Exists (volatile_dir)) {
  310. long registered_btime = GetRegisteredBootTime (btime_file);
  311. if (system_btime < 0 || registered_btime < 0 || registered_btime != system_btime)
  312. Directory.Delete (volatile_dir, true);
  313. }
  314. SaveRegisteredBootTime (btime_file, system_btime);
  315. }
  316. }
  317. public static bool VolatileKeyExists (string dir)
  318. {
  319. lock (typeof (KeyHandler)) {
  320. KeyHandler kh = (KeyHandler) dir_to_handler [dir];
  321. if (kh != null)
  322. return kh.IsVolatile;
  323. }
  324. if (Directory.Exists (dir)) // Non-volatile key exists.
  325. return false;
  326. return Directory.Exists (GetVolatileDir (dir));
  327. }
  328. public static string GetVolatileDir (string dir)
  329. {
  330. string root = GetRootFromDir (dir);
  331. string volatile_dir = dir.Replace (root, Path.Combine (root, VolatileDirectoryName));
  332. return volatile_dir;
  333. }
  334. public static KeyHandler Lookup (RegistryKey rkey, bool createNonExisting)
  335. {
  336. lock (typeof (KeyHandler)){
  337. KeyHandler k = (KeyHandler) key_to_handler [rkey];
  338. if (k != null)
  339. return k;
  340. // when a non-root key is requested for no keyhandler exist
  341. // then that key must have been marked for deletion
  342. if (!rkey.IsRoot || !createNonExisting)
  343. return null;
  344. RegistryHive x = (RegistryHive) rkey.Hive;
  345. switch (x){
  346. case RegistryHive.CurrentUser:
  347. string userDir = Path.Combine (UserStore, x.ToString ());
  348. k = new KeyHandler (rkey, userDir);
  349. dir_to_handler [userDir] = k;
  350. break;
  351. case RegistryHive.CurrentConfig:
  352. case RegistryHive.ClassesRoot:
  353. case RegistryHive.DynData:
  354. case RegistryHive.LocalMachine:
  355. case RegistryHive.PerformanceData:
  356. case RegistryHive.Users:
  357. string machine_dir = Path.Combine (MachineStore, x.ToString ());
  358. k = new KeyHandler (rkey, machine_dir);
  359. dir_to_handler [machine_dir] = k;
  360. break;
  361. default:
  362. throw new Exception ("Unknown RegistryHive");
  363. }
  364. key_to_handler [rkey] = k;
  365. return k;
  366. }
  367. }
  368. static string GetRootFromDir (string dir)
  369. {
  370. if (dir.IndexOf (UserStore) > -1)
  371. return UserStore;
  372. else if (dir.IndexOf (MachineStore) > -1)
  373. return MachineStore;
  374. throw new Exception ("Could not get root for dir " + dir);
  375. }
  376. public static void Drop (RegistryKey rkey)
  377. {
  378. lock (typeof (KeyHandler)) {
  379. KeyHandler k = (KeyHandler) key_to_handler [rkey];
  380. if (k == null)
  381. return;
  382. key_to_handler.Remove (rkey);
  383. // remove cached KeyHandler if no other keys reference it
  384. int refCount = 0;
  385. foreach (DictionaryEntry de in key_to_handler)
  386. if (de.Value == k)
  387. refCount++;
  388. if (refCount == 0)
  389. dir_to_handler.Remove (k.Dir);
  390. }
  391. }
  392. public static void Drop (string dir)
  393. {
  394. lock (typeof (KeyHandler)) {
  395. KeyHandler kh = (KeyHandler) dir_to_handler [dir];
  396. if (kh == null)
  397. return;
  398. dir_to_handler.Remove (dir);
  399. // remove (other) references to keyhandler
  400. ArrayList keys = new ArrayList ();
  401. foreach (DictionaryEntry de in key_to_handler)
  402. if (de.Value == kh)
  403. keys.Add (de.Key);
  404. foreach (object key in keys)
  405. key_to_handler.Remove (key);
  406. }
  407. }
  408. public static bool Delete (string dir)
  409. {
  410. if (!Directory.Exists (dir)) {
  411. string volatile_dir = GetVolatileDir (dir);
  412. if (!Directory.Exists (volatile_dir))
  413. return false;
  414. dir = volatile_dir;
  415. }
  416. Directory.Delete (dir, true);
  417. Drop (dir);
  418. return true;
  419. }
  420. public RegistryValueKind GetValueKind (string name)
  421. {
  422. if (name == null)
  423. return RegistryValueKind.Unknown;
  424. object value = values [name];
  425. if (value == null)
  426. return RegistryValueKind.Unknown;
  427. if (value is int)
  428. return RegistryValueKind.DWord;
  429. if (value is string [])
  430. return RegistryValueKind.MultiString;
  431. if (value is long)
  432. return RegistryValueKind.QWord;
  433. if (value is byte [])
  434. return RegistryValueKind.Binary;
  435. if (value is string)
  436. return RegistryValueKind.String;
  437. if (value is ExpandString)
  438. return RegistryValueKind.ExpandString;
  439. return RegistryValueKind.Unknown;
  440. }
  441. public object GetValue (string name, RegistryValueOptions options)
  442. {
  443. if (IsMarkedForDeletion)
  444. return null;
  445. if (name == null)
  446. name = string.Empty;
  447. object value = values [name];
  448. ExpandString exp = value as ExpandString;
  449. if (exp == null)
  450. return value;
  451. if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
  452. return exp.Expand ();
  453. return exp.ToString ();
  454. }
  455. public void SetValue (string name, object value)
  456. {
  457. AssertNotMarkedForDeletion ();
  458. if (name == null)
  459. name = string.Empty;
  460. // immediately convert non-native registry values to string to avoid
  461. // returning it unmodified in calls to UnixRegistryApi.GetValue
  462. if (value is int || value is string || value is byte[] || value is string[])
  463. values[name] = value;
  464. else
  465. values[name] = value.ToString ();
  466. SetDirty ();
  467. }
  468. public string [] GetValueNames ()
  469. {
  470. AssertNotMarkedForDeletion ();
  471. ICollection keys = values.Keys;
  472. string [] vals = new string [keys.Count];
  473. keys.CopyTo (vals, 0);
  474. return vals;
  475. }
  476. public int GetSubKeyCount ()
  477. {
  478. return GetSubKeyNames ().Length;
  479. }
  480. public string [] GetSubKeyNames ()
  481. {
  482. DirectoryInfo selfDir = new DirectoryInfo (ActualDir);
  483. DirectoryInfo[] subDirs = selfDir.GetDirectories ();
  484. string[] subKeyNames;
  485. // for volatile keys (cannot contain non-volatile subkeys) or keys
  486. // without *any* presence in the volatile key section, we can do it simple.
  487. if (IsVolatile || !Directory.Exists (GetVolatileDir (Dir))) {
  488. subKeyNames = new string[subDirs.Length];
  489. for (int i = 0; i < subDirs.Length; i++) {
  490. DirectoryInfo subDir = subDirs[i];
  491. subKeyNames[i] = subDir.Name;
  492. }
  493. return subKeyNames;
  494. }
  495. // We may have the entries repeated, so keep just one of each one.
  496. DirectoryInfo volatileDir = new DirectoryInfo (GetVolatileDir (Dir));
  497. DirectoryInfo [] volatileSubDirs = volatileDir.GetDirectories ();
  498. Dictionary<string,string> dirs = new Dictionary<string,string> ();
  499. foreach (DirectoryInfo dir in subDirs)
  500. dirs [dir.Name] = dir.Name;
  501. foreach (DirectoryInfo volDir in volatileSubDirs)
  502. dirs [volDir.Name] = volDir.Name;
  503. subKeyNames = new string [dirs.Count];
  504. int j = 0;
  505. foreach (KeyValuePair<string,string> entry in dirs)
  506. subKeyNames[j++] = entry.Value;
  507. return subKeyNames;
  508. }
  509. //
  510. // This version has to do argument validation based on the valueKind
  511. //
  512. public void SetValue (string name, object value, RegistryValueKind valueKind)
  513. {
  514. SetDirty ();
  515. if (name == null)
  516. name = string.Empty;
  517. switch (valueKind){
  518. case RegistryValueKind.String:
  519. if (value is string){
  520. values [name] = value;
  521. return;
  522. }
  523. break;
  524. case RegistryValueKind.ExpandString:
  525. if (value is string){
  526. values [name] = new ExpandString ((string)value);
  527. return;
  528. }
  529. break;
  530. case RegistryValueKind.Binary:
  531. if (value is byte []){
  532. values [name] = value;
  533. return;
  534. }
  535. break;
  536. case RegistryValueKind.DWord:
  537. try {
  538. values [name] = Convert.ToInt32 (value);
  539. return;
  540. } catch (OverflowException) {
  541. break;
  542. }
  543. case RegistryValueKind.MultiString:
  544. if (value is string []){
  545. values [name] = value;
  546. return;
  547. }
  548. break;
  549. case RegistryValueKind.QWord:
  550. try {
  551. values [name] = Convert.ToInt64 (value);
  552. return;
  553. } catch (OverflowException) {
  554. break;
  555. }
  556. default:
  557. throw new ArgumentException ("unknown value", "valueKind");
  558. }
  559. throw new ArgumentException ("Value could not be converted to specified type", "valueKind");
  560. }
  561. void SetDirty ()
  562. {
  563. lock (typeof (KeyHandler)){
  564. if (dirty)
  565. return;
  566. dirty = true;
  567. new Timer (DirtyTimeout, null, 3000, Timeout.Infinite);
  568. }
  569. }
  570. public void DirtyTimeout (object state)
  571. {
  572. Flush ();
  573. }
  574. public void Flush ()
  575. {
  576. lock (typeof (KeyHandler)) {
  577. if (dirty) {
  578. Save ();
  579. dirty = false;
  580. }
  581. }
  582. }
  583. public bool ValueExists (string name)
  584. {
  585. if (name == null)
  586. name = string.Empty;
  587. return values.Contains (name);
  588. }
  589. public int ValueCount {
  590. get {
  591. return values.Keys.Count;
  592. }
  593. }
  594. public bool IsMarkedForDeletion {
  595. get {
  596. return !dir_to_handler.Contains (Dir);
  597. }
  598. }
  599. public void RemoveValue (string name)
  600. {
  601. AssertNotMarkedForDeletion ();
  602. values.Remove (name);
  603. SetDirty ();
  604. }
  605. ~KeyHandler ()
  606. {
  607. Flush ();
  608. }
  609. void Save ()
  610. {
  611. if (IsMarkedForDeletion)
  612. return;
  613. if (!File.Exists (file) && values.Count == 0)
  614. return;
  615. SecurityElement se = new SecurityElement ("values");
  616. // With SecurityElement.Text = value, and SecurityElement.AddAttribute(key, value)
  617. // the values must be escaped prior to being assigned.
  618. foreach (DictionaryEntry de in values){
  619. object val = de.Value;
  620. SecurityElement value = new SecurityElement ("value");
  621. value.AddAttribute ("name", SecurityElement.Escape ((string) de.Key));
  622. if (val is string){
  623. value.AddAttribute ("type", "string");
  624. value.Text = SecurityElement.Escape ((string) val);
  625. } else if (val is int){
  626. value.AddAttribute ("type", "int");
  627. value.Text = val.ToString ();
  628. } else if (val is long) {
  629. value.AddAttribute ("type", "qword");
  630. value.Text = val.ToString ();
  631. } else if (val is byte []){
  632. value.AddAttribute ("type", "bytearray");
  633. value.Text = Convert.ToBase64String ((byte[]) val);
  634. } else if (val is ExpandString){
  635. value.AddAttribute ("type", "expand");
  636. value.Text = SecurityElement.Escape (val.ToString ());
  637. } else if (val is string []){
  638. value.AddAttribute ("type", "string-array");
  639. foreach (string ss in (string[]) val){
  640. SecurityElement str = new SecurityElement ("string");
  641. str.Text = SecurityElement.Escape (ss);
  642. value.AddChild (str);
  643. }
  644. }
  645. se.AddChild (value);
  646. }
  647. using (FileStream fs = File.Create (file)){
  648. StreamWriter sw = new StreamWriter (fs);
  649. sw.Write (se.ToString ());
  650. sw.Flush ();
  651. }
  652. }
  653. private void AssertNotMarkedForDeletion ()
  654. {
  655. if (IsMarkedForDeletion)
  656. throw RegistryKey.CreateMarkedForDeletionException ();
  657. }
  658. static string user_store;
  659. static string machine_store;
  660. private static string UserStore {
  661. get {
  662. if (user_store == null)
  663. user_store = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal),
  664. ".mono/registry");
  665. return user_store;
  666. }
  667. }
  668. private static string MachineStore {
  669. get {
  670. if (machine_store == null) {
  671. machine_store = Environment.GetEnvironmentVariable ("MONO_REGISTRY_PATH");
  672. if (machine_store == null) {
  673. string s = Environment.GetMachineConfigPath ();
  674. int p = s.IndexOf ("machine.config");
  675. machine_store = Path.Combine (Path.Combine (s.Substring (0, p-1), ".."), "registry");
  676. }
  677. }
  678. return machine_store;
  679. }
  680. }
  681. }
  682. internal class UnixRegistryApi : IRegistryApi {
  683. static string ToUnix (string keyname)
  684. {
  685. if (keyname.IndexOf ('\\') != -1)
  686. keyname = keyname.Replace ('\\', '/');
  687. return keyname.ToLower ();
  688. }
  689. static bool IsWellKnownKey (string parentKeyName, string keyname)
  690. {
  691. // FIXME: Add more keys if needed
  692. if (parentKeyName == Registry.CurrentUser.Name ||
  693. parentKeyName == Registry.LocalMachine.Name)
  694. return (0 == String.Compare ("software", keyname, true, CultureInfo.InvariantCulture));
  695. return false;
  696. }
  697. public RegistryKey CreateSubKey (RegistryKey rkey, string keyname)
  698. {
  699. return CreateSubKey (rkey, keyname, true);
  700. }
  701. #if NET_4_0
  702. public RegistryKey CreateSubKey (RegistryKey rkey, string keyname, RegistryOptions options)
  703. {
  704. return CreateSubKey (rkey, keyname, true, options == RegistryOptions.Volatile);
  705. }
  706. #endif
  707. public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
  708. {
  709. throw new NotImplementedException ();
  710. }
  711. public RegistryKey OpenSubKey (RegistryKey rkey, string keyname, bool writable)
  712. {
  713. KeyHandler self = KeyHandler.Lookup (rkey, true);
  714. if (self == null) {
  715. // return null if parent is marked for deletion
  716. return null;
  717. }
  718. RegistryKey result = self.Probe (rkey, ToUnix (keyname), writable);
  719. if (result == null && IsWellKnownKey (rkey.Name, keyname)) {
  720. // create the subkey even if its parent was opened read-only
  721. result = CreateSubKey (rkey, keyname, writable);
  722. }
  723. return result;
  724. }
  725. #if NET_4_0
  726. public RegistryKey FromHandle (SafeRegistryHandle handle)
  727. {
  728. throw new NotImplementedException ();
  729. }
  730. #endif
  731. public void Flush (RegistryKey rkey)
  732. {
  733. KeyHandler self = KeyHandler.Lookup (rkey, false);
  734. if (self == null) {
  735. // we do not need to flush changes as key is marked for deletion
  736. return;
  737. }
  738. self.Flush ();
  739. }
  740. public void Close (RegistryKey rkey)
  741. {
  742. KeyHandler.Drop (rkey);
  743. }
  744. public object GetValue (RegistryKey rkey, string name, object default_value, RegistryValueOptions options)
  745. {
  746. KeyHandler self = KeyHandler.Lookup (rkey, true);
  747. if (self == null) {
  748. // key was removed since it was opened
  749. return default_value;
  750. }
  751. if (self.ValueExists (name))
  752. return self.GetValue (name, options);
  753. return default_value;
  754. }
  755. public void SetValue (RegistryKey rkey, string name, object value)
  756. {
  757. KeyHandler self = KeyHandler.Lookup (rkey, true);
  758. if (self == null)
  759. throw RegistryKey.CreateMarkedForDeletionException ();
  760. self.SetValue (name, value);
  761. }
  762. public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
  763. {
  764. KeyHandler self = KeyHandler.Lookup (rkey, true);
  765. if (self == null)
  766. throw RegistryKey.CreateMarkedForDeletionException ();
  767. self.SetValue (name, value, valueKind);
  768. }
  769. public int SubKeyCount (RegistryKey rkey)
  770. {
  771. KeyHandler self = KeyHandler.Lookup (rkey, true);
  772. if (self == null)
  773. throw RegistryKey.CreateMarkedForDeletionException ();
  774. return self.GetSubKeyCount ();
  775. }
  776. public int ValueCount (RegistryKey rkey)
  777. {
  778. KeyHandler self = KeyHandler.Lookup (rkey, true);
  779. if (self == null)
  780. throw RegistryKey.CreateMarkedForDeletionException ();
  781. return self.ValueCount;
  782. }
  783. public void DeleteValue (RegistryKey rkey, string name, bool throw_if_missing)
  784. {
  785. KeyHandler self = KeyHandler.Lookup (rkey, true);
  786. if (self == null) {
  787. // if key is marked for deletion, report success regardless of
  788. // throw_if_missing
  789. return;
  790. }
  791. if (throw_if_missing && !self.ValueExists (name))
  792. throw new ArgumentException ("the given value does not exist");
  793. self.RemoveValue (name);
  794. }
  795. public void DeleteKey (RegistryKey rkey, string keyname, bool throw_if_missing)
  796. {
  797. KeyHandler self = KeyHandler.Lookup (rkey, true);
  798. if (self == null) {
  799. // key is marked for deletion
  800. if (!throw_if_missing)
  801. return;
  802. throw new ArgumentException ("the given value does not exist");
  803. }
  804. string dir = Path.Combine (self.Dir, ToUnix (keyname));
  805. if (!KeyHandler.Delete (dir) && throw_if_missing)
  806. throw new ArgumentException ("the given value does not exist");
  807. }
  808. public string [] GetSubKeyNames (RegistryKey rkey)
  809. {
  810. KeyHandler self = KeyHandler.Lookup (rkey, true);
  811. return self.GetSubKeyNames ();
  812. }
  813. public string [] GetValueNames (RegistryKey rkey)
  814. {
  815. KeyHandler self = KeyHandler.Lookup (rkey, true);
  816. if (self == null)
  817. throw RegistryKey.CreateMarkedForDeletionException ();
  818. return self.GetValueNames ();
  819. }
  820. public string ToString (RegistryKey rkey)
  821. {
  822. return rkey.Name;
  823. }
  824. private RegistryKey CreateSubKey (RegistryKey rkey, string keyname, bool writable)
  825. {
  826. return CreateSubKey (rkey, keyname, writable, false);
  827. }
  828. private RegistryKey CreateSubKey (RegistryKey rkey, string keyname, bool writable, bool is_volatile)
  829. {
  830. KeyHandler self = KeyHandler.Lookup (rkey, true);
  831. if (self == null)
  832. throw RegistryKey.CreateMarkedForDeletionException ();
  833. if (KeyHandler.VolatileKeyExists (self.Dir) && !is_volatile)
  834. throw new IOException ("Cannot create a non volatile subkey under a volatile key.");
  835. return self.Ensure (rkey, ToUnix (keyname), writable, is_volatile);
  836. }
  837. public RegistryValueKind GetValueKind (RegistryKey rkey, string name)
  838. {
  839. KeyHandler self = KeyHandler.Lookup (rkey, true);
  840. if (self != null)
  841. return self.GetValueKind (name);
  842. // key was removed since it was opened or it does not exist.
  843. return RegistryValueKind.Unknown;
  844. }
  845. #if NET_4_0
  846. public IntPtr GetHandle (RegistryKey key)
  847. {
  848. throw new NotImplementedException ();
  849. }
  850. #endif
  851. }
  852. }
  853. #endif // NET_2_1