2 // System.IO.IsolatedStorage.IsolatedStorageFile
5 // Jonathan Pryor (jonpryor@vt.edu)
6 // Sebastien Pouliot <sebastien@ximian.com>
8 // (C) 2003 Jonathan Pryor
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Collections;
32 using System.Reflection;
33 using System.Runtime.InteropServices;
34 using System.Runtime.Serialization.Formatters.Binary;
35 using System.Security;
36 using System.Security.Cryptography;
37 using System.Security.Permissions;
38 using System.Security.Policy;
41 using Mono.Security.Cryptography;
43 namespace System.IO.IsolatedStorage {
45 // This is a terribly named class. It doesn't actually represent a file as
46 // much as a directory
52 // FIXME: Further limit the assertion when imperative Assert is implemented
53 [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
54 public sealed class IsolatedStorageFile : IsolatedStorage, IDisposable {
56 private bool _resolved;
57 private ulong _maxSize;
58 private Evidence _fullEvidences;
60 public static IEnumerator GetEnumerator (IsolatedStorageScope scope)
65 case IsolatedStorageScope.User:
66 case IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
68 case IsolatedStorageScope.Machine:
72 string msg = Locale.GetText ("Invalid scope, only User, User|Roaming and Machine are valid");
73 throw new ArgumentException (msg);
76 return new IsolatedStorageFileEnumerator (scope, GetIsolatedStorageRoot (scope));
79 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope,
80 Evidence domainEvidence, Type domainEvidenceType,
81 Evidence assemblyEvidence, Type assemblyEvidenceType)
85 bool domain = ((scope & IsolatedStorageScope.Domain) != 0);
86 if (domain && (domainEvidence == null))
87 throw new ArgumentNullException ("domainEvidence");
89 bool assembly = ((scope & IsolatedStorageScope.Assembly) != 0);
90 if (assembly && (assemblyEvidence == null))
91 throw new ArgumentNullException ("assemblyEvidence");
93 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
95 if (domainEvidenceType == null) {
96 storageFile._domainIdentity = GetDomainIdentityFromEvidence (domainEvidence);
98 storageFile._domainIdentity = GetTypeFromEvidence (domainEvidence, domainEvidenceType);
101 if (storageFile._domainIdentity == null)
102 throw new IsolatedStorageException (Locale.GetText ("Couldn't find domain identity."));
106 if (assemblyEvidenceType == null) {
107 storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (assemblyEvidence);
109 storageFile._assemblyIdentity = GetTypeFromEvidence (assemblyEvidence, assemblyEvidenceType);
112 if (storageFile._assemblyIdentity == null)
113 throw new IsolatedStorageException (Locale.GetText ("Couldn't find assembly identity."));
116 storageFile.PostInit ();
120 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, object domainIdentity, object assemblyIdentity)
124 if (((scope & IsolatedStorageScope.Domain) != 0) && (domainIdentity == null))
125 throw new ArgumentNullException ("domainIdentity");
127 bool assembly = ((scope & IsolatedStorageScope.Assembly) != 0);
128 if (assembly && (assemblyIdentity == null))
129 throw new ArgumentNullException ("assemblyIdentity");
131 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
133 storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
134 storageFile._domainIdentity = domainIdentity;
135 storageFile._assemblyIdentity = assemblyIdentity;
136 storageFile.PostInit ();
140 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, Type domainEvidenceType, Type assemblyEvidenceType)
143 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
144 if ((scope & IsolatedStorageScope.Domain) != 0) {
145 storageFile._domainIdentity = GetTypeFromEvidence (AppDomain.CurrentDomain.Evidence, domainEvidenceType);
147 if ((scope & IsolatedStorageScope.Assembly) != 0) {
148 Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
149 storageFile._fullEvidences = e;
150 storageFile._assemblyIdentity = GetTypeFromEvidence (e, assemblyEvidenceType);
152 storageFile.PostInit ();
156 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, object applicationIdentity)
159 if (applicationIdentity == null)
160 throw new ArgumentNullException ("applicationIdentity");
162 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
163 storageFile._applicationIdentity = applicationIdentity;
164 storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
165 storageFile.PostInit ();
169 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, Type applicationEvidenceType)
172 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
173 storageFile.InitStore (scope, applicationEvidenceType);
174 storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
175 storageFile.PostInit ();
179 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.ApplicationIsolationByMachine)]
180 public static IsolatedStorageFile GetMachineStoreForApplication ()
182 IsolatedStorageScope scope = IsolatedStorageScope.Machine | IsolatedStorageScope.Application;
183 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
184 storageFile.InitStore (scope, null);
185 storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
186 storageFile.PostInit ();
190 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.AssemblyIsolationByMachine)]
191 public static IsolatedStorageFile GetMachineStoreForAssembly ()
193 IsolatedStorageScope scope = IsolatedStorageScope.Machine | IsolatedStorageScope.Assembly;
194 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
195 Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
196 storageFile._fullEvidences = e;
197 storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
198 storageFile.PostInit ();
202 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.DomainIsolationByMachine)]
203 public static IsolatedStorageFile GetMachineStoreForDomain ()
205 IsolatedStorageScope scope = IsolatedStorageScope.Machine | IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly;
206 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
207 storageFile._domainIdentity = GetDomainIdentityFromEvidence (AppDomain.CurrentDomain.Evidence);
208 Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
209 storageFile._fullEvidences = e;
210 storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
211 storageFile.PostInit ();
215 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.ApplicationIsolationByUser)]
216 public static IsolatedStorageFile GetUserStoreForApplication ()
218 IsolatedStorageScope scope = IsolatedStorageScope.User | IsolatedStorageScope.Application;
219 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
220 storageFile.InitStore (scope, null);
221 storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
222 storageFile.PostInit ();
226 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.AssemblyIsolationByUser)]
227 public static IsolatedStorageFile GetUserStoreForAssembly ()
229 IsolatedStorageScope scope = IsolatedStorageScope.User | IsolatedStorageScope.Assembly;
230 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
231 Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
232 storageFile._fullEvidences = e;
233 storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
234 storageFile.PostInit ();
238 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.DomainIsolationByUser)]
239 public static IsolatedStorageFile GetUserStoreForDomain ()
241 IsolatedStorageScope scope = IsolatedStorageScope.User | IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly;
242 IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
243 storageFile._domainIdentity = GetDomainIdentityFromEvidence (AppDomain.CurrentDomain.Evidence);
244 Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
245 storageFile._fullEvidences = e;
246 storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
247 storageFile.PostInit ();
251 public static void Remove (IsolatedStorageScope scope)
253 string dir = GetIsolatedStorageRoot (scope);
254 Directory.Delete (dir, true);
257 // internal static stuff
259 // Security Note: We're using InternalGetFolderPath because
260 // IsolatedStorage must be able to work even if we do not have
261 // FileIOPermission's PathDiscovery permissions
262 internal static string GetIsolatedStorageRoot (IsolatedStorageScope scope)
264 // IsolatedStorageScope mixes several flags into one.
265 // This first level deals with the root directory - it
266 // is decided based on User, User+Roaming or Machine
269 if ((scope & IsolatedStorageScope.User) != 0) {
270 if ((scope & IsolatedStorageScope.Roaming) != 0) {
271 root = Environment.InternalGetFolderPath (Environment.SpecialFolder.LocalApplicationData);
273 root = Environment.InternalGetFolderPath (Environment.SpecialFolder.ApplicationData);
276 } else if ((scope & IsolatedStorageScope.Machine) != 0) {
277 root = Environment.InternalGetFolderPath (Environment.SpecialFolder.CommonApplicationData);
282 string msg = Locale.GetText ("Couldn't access storage location for '{0}'.");
283 throw new IsolatedStorageException (String.Format (msg, scope));
286 return Path.Combine (root, ".isolated-storage");
289 private static void Demand (IsolatedStorageScope scope)
291 if (SecurityManager.SecurityEnabled) {
292 IsolatedStorageFilePermission isfp = new IsolatedStorageFilePermission (PermissionState.None);
293 isfp.UsageAllowed = ScopeToContainment (scope);
298 private static IsolatedStorageContainment ScopeToContainment (IsolatedStorageScope scope)
301 case IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly | IsolatedStorageScope.User:
302 return IsolatedStorageContainment.DomainIsolationByUser;
303 case IsolatedStorageScope.Assembly | IsolatedStorageScope.User:
304 return IsolatedStorageContainment.AssemblyIsolationByUser;
305 case IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly | IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
306 return IsolatedStorageContainment.DomainIsolationByRoamingUser;
307 case IsolatedStorageScope.Assembly | IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
308 return IsolatedStorageContainment.AssemblyIsolationByRoamingUser;
310 case IsolatedStorageScope.Application | IsolatedStorageScope.User:
311 return IsolatedStorageContainment.ApplicationIsolationByUser;
312 case IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly | IsolatedStorageScope.Machine:
313 return IsolatedStorageContainment.DomainIsolationByMachine;
314 case IsolatedStorageScope.Assembly | IsolatedStorageScope.Machine:
315 return IsolatedStorageContainment.AssemblyIsolationByMachine;
316 case IsolatedStorageScope.Application | IsolatedStorageScope.Machine:
317 return IsolatedStorageContainment.ApplicationIsolationByMachine;
318 case IsolatedStorageScope.Application | IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
319 return IsolatedStorageContainment.ApplicationIsolationByRoamingUser;
322 // unknown ?!?! then ask for maximum (unrestricted)
323 return IsolatedStorageContainment.UnrestrictedIsolatedStorage;
327 internal static ulong GetDirectorySize (DirectoryInfo di)
331 foreach (FileInfo fi in di.GetFiles ())
332 size += (ulong) fi.Length;
334 foreach (DirectoryInfo d in di.GetDirectories ())
335 size += GetDirectorySize (d);
342 private DirectoryInfo directory;
344 private IsolatedStorageFile (IsolatedStorageScope scope)
346 storage_scope = scope;
349 internal IsolatedStorageFile (IsolatedStorageScope scope, string location)
351 storage_scope = scope;
352 directory = new DirectoryInfo (location);
353 if (!directory.Exists) {
354 string msg = Locale.GetText ("Invalid storage.");
355 throw new IsolatedStorageException (msg);
357 // load the identities
360 ~IsolatedStorageFile ()
364 private void PostInit ()
366 string root = GetIsolatedStorageRoot (Scope);
369 if (_applicationIdentity != null) {
370 dir = String.Format ("a{0}{1}", SeparatorInternal, GetNameFromIdentity (_applicationIdentity));
373 if (_domainIdentity != null) {
374 dir = String.Format ("d{0}{1}{0}{2}", SeparatorInternal,
375 GetNameFromIdentity (_domainIdentity), GetNameFromIdentity (_assemblyIdentity));
376 } else if (_assemblyIdentity != null) {
377 dir = String.Format ("d{0}none{0}{1}", SeparatorInternal, GetNameFromIdentity (_assemblyIdentity));
379 throw new IsolatedStorageException (Locale.GetText ("No code identity available."));
382 root = Path.Combine (root, dir);
384 // identities have been selected
385 directory = new DirectoryInfo (root);
386 if (!directory.Exists) {
388 SaveIdentities (root);
392 [CLSCompliant(false)]
393 public override ulong CurrentSize {
394 get { return GetDirectorySize (directory); }
397 [CLSCompliant(false)]
398 public override ulong MaximumSize {
399 // return an ulong but default is signed long
401 if (!SecurityManager.SecurityEnabled)
402 return Int64.MaxValue;
408 if (_fullEvidences != null) {
409 // if possible use the complete evidences we had
410 // for computing the X identity
414 // otherwise use what was provided
415 if (_assemblyIdentity != null)
416 e.AddHost (_assemblyIdentity);
419 throw new InvalidOperationException (
420 Locale.GetText ("Couldn't get the quota from the available evidences."));
423 PermissionSet denied = null;
424 PermissionSet ps = SecurityManager.ResolvePolicy (e, null, null, null, out denied);
425 IsolatedStoragePermission isp = GetPermission (ps);
427 if (ps.IsUnrestricted ()) {
428 _maxSize = Int64.MaxValue; /* default value */
430 throw new InvalidOperationException (
431 Locale.GetText ("No quota from the available evidences."));
434 _maxSize = (ulong) isp.UserQuota;
441 internal string Root {
442 get { return directory.FullName; }
451 public void CreateDirectory (string dir)
453 directory.CreateSubdirectory (dir);
456 public void DeleteDirectory (string dir)
458 DirectoryInfo subdir = directory.CreateSubdirectory (dir);
462 public void DeleteFile (string file)
464 File.Delete (Path.Combine (directory.FullName, file));
467 public void Dispose ()
469 // nothing to dispose, anyway we want to please the tools
470 GC.SuppressFinalize (this);
473 public string[] GetDirectoryNames (string searchPattern)
475 DirectoryInfo[] adi = directory.GetDirectories (searchPattern);
476 return GetNames (adi);
479 private string[] GetNames (FileSystemInfo[] afsi)
481 string[] r = new string[afsi.Length];
482 for (int i = 0; i != afsi.Length; ++i)
487 public string[] GetFileNames (string searchPattern)
489 FileInfo[] afi = directory.GetFiles (searchPattern);
490 return GetNames (afi);
493 public override void Remove ()
495 directory.Delete (true);
499 protected override IsolatedStoragePermission GetPermission (PermissionSet ps)
503 return (IsolatedStoragePermission) ps.GetPermission (typeof (IsolatedStorageFilePermission));
508 private string GetNameFromIdentity (object identity)
510 // Note: Default evidences return an XML string with ToString
511 byte[] id = Encoding.UTF8.GetBytes (identity.ToString ());
512 SHA1 hash = SHA1.Create ();
513 // this create an unique name for an identity - bad identities like Url
514 // results in bad (i.e. changing) names.
515 byte[] full = hash.ComputeHash (id, 0, id.Length);
516 byte[] half = new byte [10];
517 Buffer.BlockCopy (full, 0, half, 0, half.Length);
518 return CryptoConvert.ToHex (half);
521 private static object GetTypeFromEvidence (Evidence e, Type t)
523 foreach (object o in e) {
524 if (o.GetType () == t)
530 internal static object GetAssemblyIdentityFromEvidence (Evidence e)
533 // a. a Publisher evidence
534 object identity = GetTypeFromEvidence (e, typeof (Publisher));
535 if (identity != null)
537 // b. a StrongName evidence
538 identity = GetTypeFromEvidence (e, typeof (StrongName));
539 if (identity != null)
542 return GetTypeFromEvidence (e, typeof (Url));
545 internal static object GetDomainIdentityFromEvidence (Evidence e)
548 // a. a ApplicationDirectory evidence
549 object identity = GetTypeFromEvidence (e, typeof (ApplicationDirectory));
550 if (identity != null)
553 return GetTypeFromEvidence (e, typeof (Url));
557 private struct Identities {
558 public object Application;
559 public object Assembly;
560 public object Domain;
562 public Identities (object application, object assembly, object domain)
564 Application = application;
570 [SecurityPermission (SecurityAction.Assert, SerializationFormatter = true)]
571 private void LoadIdentities (string root)
573 if (!File.Exists (root + ".storage"))
574 throw new IsolatedStorageException (Locale.GetText ("Missing identities."));
576 using (FileStream fs = File.OpenRead (root + ".storage")) {
577 BinaryFormatter deformatter = new BinaryFormatter ();
578 Identities identities = (Identities) deformatter.Deserialize (fs);
581 _applicationIdentity = identities.Application;
582 _assemblyIdentity = identities.Assembly;
583 _domainIdentity = identities.Domain;
587 [SecurityPermission (SecurityAction.Assert, SerializationFormatter = true)]
588 private void SaveIdentities (string root)
590 Identities identities = new Identities (_applicationIdentity, _assemblyIdentity, _domainIdentity);
591 using (FileStream fs = File.OpenWrite (root + ".storage")) {
592 BinaryFormatter formatter = new BinaryFormatter ();
593 formatter.Serialize (fs, identities);