BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / class / corlib / System.IO.IsolatedStorage / IsolatedStorageFile.cs
1 //
2 // System.IO.IsolatedStorage.IsolatedStorageFile
3 //
4 // Authors
5 //      Jonathan Pryor (jonpryor@vt.edu)
6 //      Sebastien Pouliot  <sebastien@ximian.com>
7 //
8 // (C) 2003 Jonathan Pryor
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
10 //
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:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
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.
29 //
30 #if !MOONLIGHT
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;
39 using System.Text;
40 using System.Threading;
41
42 using Mono.Security.Cryptography;
43
44 namespace System.IO.IsolatedStorage {
45
46         // This is a terribly named class.  It doesn't actually represent a file as
47         // much as a directory
48
49
50         [ComVisible (true)]
51         // FIXME: Further limit the assertion when imperative Assert is implemented
52         [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
53         public sealed class IsolatedStorageFile : IsolatedStorage, IDisposable {
54 #if !MOBILE
55                 private bool _resolved;
56                 private ulong _maxSize;
57 #endif
58                 private Evidence _fullEvidences;
59                 private static Mutex mutex = new Mutex ();
60 #if NET_4_0 || MOBILE
61                 private bool closed;
62                 private bool disposed;
63 #endif
64
65                 public static IEnumerator GetEnumerator (IsolatedStorageScope scope)
66                 {
67                         Demand (scope);
68
69                         switch (scope) {
70                         case IsolatedStorageScope.User:
71                         case IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
72                         case IsolatedStorageScope.Machine:
73                                 break;
74                         default:
75                                 string msg = Locale.GetText ("Invalid scope, only User, User|Roaming and Machine are valid");
76                                 throw new ArgumentException (msg);
77                         }
78
79                         return new IsolatedStorageFileEnumerator (scope, GetIsolatedStorageRoot (scope));
80                 }
81
82                 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope,
83                         Evidence domainEvidence, Type domainEvidenceType,
84                         Evidence assemblyEvidence, Type assemblyEvidenceType)
85                 {
86                         Demand (scope);
87
88                         bool domain = ((scope & IsolatedStorageScope.Domain) != 0);
89                         if (domain && (domainEvidence == null))
90                                 throw new ArgumentNullException ("domainEvidence");
91
92                         bool assembly = ((scope & IsolatedStorageScope.Assembly) != 0);
93                         if (assembly && (assemblyEvidence == null))
94                                 throw new ArgumentNullException ("assemblyEvidence");
95
96                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
97 #if !MOBILE
98                         if (domain) {
99                                 if (domainEvidenceType == null) {
100                                         storageFile._domainIdentity = GetDomainIdentityFromEvidence (domainEvidence);
101                                 } else {
102                                         storageFile._domainIdentity = GetTypeFromEvidence (domainEvidence, domainEvidenceType);
103                                 }
104
105                                 if (storageFile._domainIdentity == null)
106                                         throw new IsolatedStorageException (Locale.GetText ("Couldn't find domain identity."));
107                         }
108
109                         if (assembly) {
110                                 if (assemblyEvidenceType == null) {
111                                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (assemblyEvidence);
112                                 } else {
113                                         storageFile._assemblyIdentity = GetTypeFromEvidence (assemblyEvidence, assemblyEvidenceType);
114                                 }
115
116                                 if (storageFile._assemblyIdentity == null)
117                                         throw new IsolatedStorageException (Locale.GetText ("Couldn't find assembly identity."));
118                         }
119 #endif
120                         storageFile.PostInit ();
121                         return storageFile;
122                 }
123
124                 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, object domainIdentity, object assemblyIdentity)
125                 {
126                         Demand (scope);
127
128                         if (((scope & IsolatedStorageScope.Domain) != 0) && (domainIdentity == null))
129                                 throw new ArgumentNullException ("domainIdentity");
130
131                         bool assembly = ((scope & IsolatedStorageScope.Assembly) != 0);
132                         if (assembly && (assemblyIdentity == null))
133                                 throw new ArgumentNullException ("assemblyIdentity");
134
135                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
136 #if !MOBILE
137                         if (assembly)
138                                 storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
139 #endif
140                         storageFile._domainIdentity = domainIdentity;
141                         storageFile._assemblyIdentity = assemblyIdentity;
142                         storageFile.PostInit ();
143                         return storageFile;
144                 }
145
146                 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, Type domainEvidenceType, Type assemblyEvidenceType)
147                 {
148                         Demand (scope);
149                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
150 #if !MOBILE
151                         if ((scope & IsolatedStorageScope.Domain) != 0) {
152                                 if (domainEvidenceType == null)
153                                         domainEvidenceType = typeof (Url);
154                                 storageFile._domainIdentity = GetTypeFromEvidence (AppDomain.CurrentDomain.Evidence, domainEvidenceType);
155                         }
156                         if ((scope & IsolatedStorageScope.Assembly) != 0) {
157                                 Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
158                                 storageFile._fullEvidences = e;
159                                 if ((scope & IsolatedStorageScope.Domain) != 0) {
160                                         if (assemblyEvidenceType == null)
161                                                 assemblyEvidenceType = typeof (Url);
162                                         storageFile._assemblyIdentity = GetTypeFromEvidence (e, assemblyEvidenceType);
163                                 } else {
164                                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
165                                 }
166                         }
167 #endif
168                         storageFile.PostInit ();
169                         return storageFile;
170                 }
171                 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, object applicationIdentity)
172                 {
173                         Demand (scope);
174                         if (applicationIdentity == null)
175                                 throw new ArgumentNullException ("applicationIdentity");
176
177                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
178                         storageFile._applicationIdentity = applicationIdentity;
179 #if !MOBILE
180                         storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
181 #endif
182                         storageFile.PostInit ();
183                         return storageFile;
184                 }
185
186                 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, Type applicationEvidenceType)
187                 {
188                         Demand (scope);
189                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
190                         storageFile.InitStore (scope, applicationEvidenceType);
191 #if !MOBILE
192                         storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
193 #endif
194                         storageFile.PostInit ();
195                         return storageFile;
196                 }
197
198                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.ApplicationIsolationByMachine)]
199                 public static IsolatedStorageFile GetMachineStoreForApplication ()
200                 {
201                         IsolatedStorageScope scope = IsolatedStorageScope.Machine | IsolatedStorageScope.Application;
202                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
203                         storageFile.InitStore (scope, null);
204 #if !MOBILE
205                         storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
206 #endif
207                         storageFile.PostInit ();
208                         return storageFile;
209                 }
210
211                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.AssemblyIsolationByMachine)]
212                 public static IsolatedStorageFile GetMachineStoreForAssembly ()
213                 {
214                         IsolatedStorageScope scope = IsolatedStorageScope.Machine | IsolatedStorageScope.Assembly;
215                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
216 #if !MOBILE
217                         Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
218                         storageFile._fullEvidences = e;
219                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
220 #endif
221                         storageFile.PostInit ();
222                         return storageFile;
223                 }
224
225                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.DomainIsolationByMachine)]
226                 public static IsolatedStorageFile GetMachineStoreForDomain ()
227                 {
228                         IsolatedStorageScope scope = IsolatedStorageScope.Machine | IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly;
229                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
230 #if !MOBILE
231                         storageFile._domainIdentity = GetDomainIdentityFromEvidence (AppDomain.CurrentDomain.Evidence);
232                         Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
233                         storageFile._fullEvidences = e;
234                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
235 #endif
236                         storageFile.PostInit ();
237                         return storageFile;
238                 }
239
240                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.ApplicationIsolationByUser)]
241                 public static IsolatedStorageFile GetUserStoreForApplication ()
242                 {
243                         IsolatedStorageScope scope = IsolatedStorageScope.User | IsolatedStorageScope.Application;
244                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
245                         storageFile.InitStore (scope, null);
246 #if !MOBILE
247                         storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
248 #endif
249                         storageFile.PostInit ();
250                         return storageFile;
251                 }
252
253                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.AssemblyIsolationByUser)]
254                 public static IsolatedStorageFile GetUserStoreForAssembly ()
255                 {
256                         IsolatedStorageScope scope = IsolatedStorageScope.User | IsolatedStorageScope.Assembly;
257                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
258 #if !MOBILE
259                         Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
260                         storageFile._fullEvidences = e;
261                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
262 #endif
263                         storageFile.PostInit ();
264                         return storageFile;
265                 }
266
267                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.DomainIsolationByUser)]
268                 public static IsolatedStorageFile GetUserStoreForDomain ()
269                 {
270                         IsolatedStorageScope scope = IsolatedStorageScope.User | IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly;
271                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
272 #if !MOBILE
273                         storageFile._domainIdentity = GetDomainIdentityFromEvidence (AppDomain.CurrentDomain.Evidence);
274                         Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
275                         storageFile._fullEvidences = e;
276                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
277 #endif
278                         storageFile.PostInit ();
279                         return storageFile;
280                 }
281
282 #if NET_4_0 || MOBILE
283                 [ComVisible (false)]
284                 public static IsolatedStorageFile GetUserStoreForSite ()
285                 {
286                         throw new NotSupportedException ();
287                 }
288 #endif
289
290                 public static void Remove (IsolatedStorageScope scope)
291                 {
292                         string dir = GetIsolatedStorageRoot (scope);
293                         if (!Directory.Exists (dir))
294                                 return;
295
296                         try {
297                                 Directory.Delete (dir, true);
298                         } catch (IOException) {
299                                 throw new IsolatedStorageException ("Could not remove storage.");
300                         }
301                 }
302
303                 // internal static stuff
304
305                 // Security Note: We're using InternalGetFolderPath because 
306                 // IsolatedStorage must be able to work even if we do not have
307                 // FileIOPermission's PathDiscovery permissions
308                 internal static string GetIsolatedStorageRoot (IsolatedStorageScope scope)
309                 {
310                         // IsolatedStorageScope mixes several flags into one.
311                         // This first level deals with the root directory - it
312                         // is decided based on User, User+Roaming or Machine
313                         string root = null;
314
315                         if ((scope & IsolatedStorageScope.User) != 0) {
316                                 if ((scope & IsolatedStorageScope.Roaming) != 0) {
317                                         root = Environment.UnixGetFolderPath (Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create);
318                                 } else {
319                                         root = Environment.UnixGetFolderPath (Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create);
320                                 }
321                         } else if ((scope & IsolatedStorageScope.Machine) != 0) {
322                                 root = Environment.UnixGetFolderPath (Environment.SpecialFolder.CommonApplicationData, Environment.SpecialFolderOption.Create);
323                         }
324
325                         if (root == null) {
326                                 string msg = Locale.GetText ("Couldn't access storage location for '{0}'.");
327                                 throw new IsolatedStorageException (String.Format (msg, scope));
328                         }
329
330                         return Path.Combine (root, ".isolated-storage");
331                 }
332
333                 private static void Demand (IsolatedStorageScope scope)
334                 {
335 #if !MOBILE
336                         if (SecurityManager.SecurityEnabled) {
337                                 IsolatedStorageFilePermission isfp = new IsolatedStorageFilePermission (PermissionState.None);
338                                 isfp.UsageAllowed = ScopeToContainment (scope);
339                                 isfp.Demand ();
340                         }
341 #endif
342                 }
343
344                 private static IsolatedStorageContainment ScopeToContainment (IsolatedStorageScope scope)
345                 {
346                         switch (scope) {
347                         case IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly | IsolatedStorageScope.User:
348                                 return IsolatedStorageContainment.DomainIsolationByUser;
349                         case IsolatedStorageScope.Assembly | IsolatedStorageScope.User:
350                                 return IsolatedStorageContainment.AssemblyIsolationByUser;
351                         case IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly | IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
352                                 return IsolatedStorageContainment.DomainIsolationByRoamingUser;
353                         case IsolatedStorageScope.Assembly | IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
354                                 return IsolatedStorageContainment.AssemblyIsolationByRoamingUser;
355                         case IsolatedStorageScope.Application | IsolatedStorageScope.User:
356                                 return IsolatedStorageContainment.ApplicationIsolationByUser;
357                         case IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly | IsolatedStorageScope.Machine:
358                                 return IsolatedStorageContainment.DomainIsolationByMachine;
359                         case IsolatedStorageScope.Assembly | IsolatedStorageScope.Machine:
360                                 return IsolatedStorageContainment.AssemblyIsolationByMachine;
361                         case IsolatedStorageScope.Application | IsolatedStorageScope.Machine:
362                                 return IsolatedStorageContainment.ApplicationIsolationByMachine;
363                         case IsolatedStorageScope.Application | IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
364                                 return IsolatedStorageContainment.ApplicationIsolationByRoamingUser;
365                         default:
366                                 // unknown ?!?! then ask for maximum (unrestricted)
367                                 return IsolatedStorageContainment.UnrestrictedIsolatedStorage;
368                         }
369                 }
370
371                 internal static ulong GetDirectorySize (DirectoryInfo di)
372                 {
373                         ulong size = 0;
374
375                         foreach (FileInfo fi in di.GetFiles ())
376                                 size += (ulong) fi.Length;
377
378                         foreach (DirectoryInfo d in di.GetDirectories ())
379                                 size += GetDirectorySize (d);
380
381                         return size;
382                 }
383
384                 // non-static stuff
385
386                 private DirectoryInfo directory;
387
388                 private IsolatedStorageFile (IsolatedStorageScope scope)
389                 {
390                         storage_scope = scope;
391                 }
392
393                 internal IsolatedStorageFile (IsolatedStorageScope scope, string location)
394                 {
395                         storage_scope = scope;
396                         directory = new DirectoryInfo (location);
397                         if (!directory.Exists) {
398                                 string msg = Locale.GetText ("Invalid storage.");
399                                 throw new IsolatedStorageException (msg);
400                         }
401                         // load the identities
402                 }
403
404                 ~IsolatedStorageFile ()
405                 {
406                 }
407
408                 private void PostInit ()
409                 {
410                         string root = GetIsolatedStorageRoot (Scope);
411                         string dir = null;
412 #if MOBILE
413                         dir = "";
414 #else
415                         if (_applicationIdentity != null) {
416                                 dir = String.Format ("a{0}{1}", SeparatorInternal, GetNameFromIdentity (_applicationIdentity));
417                         } else if (_domainIdentity != null) {
418                                 dir = String.Format ("d{0}{1}{0}{2}", SeparatorInternal,
419                                         GetNameFromIdentity (_domainIdentity), GetNameFromIdentity (_assemblyIdentity));
420                         } else if (_assemblyIdentity != null) {
421                                 dir = String.Format ("d{0}none{0}{1}", SeparatorInternal, GetNameFromIdentity (_assemblyIdentity));
422                         } else {
423                                 throw new IsolatedStorageException (Locale.GetText ("No code identity available."));
424                         }
425 #endif
426
427                         root = Path.Combine (root, dir);
428
429                         // identities have been selected
430                         directory = new DirectoryInfo (root);
431                         if (!directory.Exists) {
432                                 try {
433                                         directory.Create ();
434 #if !MOBILE
435                                         SaveIdentities (root);
436 #endif
437                                 }
438                                 catch (IOException) {
439                                 }
440                         }
441                 }
442
443                 [CLSCompliant(false)]
444 #if NET_4_0 || MOBILE
445                 [Obsolete]
446 #endif
447                 public override ulong CurrentSize {
448                         get { return GetDirectorySize (directory); }
449                 }
450
451                 [CLSCompliant(false)]
452 #if NET_4_0 || MOBILE
453                 [Obsolete]
454 #endif
455                 public override ulong MaximumSize {
456                         // return an ulong but default is signed long
457                         get {
458 #if MOBILE
459                                 // no security manager is active so the rest of the code is useless
460                                 return Int64.MaxValue;
461 #else
462                                 if (!SecurityManager.SecurityEnabled)
463                                         return Int64.MaxValue;
464
465                                 if (_resolved)
466                                         return _maxSize;
467
468                                 Evidence e = null;
469                                 if (_fullEvidences != null) {
470                                         // if possible use the complete evidences we had
471                                         // for computing the X identity
472                                         e = _fullEvidences;
473                                 } else {
474                                         e = new Evidence ();
475                                         // otherwise use what was provided
476                                         if (_assemblyIdentity != null)
477                                                 e.AddHost (_assemblyIdentity);
478                                 }
479                                 if (e.Count < 1) {
480                                         throw new InvalidOperationException (
481                                                 Locale.GetText ("Couldn't get the quota from the available evidences."));
482                                 }
483
484                                 PermissionSet denied = null;
485                                 PermissionSet ps = SecurityManager.ResolvePolicy (e, null, null, null, out denied);
486                                 IsolatedStoragePermission isp = GetPermission (ps);
487                                 if (isp == null) {
488                                         if (ps.IsUnrestricted ()) {
489                                                 _maxSize = Int64.MaxValue; /* default value */
490                                         } else {
491                                                 throw new InvalidOperationException (
492                                                         Locale.GetText ("No quota from the available evidences."));
493                                         }
494                                 } else {
495                                         _maxSize = (ulong) isp.UserQuota;
496                                 }
497                                 _resolved = true;
498                                 return _maxSize;
499 #endif
500                         }
501                 }
502
503                 internal string Root {
504                         get { return directory.FullName; }
505                 }
506
507 #if NET_4_0 || MOBILE
508                 [ComVisible (false)]
509                 public override long AvailableFreeSpace {
510                         get {
511                                 CheckOpen ();
512
513                                 // See the notes for 'Quota'
514                                 return Int64.MaxValue;
515
516                         }
517                 }
518
519                 [ComVisible (false)]
520                 public override long Quota {
521                         get {
522                                 CheckOpen ();
523
524                                 // Since we don't fully support CAS, we are likely
525                                 // going to return Int64.MaxValue always, but we return
526                                 // MaximumSize just in case.
527                                 return (long)MaximumSize;
528                         }
529                 }
530
531                 [ComVisible (false)]
532                 public override long UsedSize {
533                         get {
534                                 CheckOpen ();
535                                 return (long)GetDirectorySize (directory);
536                         }
537                 }
538
539                 [ComVisible (false)]
540                 public static bool IsEnabled {
541                         get {
542                                 return true;
543                         }
544                 }
545
546                 internal bool IsClosed {
547                         get {
548                                 return closed;
549                         }
550                 }
551
552                 internal bool IsDisposed {
553                         get {
554                                 return disposed;
555                         }
556                 }
557 #endif
558
559                 // methods
560
561                 public void Close ()
562                 {
563 #if NET_4_0 || MOBILE
564                         closed = true;
565 #endif
566                 }
567
568                 public void CreateDirectory (string dir)
569                 {
570                         if (dir == null)
571                                 throw new ArgumentNullException ("dir");
572
573                         if (dir.IndexOfAny (Path.PathSeparatorChars) < 0) {
574                                 if (directory.GetFiles (dir).Length > 0)
575 #if NET_4_0 || MOBILE
576                                         throw new IsolatedStorageException ("Unable to create directory.");
577 #else
578                                         throw new IOException (Locale.GetText ("Directory name already exists as a file."));
579 #endif
580                                 directory.CreateSubdirectory (dir);
581                         } else {
582                                 string[] dirs = dir.Split (Path.PathSeparatorChars);
583                                 DirectoryInfo dinfo = directory;
584
585                                 for (int i = 0; i < dirs.Length; i++) {
586                                         if (dinfo.GetFiles (dirs [i]).Length > 0)
587 #if NET_4_0 || MOBILE
588                                                 throw new IsolatedStorageException ("Unable to create directory.");
589 #else
590                                                 throw new IOException (Locale.GetText (
591                                                         "Part of the directory name already exists as a file."));
592 #endif
593                                         dinfo = dinfo.CreateSubdirectory (dirs [i]);
594                                 }
595                         }
596                 }
597
598 #if NET_4_0 || MOBILE
599                 [ComVisible (false)]
600                 public void CopyFile (string sourceFileName, string destinationFileName)
601                 {
602                         CopyFile (sourceFileName, destinationFileName, false);
603                 }
604
605                 [ComVisible (false)]
606                 public void CopyFile (string sourceFileName, string destinationFileName, bool overwrite)
607                 {
608                         if (sourceFileName == null)
609                                 throw new ArgumentNullException ("sourceFileName");
610                         if (destinationFileName == null)
611                                 throw new ArgumentNullException ("destinationFileName");
612                         if (sourceFileName.Trim ().Length == 0)
613                                 throw new ArgumentException ("An empty file name is not valid.", "sourceFileName");
614                         if (destinationFileName.Trim ().Length == 0)
615                                 throw new ArgumentException ("An empty file name is not valid.", "destinationFileName");
616
617                         CheckOpen ();
618
619                         string source_full_path = Path.Combine (directory.FullName, sourceFileName);
620                         string dest_full_path = Path.Combine (directory.FullName, destinationFileName);
621
622                         if (!IsPathInStorage (source_full_path) || !IsPathInStorage (dest_full_path))
623                                 throw new IsolatedStorageException ("Operation not allowed.");
624                         // These excs can be thrown from File.Copy, but we can try to detect them from here.
625                         if (!Directory.Exists (Path.GetDirectoryName (source_full_path)))
626                                 throw new DirectoryNotFoundException ("Could not find a part of path '" + sourceFileName + "'.");
627                         if (!File.Exists (source_full_path))
628                                 throw new FileNotFoundException ("Could not find a part of path '" + sourceFileName + "'.");
629                         if (File.Exists (dest_full_path) && !overwrite)
630                                 throw new IsolatedStorageException ("Operation not allowed.");
631
632                         try {
633                                 File.Copy (source_full_path, dest_full_path, overwrite);
634                         } catch (IOException) {
635                                 throw new IsolatedStorageException ("Operation not allowed.");
636                         }
637                 }
638
639                 [ComVisible (false)]
640                 public IsolatedStorageFileStream CreateFile (string path)
641                 {
642                         return new IsolatedStorageFileStream (path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, this);
643                 }
644 #endif
645
646                 public void DeleteDirectory (string dir)
647                 {
648                         try {
649                                 DirectoryInfo subdir = directory.CreateSubdirectory (dir);
650                                 subdir.Delete ();
651                         }
652                         catch {
653                                 // hide the real exception to avoid leaking the full path
654                                 throw new IsolatedStorageException (Locale.GetText ("Could not delete directory '{0}'", dir));
655                         }
656                 }
657
658                 public void DeleteFile (string file)
659                 {
660                         if (file == null)
661                                 throw new ArgumentNullException ("file");
662
663                         string full_path = Path.Combine (directory.FullName, file);
664                         if (!File.Exists (full_path))
665                                 throw new IsolatedStorageException (Locale.GetText ("Could not delete file '{0}'", file));
666
667                         try {
668                                 File.Delete (Path.Combine (directory.FullName, file));
669                         } catch {
670                                 // hide the internal exception, just as DeleteDirectory does.
671                                 throw new IsolatedStorageException (Locale.GetText ("Could not delete file '{0}'", file));
672                         }
673                 }
674
675                 public void Dispose ()
676                 {
677 #if NET_4_0 || MOBILE
678                         // Dispose may be calling Close, but we are not sure
679                         disposed = true;
680 #endif
681                         // nothing to dispose, anyway we want to please the tools
682                         GC.SuppressFinalize (this);
683                 }
684
685 #if NET_4_0 || MOBILE
686                 [ComVisible (false)]
687                 public bool DirectoryExists (string path)
688                 {
689                         if (path == null)
690                                 throw new ArgumentNullException ("path");
691
692                         CheckOpen ();
693
694                         string full_path = Path.Combine (directory.FullName, path);
695                         if (!IsPathInStorage (full_path))
696                                 return false;
697
698                         return Directory.Exists (full_path);
699                 }
700
701                 [ComVisible (false)]
702                 public bool FileExists (string path)
703                 {
704                         if (path == null)
705                                 throw new ArgumentNullException ("path");
706
707                         CheckOpen ();
708
709                         string full_path = Path.Combine (directory.FullName, path);
710                         if (!IsPathInStorage (full_path))
711                                 return false;
712
713                         return File.Exists (full_path);
714                 }
715
716                 [ComVisible (false)]
717                 public DateTimeOffset GetCreationTime (string path)
718                 {
719                         if (path == null)
720                                 throw new ArgumentNullException ("path");
721                         if (path.Trim ().Length == 0)
722                                 throw new ArgumentException ("An empty path is not valid.");
723
724                         CheckOpen ();
725
726                         string full_path = Path.Combine (directory.FullName, path);
727                         if (File.Exists (full_path))
728                                 return File.GetCreationTime (full_path);
729
730                         return Directory.GetCreationTime (full_path);
731                 }
732
733                 [ComVisible (false)]
734                 public DateTimeOffset GetLastAccessTime (string path)
735                 {
736                         if (path == null)
737                                 throw new ArgumentNullException ("path");
738                         if (path.Trim ().Length == 0)
739                                 throw new ArgumentException ("An empty path is not valid.");
740
741                         CheckOpen ();
742
743                         string full_path = Path.Combine (directory.FullName, path);
744                         if (File.Exists (full_path))
745                                 return File.GetLastAccessTime (full_path);
746
747                         return Directory.GetLastAccessTime (full_path);
748                 }
749
750                 [ComVisible (false)]
751                 public DateTimeOffset GetLastWriteTime (string path)
752                 {
753                         if (path == null)
754                                 throw new ArgumentNullException ("path");
755                         if (path.Trim ().Length == 0)
756                                 throw new ArgumentException ("An empty path is not valid.");
757
758                         CheckOpen ();
759
760                         string full_path = Path.Combine (directory.FullName, path);
761                         if (File.Exists (full_path))
762                                 return File.GetLastWriteTime (full_path);
763
764                         return Directory.GetLastWriteTime (full_path);
765                 }
766 #endif
767                 
768                 public string[] GetDirectoryNames (string searchPattern)
769                 {
770                         if (searchPattern == null)
771                                 throw new ArgumentNullException ("searchPattern");
772 #if NET_4_0 || MOBILE
773                         if (searchPattern.Contains (".."))
774                                 throw new ArgumentException ("Search pattern cannot contain '..' to move up directories.", "searchPattern");
775 #endif
776
777                         // note: IsolatedStorageFile accept a "dir/file" pattern which is not allowed by DirectoryInfo
778                         // so we need to split them to get the right results
779                         string path = Path.GetDirectoryName (searchPattern);
780                         string pattern = Path.GetFileName (searchPattern);
781                         DirectoryInfo[] adi = null;
782                         if (path == null || path.Length == 0) {
783                                 adi = directory.GetDirectories (searchPattern);
784                         } else {
785                                 // we're looking for a single result, identical to path (no pattern here)
786                                 DirectoryInfo[] subdirs = directory.GetDirectories (path);
787                                 DirectoryInfo di = subdirs [0];
788                                 // we're also looking for something under the current path (not outside isolated storage)
789                                 if (di.FullName.IndexOf (directory.FullName) >= 0) {
790                                         adi = di.GetDirectories (pattern);
791                                         string[] segments = path.Split (new char [] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
792                                         for (int i = segments.Length - 1; i >= 0; i--) {
793                                                 if (di.Name != segments [i]) {
794                                                         adi = null;
795                                                         break;
796                                                 }
797                                                 di = di.Parent;
798                                         }
799                                 }
800                         }
801                         // CAS, even in FullTrust, normally enforce IsolatedStorage
802                         if (adi == null)
803                                 throw new SecurityException ();
804                          
805                         return GetNames (adi);
806                 }
807
808 #if NET_4_0 || MOBILE
809                 [ComVisible (false)]
810                 public string [] GetDirectoryNames ()
811                 {
812                         return GetDirectoryNames ("*");
813                 }
814 #endif
815
816                 private string[] GetNames (FileSystemInfo[] afsi)
817                 {
818                         string[] r = new string[afsi.Length];
819                         for (int i = 0; i != afsi.Length; ++i)
820                                 r[i] = afsi[i].Name;
821                         return r;
822                 }
823
824                 public string[] GetFileNames (string searchPattern)
825                 {
826                         if (searchPattern == null)
827                                 throw new ArgumentNullException ("searchPattern");
828 #if NET_4_0 || MOBILE
829                         if (searchPattern.Contains (".."))
830                                 throw new ArgumentException ("Search pattern cannot contain '..' to move up directories.", "searchPattern");
831 #endif
832
833                         // note: IsolatedStorageFile accept a "dir/file" pattern which is not allowed by DirectoryInfo
834                         // so we need to split them to get the right results
835                         string path = Path.GetDirectoryName (searchPattern);
836                         string pattern = Path.GetFileName (searchPattern);
837                         FileInfo[] afi = null;
838                         if (path == null || path.Length == 0) {
839                                 afi = directory.GetFiles (searchPattern);
840                         } else {
841                                 DirectoryInfo[] subdirs = directory.GetDirectories (path);
842                                 // we're looking for a single result, identical to path (no pattern here)
843                                 // we're also looking for something under the current path (not outside isolated storage)
844                                 if ((subdirs.Length == 1) && (subdirs [0].Name == path) && (subdirs [0].FullName.IndexOf (directory.FullName) >= 0)) {
845                                         afi = subdirs [0].GetFiles (pattern);
846                                 } else {
847                                         // CAS, even in FullTrust, normally enforce IsolatedStorage
848                                         throw new SecurityException ();
849                                 }
850                         }
851
852                         return GetNames (afi);
853                 }
854
855 #if NET_4_0 || MOBILE
856                 [ComVisible (false)]
857                 public string [] GetFileNames ()
858                 {
859                         return GetFileNames ("*");
860                 }
861
862                 [ComVisible (false)]
863                 public override bool IncreaseQuotaTo (long newQuotaSize)
864                 {
865                         if (newQuotaSize < Quota)
866                                 throw new ArgumentException ();
867
868                         CheckOpen ();
869
870                         // .Net is supposed to be returning false, as mentioned in the docs.
871                         return false;
872                 }
873
874                 [ComVisible (false)]
875                 public void MoveDirectory (string sourceDirectoryName, string destinationDirectoryName)
876                 {
877                         if (sourceDirectoryName == null)
878                                 throw new ArgumentNullException ("sourceDirectoryName");
879                         if (destinationDirectoryName == null)
880                                 throw new ArgumentNullException ("sourceDirectoryName");
881                         if (sourceDirectoryName.Trim ().Length == 0)
882                                 throw new ArgumentException ("An empty directory name is not valid.", "sourceDirectoryName");
883                         if (destinationDirectoryName.Trim ().Length == 0)
884                                 throw new ArgumentException ("An empty directory name is not valid.", "destinationDirectoryName");
885
886                         CheckOpen ();
887
888                         string src_full_path = Path.Combine (directory.FullName, sourceDirectoryName);
889                         string dest_full_path = Path.Combine (directory.FullName, destinationDirectoryName);
890
891                         if (!IsPathInStorage (src_full_path) || !IsPathInStorage (dest_full_path))
892                                 throw new IsolatedStorageException ("Operation not allowed.");
893                         if (!Directory.Exists (src_full_path))
894                                 throw new DirectoryNotFoundException ("Could not find a part of path '" + sourceDirectoryName + "'.");
895                         if (!Directory.Exists (Path.GetDirectoryName (dest_full_path)))
896                                 throw new DirectoryNotFoundException ("Could not find a part of path '" + destinationDirectoryName + "'.");
897
898                         try {
899                                 Directory.Move (src_full_path, dest_full_path);
900                         } catch (IOException) {
901                                 throw new IsolatedStorageException ("Operation not allowed.");
902                         }
903                 }
904
905                 [ComVisible (false)]
906                 public void MoveFile (string sourceFileName, string destinationFileName)
907                 {
908                         if (sourceFileName == null)
909                                 throw new ArgumentNullException ("sourceFileName");
910                         if (destinationFileName == null)
911                                 throw new ArgumentNullException ("sourceFileName");
912                         if (sourceFileName.Trim ().Length == 0)
913                                 throw new ArgumentException ("An empty file name is not valid.", "sourceFileName");
914                         if (destinationFileName.Trim ().Length == 0)
915                                 throw new ArgumentException ("An empty file name is not valid.", "destinationFileName");
916
917                         CheckOpen ();
918
919                         string source_full_path = Path.Combine (directory.FullName, sourceFileName);
920                         string dest_full_path = Path.Combine (directory.FullName, destinationFileName);
921
922                         if (!IsPathInStorage (source_full_path) || !IsPathInStorage (dest_full_path))
923                                 throw new IsolatedStorageException ("Operation not allowed.");
924                         if (!File.Exists (source_full_path))
925                                 throw new FileNotFoundException ("Could not find a part of path '" + sourceFileName + "'.");
926                         // I expected a DirectoryNotFound exception.
927                         if (!Directory.Exists (Path.GetDirectoryName (dest_full_path)))
928                                 throw new IsolatedStorageException ("Operation not allowed.");
929
930                         try {
931                                 File.Move (source_full_path, dest_full_path);
932                         } catch (IOException) {
933                                 throw new IsolatedStorageException ("Operation not allowed.");
934                         }
935                 }
936
937                 [ComVisible (false)]
938                 public IsolatedStorageFileStream OpenFile (string path, FileMode mode)
939                 {
940                         return new IsolatedStorageFileStream (path, mode, this);
941                 }
942
943                 [ComVisible (false)]
944                 public IsolatedStorageFileStream OpenFile (string path, FileMode mode, FileAccess access)
945                 {
946                         return new IsolatedStorageFileStream (path, mode, access, this);
947                 }
948
949                 [ComVisible (false)]
950                 public IsolatedStorageFileStream OpenFile (string path, FileMode mode, FileAccess access, FileShare share)
951                 {
952                         return new IsolatedStorageFileStream (path, mode, access, share, this);
953                 }
954 #endif
955
956                 public override void Remove ()
957                 {
958 #if NET_4_0 || MOBILE
959                         CheckOpen (false);
960 #endif
961                         try {
962                                 directory.Delete (true);
963                         } catch {
964                                 throw new IsolatedStorageException ("Could not remove storage.");
965                         }
966
967                         // It seems .Net is calling Close from here.
968                         Close ();
969                 }
970
971
972                 protected override IsolatedStoragePermission GetPermission (PermissionSet ps)
973                 {
974                         if (ps == null)
975                                 return null;
976                         return (IsolatedStoragePermission) ps.GetPermission (typeof (IsolatedStorageFilePermission));
977                 }
978
979                 // internal stuff
980 #if NET_4_0 || MOBILE
981                 void CheckOpen ()
982                 {
983                         CheckOpen (true);
984                 }
985
986                 void CheckOpen (bool checkDirExists)
987                 {
988                         if (disposed)
989                                 throw new ObjectDisposedException ("IsolatedStorageFile");
990                         if (closed)
991                                 throw new InvalidOperationException ("Storage needs to be open for this operation.");
992                         if (checkDirExists && !Directory.Exists (directory.FullName))
993                                 throw new IsolatedStorageException ("Isolated storage has been removed or disabled.");
994                 }
995
996                 bool IsPathInStorage (string path)
997                 {
998                         return Path.GetFullPath (path).StartsWith (directory.FullName);
999                 }
1000 #endif
1001
1002 #if !MOBILE
1003                 private string GetNameFromIdentity (object identity)
1004                 {
1005                         // Note: Default evidences return an XML string with ToString
1006                         byte[] id = Encoding.UTF8.GetBytes (identity.ToString ());
1007                         SHA1 hash = SHA1.Create ();
1008                         // this create an unique name for an identity - bad identities like Url
1009                         // results in bad (i.e. changing) names.
1010                         byte[] full = hash.ComputeHash (id, 0, id.Length);
1011                         byte[] half = new byte [10];
1012                         Buffer.BlockCopy (full, 0, half, 0, half.Length);
1013                         return CryptoConvert.ToHex (half);
1014                 }
1015
1016                 private static object GetTypeFromEvidence (Evidence e, Type t)
1017                 {
1018                         foreach (object o in e) {
1019                                 if (o.GetType () == t)
1020                                         return o;
1021                         }
1022                         return null;
1023                 }
1024
1025                 internal static object GetAssemblyIdentityFromEvidence (Evidence e)
1026                 {
1027                         // we prefer...
1028                         // a. a Publisher evidence
1029                         object identity = GetTypeFromEvidence (e, typeof (Publisher));
1030                         if (identity != null)
1031                                 return identity;
1032                         // b. a StrongName evidence
1033                         identity = GetTypeFromEvidence (e, typeof (StrongName));
1034                         if (identity != null)
1035                                 return identity;
1036                         // c. a Url evidence
1037                         return GetTypeFromEvidence (e, typeof (Url));
1038                 }
1039
1040                 internal static object GetDomainIdentityFromEvidence (Evidence e)
1041                 {
1042                         // we prefer...
1043                         // a. a ApplicationDirectory evidence
1044                         object identity = GetTypeFromEvidence (e, typeof (ApplicationDirectory));
1045                         if (identity != null)
1046                                 return identity;
1047                         // b. a Url evidence
1048                         return GetTypeFromEvidence (e, typeof (Url));
1049                 }
1050
1051                 [Serializable]
1052                 private struct Identities {
1053                         public object Application;
1054                         public object Assembly;
1055                         public object Domain;
1056
1057                         public Identities (object application, object assembly, object domain)
1058                         {
1059                                 Application = application;
1060                                 Assembly = assembly;
1061                                 Domain = domain;
1062                         }
1063                 }
1064 /*
1065                 [SecurityPermission (SecurityAction.Assert, SerializationFormatter = true)]
1066                 private void LoadIdentities (string root)
1067                 {
1068                         if (!File.Exists (root + ".storage"))
1069                                 throw new IsolatedStorageException (Locale.GetText ("Missing identities."));
1070
1071                         BinaryFormatter deformatter = new BinaryFormatter ();
1072                         using (FileStream fs = File.OpenRead (root + ".storage")) {
1073                                 Identities identities = (Identities) deformatter.Deserialize (fs);
1074
1075                                 _applicationIdentity = identities.Application;
1076                                 _assemblyIdentity = identities.Assembly;
1077                                 _domainIdentity = identities.Domain;
1078                         }
1079                 }
1080 */
1081                 [SecurityPermission (SecurityAction.Assert, SerializationFormatter = true)]
1082                 private void SaveIdentities (string root)
1083                 {
1084                         Identities identities = new Identities (_applicationIdentity, _assemblyIdentity, _domainIdentity);
1085                         BinaryFormatter formatter = new BinaryFormatter ();
1086                         mutex.WaitOne ();
1087                         try {
1088                                 using (FileStream fs = File.Create (root + ".storage")) {
1089                                         formatter.Serialize (fs, identities);
1090                                 }
1091                         }
1092                         finally {
1093                                 mutex.ReleaseMutex ();
1094                         }
1095                 }
1096 #endif
1097         }
1098 }
1099 #endif