Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastien Pouliot <sebastien@ximian.com>2009-04-16 19:25:45 +0400
committerSebastien Pouliot <sebastien@ximian.com>2009-04-16 19:25:45 +0400
commit52531b6daab1f92ac7144196f120c74ff42614c6 (patch)
tree73f80898e24852d8e2ba47cee57978bb8d8c8651 /mcs/class/corlib/System.IO.IsolatedStorage
parentbc211b721b6a2181ad692e7b9fcb1e465593f457 (diff)
In .:
2009-04-16 Sebastien Pouliot <sebastien@ximian.com> * corlib.dll.sources: Add System.IO.IsolatedStorage/ MoonIsolatedStorage.cs to the build In System.IO.IsolatedStorage: 2009-04-16 Sebastien Pouliot <sebastien@ximian.com> * MoonIsolatedStorage.cs: New. Manage the shared (location, quota) information about Moonlight isolated storage. * MoonIsolatedStorageFile.cs: Adjust with new MoonIsolatedStorage. Implement IncreaseQuotaTo UI. * MoonIsolatedStorageFileStream.cs: Adjust with MoonIsolatedStorage. svn path=/trunk/mcs/; revision=131903
Diffstat (limited to 'mcs/class/corlib/System.IO.IsolatedStorage')
-rw-r--r--mcs/class/corlib/System.IO.IsolatedStorage/ChangeLog8
-rw-r--r--mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorage.cs232
-rw-r--r--mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorageFile.cs182
-rw-r--r--mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorageFileStream.cs4
4 files changed, 286 insertions, 140 deletions
diff --git a/mcs/class/corlib/System.IO.IsolatedStorage/ChangeLog b/mcs/class/corlib/System.IO.IsolatedStorage/ChangeLog
index e7b4e66d54f..d69ec409887 100644
--- a/mcs/class/corlib/System.IO.IsolatedStorage/ChangeLog
+++ b/mcs/class/corlib/System.IO.IsolatedStorage/ChangeLog
@@ -1,3 +1,11 @@
+2009-04-16 Sebastien Pouliot <sebastien@ximian.com>
+
+ * MoonIsolatedStorage.cs: New. Manage the shared (location, quota)
+ information about Moonlight isolated storage.
+ * MoonIsolatedStorageFile.cs: Adjust with new MoonIsolatedStorage.
+ Implement IncreaseQuotaTo UI.
+ * MoonIsolatedStorageFileStream.cs: Adjust with MoonIsolatedStorage.
+
2009-04-16 Sebastien Pouliot <sebastien@ximian.com>
* IsolatedStorageFile.cs: Fix thread-safety issue at creation time.
diff --git a/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorage.cs b/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorage.cs
new file mode 100644
index 00000000000..184137eafcb
--- /dev/null
+++ b/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorage.cs
@@ -0,0 +1,232 @@
+//
+// System.IO.IsolatedStorage.IsolatedQuotaGroup
+//
+// Authors
+// Sebastien Pouliot <sebastien@ximian.com>
+//
+// Copyright (C) 2009 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if NET_2_1
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace System.IO.IsolatedStorage {
+
+ static class IsolatedStorage {
+
+ // NOTE: both the 'site' and 'application' share the same quota
+ internal const long DefaultQuota = 1024 * 1024;
+ // Since we can extend more than AvailableFreeSize we need to substract the "safety" value out of it
+ private const int SafetyZone = 1024;
+
+
+ static string site_root;
+ static string site_config;
+ static long site_quota;
+
+ // this is similar to Silverlight hierarchy because differing too much would cause
+ // problems with the 260 character maximum allowed for paths
+
+ // Considering a 10 characters user name the following platform will allow:
+ // 109 characters path, under Windows Vista (Silverlight 2)
+ // 77 characters path, under Windows XP (Silverlight 2)
+ // 100 characters path, under Mac OSX (Silverlight 2)
+ // 159 characters path, under Linux (Moonlight 2)
+
+ // 1234567890123456789012345678901234567890123 = 43 + 28 + 1 + 28 +1 = 101
+ // 1 2 3 4
+ // /home/1234567890/.local/share/moonlight/is/{site-hash:28}/{app-hash:28}/
+
+ static IsolatedStorage ()
+ {
+ string isolated_root = GetIsolatedStorageRoot ();
+ // enable/disable osilated storage - requires restart
+ Enabled = !File.Exists (Path.Combine (isolated_root, "disabled"));
+
+ // from System.Windows.Application we made "xap_uri" correspond to
+ // Application.Current.Host.Source.AbsoluteUri
+ string app = (AppDomain.CurrentDomain.GetData ("xap_uri") as string);
+ if (app.StartsWith ("file://")) {
+ // every path is a different site, the XAP is the application
+ Site = Path.GetDirectoryName (app.Substring (7));
+ } else {
+ // for http[s] the "Site Identity" is built using:
+ // * the protocol (so http and https are different);
+ // * the host (so beta.moonlight.com is different from www.moonlight.com); and
+ // * the port (so 8080 is different from 80) but
+ // ** 80 and none are identical for HTTP
+ // ** 443 and none are identical for HTTPS
+ Site = app.Substring (0, app.IndexOf ('/', 8));
+ }
+
+ // the "Site Identity"
+ string site_hash = Hash (Site);
+ site_root = TryDirectory (Path.Combine (isolated_root, site_hash));
+ SetupSite (site_root);
+ SitePath = TryDirectory (Path.Combine (site_root, site_hash));
+
+ // the "Application Identity"
+ string app_hash = Hash (app);
+ SetupApplication (app, app_hash, site_root);
+ ApplicationPath = TryDirectory (Path.Combine (site_root, app_hash));
+ }
+
+ static string GetIsolatedStorageRoot ()
+ {
+ // http://freedesktop.org/Standards/basedir-spec/basedir-spec-0.6.html
+ string xdg_data_home = Environment.GetEnvironmentVariable ("XDG_DATA_HOME");
+ if (String.IsNullOrEmpty (xdg_data_home)) {
+ xdg_data_home = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData);
+ }
+
+ string moonlight = TryDirectory (Path.Combine (xdg_data_home, "moonlight"));
+ return TryDirectory (Path.Combine (moonlight, "is"));
+ }
+
+ static void SetupSite (string dir)
+ {
+ site_quota = DefaultQuota;
+ // read configuration file (e.g. quota) if it exists, otherwise write it
+ site_config = Path.Combine (dir, "config");
+ if (File.Exists (site_config)) {
+ LoadConfiguration ();
+ } else {
+ SaveConfiguration ();
+ }
+ }
+
+ static void LoadConfiguration ()
+ {
+ // read quota, the rest is not useful to us
+ using (StreamReader sr = new StreamReader (site_config)) {
+ string line = sr.ReadLine ();
+ while (line != null) {
+ if (line.StartsWith ("QUOTA = ")) {
+ if (!Int64.TryParse (line.Substring (8, line.Length - 8), out site_quota))
+ Quota = DefaultQuota;
+ }
+ line = sr.ReadLine ();
+ }
+ }
+ }
+
+ static void SaveConfiguration ()
+ {
+ using (StreamWriter sw = new StreamWriter (site_config)) {
+ sw.WriteLine ("URI = {0}", Site);
+ sw.WriteLine ("QUOTA = {0}", Quota);
+ }
+ }
+
+ static void SetupApplication (string app, string app_hash, string dir)
+ {
+ // save the application information (for the management UI)
+ string config = Path.Combine (dir, app_hash + ".info");
+ if (File.Exists (config))
+ return;
+
+ using (StreamWriter sw = new StreamWriter (config)) {
+ sw.WriteLine ("URI = {0}", app);
+ }
+ }
+
+ // goal: uniform length directory name
+ // non-goal: security by obsfucation
+ static string Hash (string name)
+ {
+ string id;
+ using (SHA1Managed hash = new SHA1Managed ()) {
+ byte[] digest = hash.ComputeHash (Encoding.UTF8.GetBytes (name));
+ id = Convert.ToBase64String (digest);
+ }
+ return (id.IndexOf ('/') == -1) ? id : id.Replace ('/', '-');
+ }
+
+ static string TryDirectory (string path)
+ {
+ try {
+ Directory.CreateDirectory (path);
+ return path;
+ } catch {
+ return null;
+ }
+ }
+
+ static internal void Remove (string dir)
+ {
+ try {
+ Directory.Delete (dir, true);
+ }
+ finally {
+ TryDirectory (dir);
+ }
+ }
+
+ static internal bool CanExtend (long request)
+ {
+ return (request <= AvailableFreeSpace + SafetyZone);
+ }
+
+ static public string ApplicationPath {
+ get; private set;
+ }
+
+ static public string SitePath {
+ get; private set;
+ }
+
+ static public long AvailableFreeSpace {
+ get { return Quota - Current - SafetyZone; }
+ }
+
+ [DllImport ("moon")]
+ extern static long isolated_storage_get_current_usage (string root);
+
+ static public long Current {
+ get { return isolated_storage_get_current_usage (site_root); }
+ }
+
+ static public long Quota {
+ get { return site_quota; }
+ set {
+ site_quota = value;
+ SaveConfiguration ();
+ }
+ }
+
+ static public string Site {
+ get; private set;
+ }
+
+ // it is possible, from the UI, to completely disable IsolatedStorage
+ static public bool Enabled { get; private set; }
+ }
+}
+
+#endif
+
diff --git a/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorageFile.cs b/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorageFile.cs
index 1fb01ea9e4a..a89798417ad 100644
--- a/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorageFile.cs
+++ b/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorageFile.cs
@@ -7,7 +7,7 @@
// Miguel de Icaza (miguel@novell.com)
// Sebastien Pouliot <sebastien@ximian.com>
//
-// Copyright (C) 2007, 2008 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2007, 2008, 2009 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -31,6 +31,7 @@
#if NET_2_1
using System;
using System.IO;
+using System.Runtime.InteropServices;
using System.Security;
namespace System.IO.IsolatedStorage {
@@ -38,78 +39,27 @@ namespace System.IO.IsolatedStorage {
// Most of the time there will only be a single instance of both
// * Application Store (GetUserStoreForApplication)
// * Site Store (GetUserStoreForSite)
- // However both can have multiple concurrent uses
- // E.g. another instance of the same application (same URL) running in another Moonlight instance
- // E.g. another application on the same site (i.e. host) for a site store
-
- // TODO: use shared memory (and locks) to keep the quota and used values synchronized
-
- // TODO: we need to be initialized by Application (System.Windows.dll) to know the correct root directories
-
- // FIXME: we need to tool to set quota (SL does this on a property page of the "Silverlight Configuration" menu)
- // the beta2 user interface (that I only see in IE) does not seems to differentiate application and site storage
+ // However both can have multiple concurrent uses, e.g.
+ // * another instance of the same application (same URL) running in another Moonlight instance
+ // * another application on the same site (i.e. host) for a site store
+ // and share the some quota, i.e. a site and all applications on the sites share the same space
// notes:
// * quota seems computed in (disk) blocks, i.e. a small file will have a (non-small) size
+ // e.g. every files and directories entries takes 1KB
public sealed class IsolatedStorageFile : IDisposable {
- private const long DefaultQuota = 1024 * 1024;
- // Since we can extend more than AvailableFreeSize we need to substract the "safety" value out of it
- private const int SafetyZone = 1024;
-
- static string isolated_root;
- static string isolated_appdir;
- static string isolated_sitedir;
-
static object locker = new object ();
-
- static string TryDirectory (string path)
- {
- try {
- Directory.CreateDirectory (path);
- return path;
- } catch {
- return null;
- }
- }
-
- static IsolatedStorageFile ()
- {
- string xdg_data_home = Environment.GetEnvironmentVariable ("XDG_DATA_HOME");
- if (String.IsNullOrEmpty (xdg_data_home)) {
- xdg_data_home = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData);
- }
-
- string isolated_root;
- isolated_root = TryDirectory (Path.Combine (xdg_data_home, "moonlight"));
- if (isolated_root == null)
- throw new IsolatedStorageException ("No root");
-
- isolated_appdir = TryDirectory (Path.Combine (isolated_root, "application"));
- isolated_sitedir = TryDirectory (Path.Combine (isolated_root, "site"));
- }
-
+
private string basedir;
- private string datafile;
- private long quota;
private long used;
private bool removed = false;
private bool disposed = false;
- internal IsolatedStorageFile (string root, string dirname)
+ internal IsolatedStorageFile (string root)
{
- string dir = Path.Combine (root, dirname);
- basedir = TryDirectory (dir);
-
- datafile = Path.Combine (root, dirname + ".data");
- if (File.Exists (datafile)) {
- ReadStoreData ();
- } else {
- used = 0;
- quota = DefaultQuota;
- UpdateStoreData ();
- }
+ basedir = root;
}
internal void PreCheck ()
@@ -122,47 +72,29 @@ namespace System.IO.IsolatedStorage {
public static IsolatedStorageFile GetUserStoreForApplication ()
{
- if (isolated_appdir == null)
- throw new SecurityException ();
-
- // from System.Windows.Application we made "xap_uri" correspond to
- // Application.Current.Host.Source.AbsoluteUri
- string app = (AppDomain.CurrentDomain.GetData ("xap_uri") as string);
- if (app == null)
- throw new SecurityException ();
-
- return new IsolatedStorageFile (isolated_appdir, app.Replace ("/", "%27"));
+ return new IsolatedStorageFile (IsolatedStorage.ApplicationPath);
}
public static IsolatedStorageFile GetUserStoreForSite ()
{
- if (isolated_sitedir == null)
- throw new SecurityException ();
-
- // from System.Windows.Application we made "xap_host" correspond to
- // Application.Current.Host.Source.Host
- string site = (AppDomain.CurrentDomain.GetData ("xap_host") as string);
- if (site == null)
- throw new SecurityException ();
- // no host is defined for things like: file://home/...
- if (site.Length == 0)
- site = "localhost:file";
-
- return new IsolatedStorageFile (isolated_sitedir, site);
+ return new IsolatedStorageFile (IsolatedStorage.SitePath);
}
internal string Verify (string path)
{
+ // special case: 'path' would be returned (instead of combined)
+ if ((path.Length > 0) && (path [0] == '/'))
+ path = path.Substring (1, path.Length - 1);
+
// outside of try/catch since we want to get things like
- // ArgumentNullException for null paths
// ArgumentException for invalid characters
string combined = Path.Combine (basedir, path);
try {
string full = Path.GetFullPath (combined);
- full = Path.GetFullPath (combined);
if (full.StartsWith (basedir))
return full;
} catch {
+ // we do not supply an inner exception since it could contains details about the path
throw new IsolatedStorageException ();
}
throw new IsolatedStorageException ();
@@ -171,7 +103,11 @@ namespace System.IO.IsolatedStorage {
public void CreateDirectory (string dir)
{
PreCheck ();
- Directory.CreateDirectory (Verify (dir));
+ if (dir == null)
+ throw new ArgumentNullException ("dir");
+ // empty dir is ignored
+ if (dir.Length > 0)
+ Directory.CreateDirectory (Verify (dir));
}
public IsolatedStorageFileStream CreateFile (string path)
@@ -189,12 +125,16 @@ namespace System.IO.IsolatedStorage {
public void DeleteDirectory (string dir)
{
PreCheck ();
+ if (dir == null)
+ throw new ArgumentNullException ("dir");
Directory.Delete (Verify (dir));
}
public void DeleteFile (string file)
{
PreCheck ();
+ if (file == null)
+ throw new ArgumentNullException ("file");
string checked_filename = Verify (file);
if (!File.Exists (checked_filename))
throw new IsolatedStorageException ("File does not exists");
@@ -276,16 +216,8 @@ namespace System.IO.IsolatedStorage {
public void Remove ()
{
PreCheck ();
- try {
- Directory.Delete (basedir, true);
- }
- finally {
- used = 0;
- quota = DefaultQuota;
- UpdateStoreData ();
- TryDirectory (basedir);
- removed = true;
- }
+ IsolatedStorage.Remove (basedir);
+ removed = true;
}
// note: available free space could be changed from another application (same URL, another ML instance) or
@@ -293,8 +225,7 @@ namespace System.IO.IsolatedStorage {
public long AvailableFreeSpace {
get {
PreCheck ();
- ReadStoreData ();
- return quota - used - SafetyZone;
+ return IsolatedStorage.AvailableFreeSpace;
}
}
@@ -303,57 +234,32 @@ namespace System.IO.IsolatedStorage {
public long Quota {
get {
PreCheck ();
- ReadStoreData ();
- return quota;
+ return IsolatedStorage.Quota;
}
}
+ [DllImport ("moon")]
+ [return: MarshalAs (UnmanagedType.Bool)]
+ extern static bool isolated_storage_increase_quota_to (string primary_text, string secondary_text);
+
+ const long mb = 1024 * 1024;
+
public bool IncreaseQuotaTo (long newQuotaSize)
{
PreCheck ();
- if (newQuotaSize <= quota)
+ if (newQuotaSize <= Quota)
throw new ArgumentException ("newQuotaSize", "Only increases are possible");
- // FIXME: we must ensure this is called from an event handler (or return false)
- // we need to find out where it's valid (e.g. Page.Loaded and Page.MouseLeftButtonUp are not)
-
- // FIXME: need plugin UI to confirm the change
-
- return false;
- }
-
- internal bool CanExtend (long request)
- {
- bool result = (request <= AvailableFreeSpace + SafetyZone);
- if (result) {
- lock (locker) {
- used += request;
- }
- }
+ string message = String.Format ("This web site, <u>{0}</u>, is requesting an increase of its local storage capacity on your computer. It is currently using <b>{1:F1} MB</b> out of a maximum of <b>{2:F1} MB</b>.",
+ IsolatedStorage.Site, IsolatedStorage.Current / mb, IsolatedStorage.Quota / mb);
+ string question = String.Format ("Do you want to increase the web site quota to a new maximum of <b>{0:F1} MB</b> ?",
+ newQuotaSize / mb);
+ bool result = isolated_storage_increase_quota_to (message, question);
+ if (result)
+ IsolatedStorage.Quota = newQuotaSize;
return result;
}
-
- // TEMPORARY - not thread (or cross process) safe
-
- private byte [] data = new byte [16];
-
- internal void ReadStoreData ()
- {
- using (FileStream fs = new FileStream (datafile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
- fs.Read (data, 0, 16);
- quota = BitConverter.ToInt64 (data, 0);
- used = BitConverter.ToInt64 (data, 8);
- }
- }
-
- internal void UpdateStoreData ()
- {
- using (FileStream fs = new FileStream (datafile, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) {
- fs.Write (BitConverter.GetBytes (quota), 0, 8);
- fs.Write (BitConverter.GetBytes (used), 0, 8);
- }
- }
}
}
#endif
diff --git a/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorageFileStream.cs b/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorageFileStream.cs
index e6bb71a3906..d0f9f11a7f4 100644
--- a/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorageFileStream.cs
+++ b/mcs/class/corlib/System.IO.IsolatedStorage/MoonIsolatedStorageFileStream.cs
@@ -114,7 +114,7 @@ namespace System.IO.IsolatedStorage {
// will that request put us in a position to grow *or shrink* the file ?
// note: this can be negative, e.g. calling SetLength(0), so we can't call EnsureQuotaLimits directly
- if (!container.CanExtend (value - Length))
+ if (!IsolatedStorage.CanExtend (value - Length))
throw new IsolatedStorageException ("Requested size is larger than remaining quota allowance.");
base.SetLength (value);
@@ -212,7 +212,7 @@ namespace System.IO.IsolatedStorage {
if (grow < 0)
return;
- if (!container.CanExtend (grow))
+ if (!IsolatedStorage.CanExtend (grow))
throw new IsolatedStorageException ("Requested size is larger than remaining quota allowance.");
}
}