2 // System.Security.Permissions.FileIOPermission.cs
5 // Nick Drochak, ndrochak@gol.com
6 // Sebastien Pouliot <sebastien@ximian.com>
8 // Copyright (C) 2001 Nick Drochak, All Rights Reserved
9 // Copyright (C) 2004 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
;
36 using System
.Security
.AccessControl
;
39 namespace System
.Security
.Permissions
{
42 public sealed class FileIOPermission
43 : CodeAccessPermission
, IBuiltInPermission
, IUnrestrictedPermission
{
45 private const int version
= 1;
46 private static char[] m_badCharacters
= {'\"','<', '>', '|', '*', '?'}
;
48 private bool m_Unrestricted
= false;
49 private FileIOPermissionAccess m_AllFilesAccess
= FileIOPermissionAccess
.NoAccess
;
50 private FileIOPermissionAccess m_AllLocalFilesAccess
= FileIOPermissionAccess
.NoAccess
;
51 private ArrayList readList
;
52 private ArrayList writeList
;
53 private ArrayList appendList
;
54 private ArrayList pathList
;
56 public FileIOPermission (PermissionState state
)
58 if (CheckPermissionState (state
, true) == PermissionState
.Unrestricted
) {
59 m_Unrestricted
= true;
60 m_AllFilesAccess
= FileIOPermissionAccess
.AllAccess
;
61 m_AllLocalFilesAccess
= FileIOPermissionAccess
.AllAccess
;
66 public FileIOPermission (FileIOPermissionAccess access
, string path
)
69 throw new ArgumentNullException ("path");
72 // access and path will be validated in AddPathList
73 AddPathList (access
, path
);
76 public FileIOPermission (FileIOPermissionAccess access
, string[] pathList
)
79 throw new ArgumentNullException ("pathList");
82 // access and path will be validated in AddPathList
83 AddPathList (access
, pathList
);
86 internal void CreateLists ()
88 readList
= new ArrayList ();
89 writeList
= new ArrayList ();
90 appendList
= new ArrayList ();
91 pathList
= new ArrayList ();
95 [MonoTODO ("Access Control isn't implemented")]
96 public FileIOPermission (FileIOPermissionAccess access
, AccessControlActions control
, string path
)
98 throw new NotImplementedException ();
101 [MonoTODO ("Access Control isn't implemented")]
102 public FileIOPermission (FileIOPermissionAccess access
, AccessControlActions control
, string[] pathList
)
104 throw new NotImplementedException ();
108 public FileIOPermissionAccess AllFiles
{
109 get { return m_AllFilesAccess; }
111 // if we are already set to unrestricted, don't change this property
112 if (!m_Unrestricted
){
113 m_AllFilesAccess
= value;
118 public FileIOPermissionAccess AllLocalFiles
{
119 get { return m_AllLocalFilesAccess; }
121 // if we are already set to unrestricted, don't change this property
122 if (!m_Unrestricted
){
123 m_AllLocalFilesAccess
= value;
128 public void AddPathList (FileIOPermissionAccess access
, string path
)
130 if ((FileIOPermissionAccess
.AllAccess
& access
) != access
)
131 ThrowInvalidFlag (access
, true);
132 ThrowIfInvalidPath (path
);
133 AddPathInternal (access
, path
);
136 public void AddPathList (FileIOPermissionAccess access
, string[] pathList
)
138 if ((FileIOPermissionAccess
.AllAccess
& access
) != access
)
139 ThrowInvalidFlag (access
, true);
140 ThrowIfInvalidPath (pathList
);
141 foreach (string path
in pathList
) {
142 AddPathInternal (access
, path
);
146 // internal to avoid duplicate checks
147 internal void AddPathInternal (FileIOPermissionAccess access
, string path
)
149 path
= Path
.GetFullPath (path
);
151 case FileIOPermissionAccess.AllAccess:
153 writeList.Add (path);
154 appendList.Add (path);
157 case FileIOPermissionAccess.NoAccess:
158 // ??? unit tests doesn't show removal using NoAccess ???
160 case FileIOPermissionAccess.Read:
163 case FileIOPermissionAccess.Write:
164 writeList.Add (path);
166 case FileIOPermissionAccess.Append:
167 appendList.Add (path);
169 case FileIOPermissionAccess.PathDiscovery:
173 ThrowInvalidFlag (access, true);
177 if ((access
& FileIOPermissionAccess
.Read
) == FileIOPermissionAccess
.Read
)
179 if ((access
& FileIOPermissionAccess
.Write
) == FileIOPermissionAccess
.Write
)
180 writeList
.Add (path
);
181 if ((access
& FileIOPermissionAccess
.Append
) == FileIOPermissionAccess
.Append
)
182 appendList
.Add (path
);
183 if ((access
& FileIOPermissionAccess
.PathDiscovery
) == FileIOPermissionAccess
.PathDiscovery
)
187 public override IPermission
Copy ()
190 return new FileIOPermission (PermissionState
.Unrestricted
);
192 FileIOPermission copy
= new FileIOPermission (PermissionState
.None
);
193 copy
.readList
= (ArrayList
) readList
.Clone ();
194 copy
.writeList
= (ArrayList
) writeList
.Clone ();
195 copy
.appendList
= (ArrayList
) appendList
.Clone ();
196 copy
.pathList
= (ArrayList
) pathList
.Clone ();
197 copy
.m_AllFilesAccess
= m_AllFilesAccess
;
198 copy
.m_AllLocalFilesAccess
= m_AllLocalFilesAccess
;
202 public override void FromXml (SecurityElement esd
)
204 // General validation in CodeAccessPermission
205 CheckSecurityElement (esd
, "esd", version
, version
);
206 // Note: we do not (yet) care about the return value
207 // as we only accept version 1 (min/max values)
209 if (IsUnrestricted (esd
)) {
210 m_Unrestricted
= true;
213 m_Unrestricted
= false;
214 string fileList
= esd
.Attribute ("Read");
216 if (fileList
!= null){
217 files
= fileList
.Split (';');
218 AddPathList (FileIOPermissionAccess
.Read
, files
);
220 fileList
= esd
.Attribute ("Write");
221 if (fileList
!= null){
222 files
= fileList
.Split (';');
223 AddPathList (FileIOPermissionAccess
.Write
, files
);
225 fileList
= esd
.Attribute ("Append");
226 if (fileList
!= null){
227 files
= fileList
.Split (';');
228 AddPathList (FileIOPermissionAccess
.Append
, files
);
230 fileList
= esd
.Attribute ("PathDiscovery");
231 if (fileList
!= null){
232 files
= fileList
.Split (';');
233 AddPathList (FileIOPermissionAccess
.PathDiscovery
, files
);
238 public string[] GetPathList (FileIOPermissionAccess access
)
240 if ((FileIOPermissionAccess
.AllAccess
& access
) != access
)
241 ThrowInvalidFlag (access
, true);
243 //LAMESPEC: docs says it returns (semicolon separated) list, but return
244 //type is array. I think docs are wrong and it just returns an array
245 ArrayList result
= new ArrayList ();
247 case FileIOPermissionAccess
.NoAccess
:
249 case FileIOPermissionAccess
.Read
:
250 result
.AddRange (readList
);
252 case FileIOPermissionAccess
.Write
:
253 result
.AddRange (writeList
);
255 case FileIOPermissionAccess
.Append
:
256 result
.AddRange (appendList
);
258 case FileIOPermissionAccess
.PathDiscovery
:
259 result
.AddRange (pathList
);
262 ThrowInvalidFlag (access
, false);
265 return (result
.Count
> 0) ? (string[]) result
.ToArray (typeof (string)) : null;
268 public override IPermission
Intersect (IPermission target
)
270 FileIOPermission fiop
= Cast (target
);
274 if (IsUnrestricted ())
276 if (fiop
.IsUnrestricted ())
279 FileIOPermission result
= new FileIOPermission (PermissionState
.None
);
280 result
.AllFiles
= m_AllFilesAccess
& fiop
.AllFiles
;
281 result
.AllLocalFiles
= m_AllLocalFilesAccess
& fiop
.AllLocalFiles
;
283 IntersectKeys (readList
, fiop
.readList
, result
.readList
);
284 IntersectKeys (writeList
, fiop
.writeList
, result
.writeList
);
285 IntersectKeys (appendList
, fiop
.appendList
, result
.appendList
);
286 IntersectKeys (pathList
, fiop
.pathList
, result
.pathList
);
288 return (result
.IsEmpty () ? null : result
);
292 public override bool IsSubsetOf (IPermission target
)
294 FileIOPermission fiop
= Cast (target
);
300 if (IsUnrestricted ())
301 return fiop
.IsUnrestricted ();
302 else if (fiop
.IsUnrestricted ())
305 if ((m_AllFilesAccess
& fiop
.AllFiles
) != m_AllFilesAccess
)
307 if ((m_AllLocalFilesAccess
& fiop
.AllLocalFiles
) != m_AllLocalFilesAccess
)
310 if (!KeyIsSubsetOf (appendList
, fiop
.appendList
))
312 if (!KeyIsSubsetOf (readList
, fiop
.readList
))
314 if (!KeyIsSubsetOf (writeList
, fiop
.writeList
))
316 if (!KeyIsSubsetOf (pathList
, fiop
.pathList
))
322 public bool IsUnrestricted ()
324 return m_Unrestricted
;
327 public void SetPathList (FileIOPermissionAccess access
, string path
)
329 if ((FileIOPermissionAccess
.AllAccess
& access
) != access
)
330 ThrowInvalidFlag (access
, true);
331 ThrowIfInvalidPath (path
);
332 // note: throw before clearing the actual list
334 AddPathInternal (access
, path
);
337 public void SetPathList (FileIOPermissionAccess access
, string[] pathList
)
339 if ((FileIOPermissionAccess
.AllAccess
& access
) != access
)
340 ThrowInvalidFlag (access
, true);
341 ThrowIfInvalidPath (pathList
);
342 // note: throw before clearing the actual list
344 foreach (string path
in pathList
)
345 AddPathInternal (access
, path
);
348 public override SecurityElement
ToXml ()
350 SecurityElement se
= Element (1);
351 if (m_Unrestricted
) {
352 se
.AddAttribute ("Unrestricted", "true");
355 string[] paths
= GetPathList (FileIOPermissionAccess
.Append
);
356 if (null != paths
&& paths
.Length
> 0) {
357 se
.AddAttribute ("Append", String
.Join (";", paths
));
359 paths
= GetPathList (FileIOPermissionAccess
.Read
);
360 if (null != paths
&& paths
.Length
> 0) {
361 se
.AddAttribute ("Read", String
.Join (";", paths
));
363 paths
= GetPathList (FileIOPermissionAccess
.Write
);
364 if (null != paths
&& paths
.Length
> 0) {
365 se
.AddAttribute ("Write", String
.Join (";", paths
));
367 paths
= GetPathList (FileIOPermissionAccess
.PathDiscovery
);
368 if (null != paths
&& paths
.Length
> 0) {
369 se
.AddAttribute ("PathDiscovery", String
.Join (";", paths
));
375 public override IPermission
Union (IPermission other
)
377 FileIOPermission fiop
= Cast (other
);
381 if (IsUnrestricted () || fiop
.IsUnrestricted ())
382 return new FileIOPermission (PermissionState
.Unrestricted
);
384 if (IsEmpty () && fiop
.IsEmpty ())
387 FileIOPermission result
= (FileIOPermission
) Copy ();
388 result
.AllFiles
|= fiop
.AllFiles
;
389 result
.AllLocalFiles
|= fiop
.AllLocalFiles
;
391 string[] paths
= fiop
.GetPathList (FileIOPermissionAccess
.Read
);
393 UnionKeys (result
.readList
, paths
);
395 paths
= fiop
.GetPathList (FileIOPermissionAccess
.Write
);
397 UnionKeys (result
.writeList
, paths
);
399 paths
= fiop
.GetPathList (FileIOPermissionAccess
.Append
);
401 UnionKeys (result
.appendList
, paths
);
403 paths
= fiop
.GetPathList (FileIOPermissionAccess
.PathDiscovery
);
405 UnionKeys (result
.pathList
, paths
);
412 public override bool Equals (object obj
)
418 public override int GetHashCode ()
420 return base.GetHashCode ();
424 // IBuiltInPermission
425 int IBuiltInPermission
.GetTokenIndex ()
427 return (int) BuiltInToken
.FileIO
;
432 private bool IsEmpty ()
434 return ((!m_Unrestricted
) && (appendList
.Count
== 0) && (readList
.Count
== 0)
435 && (writeList
.Count
== 0) && (pathList
.Count
== 0));
438 private FileIOPermission
Cast (IPermission target
)
443 FileIOPermission fiop
= (target
as FileIOPermission
);
445 ThrowInvalidPermission (target
, typeof (FileIOPermission
));
451 internal void ThrowInvalidFlag (FileIOPermissionAccess access
, bool context
)
455 msg
= Locale
.GetText ("Unknown flag '{0}'.");
457 msg
= Locale
.GetText ("Invalid flag '{0}' in this context.");
458 throw new ArgumentException (String
.Format (msg
, access
), "access");
461 internal void ThrowIfInvalidPath (string path
)
463 if (path
.LastIndexOfAny (m_badCharacters
) >= 0) {
464 string msg
= String
.Format (Locale
.GetText ("Invalid characters in path: '{0}'"), path
);
465 throw new ArgumentException (msg
, "path");
467 // LAMESPEC: docs don't say it must be a rooted path, but the MS implementation enforces it, so we will too.
468 if (!Path
.IsPathRooted (path
)) {
469 string msg
= Locale
.GetText ("Absolute path information is required.");
470 throw new ArgumentException (msg
, "path");
474 internal void ThrowIfInvalidPath (string[] paths
)
476 foreach (string path
in paths
)
477 ThrowIfInvalidPath (path
);
480 // we known that access is valid at this point
481 internal void Clear (FileIOPermissionAccess access
)
483 if ((access
& FileIOPermissionAccess
.Read
) == FileIOPermissionAccess
.Read
)
485 if ((access
& FileIOPermissionAccess
.Write
) == FileIOPermissionAccess
.Write
)
487 if ((access
& FileIOPermissionAccess
.Append
) == FileIOPermissionAccess
.Append
)
489 if ((access
& FileIOPermissionAccess
.PathDiscovery
) == FileIOPermissionAccess
.PathDiscovery
)
493 internal bool KeyIsSubsetOf (IList local
, IList target
)
496 foreach (string l
in local
) {
497 string c14nl
= Path
.GetFullPath (l
);
498 foreach (string t
in target
) {
499 if (c14nl
.StartsWith (Path
.GetFullPath (t
))) {
510 internal void UnionKeys (IList list
, string[] paths
)
512 foreach (string p
in paths
) {
514 string path
= Path
.GetFullPath (p
);
515 int len
= list
.Count
;
520 for (int i
=0; i
< len
; i
++) {
522 string s
= Path
.GetFullPath ((string) list
[i
]);
523 if (s
.StartsWith (path
)) {
524 // replace (with reduced version)
528 else if (path
.StartsWith (s
)) {
541 internal void IntersectKeys (IList local
, IList target
, IList result
)
543 foreach (string l
in local
) {
544 foreach (string t
in target
) {
545 if (t
.Length
> l
.Length
) {
546 if (t
.StartsWith (l
))
550 if (l
.StartsWith (t
))