2 // System.IO.IsolatedStorage.MoonIsolatedStorageFile
4 // Moonlight's implementation for the IsolatedStorageFile
7 // Miguel de Icaza (miguel@novell.com)
8 // Sebastien Pouliot <sebastien@ximian.com>
10 // Copyright (C) 2007, 2008, 2009 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Runtime
.InteropServices
;
35 using System
.Security
;
37 namespace System
.IO
.IsolatedStorage
{
39 // Most of the time there will only be a single instance of both
40 // * Application Store (GetUserStoreForApplication)
41 // * Site Store (GetUserStoreForSite)
42 // However both can have multiple concurrent uses, e.g.
43 // * another instance of the same application (same URL) running in another Moonlight instance
44 // * another application on the same site (i.e. host) for a site store
45 // and share the some quota, i.e. a site and all applications on the sites share the same space
48 // * quota seems computed in (disk) blocks, i.e. a small file will have a (non-small) size
49 // e.g. every files and directories entries takes 1KB
51 public sealed class IsolatedStorageFile
: IDisposable
{
53 static object locker
= new object ();
55 private string basedir
;
57 private bool removed
= false;
58 private bool disposed
= false;
60 internal IsolatedStorageFile (string root
)
65 internal void PreCheck ()
68 throw new ObjectDisposedException ("Storage was disposed");
70 throw new IsolatedStorageException ("Storage was removed");
73 public static IsolatedStorageFile
GetUserStoreForApplication ()
75 return new IsolatedStorageFile (IsolatedStorage
.ApplicationPath
);
78 public static IsolatedStorageFile
GetUserStoreForSite ()
80 return new IsolatedStorageFile (IsolatedStorage
.SitePath
);
83 internal string Verify (string path
)
85 // special case: 'path' would be returned (instead of combined)
86 if ((path
.Length
> 0) && (path
[0] == '/'))
87 path
= path
.Substring (1, path
.Length
- 1);
89 // outside of try/catch since we want to get things like
90 // ArgumentException for invalid characters
91 string combined
= Path
.Combine (basedir
, path
);
93 string full
= Path
.GetFullPath (combined
);
94 if (full
.StartsWith (basedir
))
97 // we do not supply an inner exception since it could contains details about the path
98 throw new IsolatedStorageException ();
100 throw new IsolatedStorageException ();
103 public void CreateDirectory (string dir
)
107 throw new ArgumentNullException ("dir");
108 // empty dir is ignored
110 Directory
.CreateDirectory (Verify (dir
));
113 public IsolatedStorageFileStream
CreateFile (string path
)
117 return new IsolatedStorageFileStream (path
, FileMode
.Create
, this);
119 catch (DirectoryNotFoundException
) {
120 // this can happen if the supplied path includes an unexisting directory
121 throw new IsolatedStorageException ();
125 public void DeleteDirectory (string dir
)
129 throw new ArgumentNullException ("dir");
130 Directory
.Delete (Verify (dir
));
133 public void DeleteFile (string file
)
137 throw new ArgumentNullException ("file");
138 string checked_filename
= Verify (file
);
139 if (!File
.Exists (checked_filename
))
140 throw new IsolatedStorageException ("File does not exists");
141 File
.Delete (checked_filename
);
144 public void Dispose ()
149 public bool DirectoryExists (string path
)
152 return Directory
.Exists (Verify (path
));
155 public bool FileExists (string path
)
158 return File
.Exists (Verify (path
));
161 private string HideAppDir (string path
)
163 // remove the "isolated" part of the path (and the extra '/')
164 return path
.Substring (basedir
.Length
+ 1);
167 private string [] HideAppDirs (string[] paths
)
169 for (int i
=0; i
< paths
.Length
; i
++)
170 paths
[i
] = HideAppDir (paths
[i
]);
174 private void CheckSearchPattern (string searchPattern
)
176 if (searchPattern
== null)
177 throw new ArgumentNullException ("searchPattern");
178 if (searchPattern
.Length
== 0)
179 throw new IsolatedStorageException ("searchPattern");
180 if (searchPattern
.IndexOfAny (Path
.GetInvalidPathChars ()) != -1)
181 throw new ArgumentException ("searchPattern");
184 public string [] GetDirectoryNames ()
186 return HideAppDirs (Directory
.GetDirectories (basedir
));
189 public string [] GetDirectoryNames (string searchPattern
)
191 CheckSearchPattern (searchPattern
);
193 // note: IsolatedStorageFile accept a "dir/file" pattern which is not allowed by DirectoryInfo
194 // so we need to split them to get the right results
195 string path
= Path
.GetDirectoryName (searchPattern
);
196 string pattern
= Path
.GetFileName (searchPattern
);
197 string [] afi
= null;
199 if (path
== null || path
.Length
== 0) {
200 return HideAppDirs (Directory
.GetDirectories (basedir
, searchPattern
));
202 // we're looking for a single result, identical to path (no pattern here)
203 // we're also looking for something under the current path (not outside isolated storage)
205 string [] subdirs
= Directory
.GetDirectories (basedir
, path
);
206 if (subdirs
.Length
!= 1 || subdirs
[0].IndexOf (basedir
) < 0)
207 throw new IsolatedStorageException ();
209 DirectoryInfo dir
= new DirectoryInfo (subdirs
[0]);
210 if (dir
.Name
!= path
)
211 throw new IsolatedStorageException ();
213 return GetNames (dir
.GetDirectories (pattern
));
217 public string [] GetFileNames ()
219 return HideAppDirs (Directory
.GetFiles (basedir
));
222 public string [] GetFileNames (string searchPattern
)
224 CheckSearchPattern (searchPattern
);
226 // note: IsolatedStorageFile accept a "dir/file" pattern which is not allowed by DirectoryInfo
227 // so we need to split them to get the right results
228 string path
= Path
.GetDirectoryName (searchPattern
);
229 string pattern
= Path
.GetFileName (searchPattern
);
230 string [] afi
= null;
232 if (path
== null || path
.Length
== 0) {
233 return HideAppDirs (Directory
.GetFiles (basedir
, searchPattern
));
235 // we're looking for a single result, identical to path (no pattern here)
236 // we're also looking for something under the current path (not outside isolated storage)
238 string [] subdirs
= Directory
.GetDirectories (basedir
, path
);
239 if (subdirs
.Length
!= 1 || subdirs
[0].IndexOf (basedir
) < 0)
240 throw new IsolatedStorageException ();
242 DirectoryInfo dir
= new DirectoryInfo (subdirs
[0]);
243 if (dir
.Name
!= path
)
244 throw new IsolatedStorageException ();
246 return GetNames (dir
.GetFiles (pattern
));
250 // Return the file name portion of a full path
251 private string[] GetNames (FileSystemInfo
[] afsi
)
253 string[] r
= new string[afsi
.Length
];
254 for (int i
= 0; i
!= afsi
.Length
; ++i
)
259 public IsolatedStorageFileStream
OpenFile (string path
, FileMode mode
)
261 return OpenFile (path
, mode
, FileAccess
.ReadWrite
, FileShare
.None
);
264 public IsolatedStorageFileStream
OpenFile (string path
, FileMode mode
, FileAccess access
)
266 return OpenFile (path
, mode
, access
, FileShare
.None
);
269 public IsolatedStorageFileStream
OpenFile (string path
, FileMode mode
, FileAccess access
, FileShare share
)
272 return new IsolatedStorageFileStream (path
, mode
, access
, share
, this);
275 public void Remove ()
278 IsolatedStorage
.Remove (basedir
);
282 // note: available free space could be changed from another application (same URL, another ML instance) or
283 // another application on the same site
284 public long AvailableFreeSpace
{
287 return IsolatedStorage
.AvailableFreeSpace
;
291 // note: quota could be changed from another application (same URL, another ML instance) or
292 // another application on the same site
296 return IsolatedStorage
.Quota
;
301 [return: MarshalAs (UnmanagedType
.Bool
)]
302 extern static bool isolated_storage_increase_quota_to (string primary_text
, string secondary_text
);
304 const long mb
= 1024 * 1024;
306 public bool IncreaseQuotaTo (long newQuotaSize
)
310 if (newQuotaSize
<= Quota
)
311 throw new ArgumentException ("newQuotaSize", "Only increases are possible");
313 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>.",
314 IsolatedStorage
.Site
, IsolatedStorage
.Current
/ mb
, IsolatedStorage
.Quota
/ mb
);
315 string question
= String
.Format ("Do you want to increase the web site quota to a new maximum of <b>{0:F1} MB</b> ?",
317 bool result
= isolated_storage_increase_quota_to (message
, question
);
319 IsolatedStorage
.Quota
= newQuotaSize
;