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-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
;
35 using System
.Runtime
.InteropServices
;
36 using System
.Security
.AccessControl
;
38 namespace System
.Security
.Permissions
{
42 public sealed class FileIOPermission
43 : CodeAccessPermission
, IBuiltInPermission
, IUnrestrictedPermission
{
45 private const int version
= 1;
47 private static char[] BadPathNameCharacters
;
48 private static char[] BadFileNameCharacters
;
50 static FileIOPermission ()
52 // we keep a local (static) copies to avoid calls/allocations
53 BadPathNameCharacters
= Path
.GetInvalidPathChars ();
54 BadFileNameCharacters
= Path
.GetInvalidFileNameChars ();
57 private bool m_Unrestricted
= false;
58 private FileIOPermissionAccess m_AllFilesAccess
= FileIOPermissionAccess
.NoAccess
;
59 private FileIOPermissionAccess m_AllLocalFilesAccess
= FileIOPermissionAccess
.NoAccess
;
60 private ArrayList readList
;
61 private ArrayList writeList
;
62 private ArrayList appendList
;
63 private ArrayList pathList
;
65 public FileIOPermission (PermissionState state
)
67 if (CheckPermissionState (state
, true) == PermissionState
.Unrestricted
) {
68 m_Unrestricted
= true;
69 m_AllFilesAccess
= FileIOPermissionAccess
.AllAccess
;
70 m_AllLocalFilesAccess
= FileIOPermissionAccess
.AllAccess
;
75 public FileIOPermission (FileIOPermissionAccess access
, string path
)
78 throw new ArgumentNullException ("path");
81 // access and path will be validated in AddPathList
82 AddPathList (access
, path
);
85 public FileIOPermission (FileIOPermissionAccess access
, string[] pathList
)
88 throw new ArgumentNullException ("pathList");
91 // access and path will be validated in AddPathList
92 AddPathList (access
, pathList
);
95 internal void CreateLists ()
97 readList
= new ArrayList ();
98 writeList
= new ArrayList ();
99 appendList
= new ArrayList ();
100 pathList
= new ArrayList ();
103 [MonoTODO ("(2.0) Access Control isn't implemented")]
104 public FileIOPermission (FileIOPermissionAccess access
, AccessControlActions control
, string path
)
106 throw new NotImplementedException ();
109 [MonoTODO ("(2.0) Access Control isn't implemented")]
110 public FileIOPermission (FileIOPermissionAccess access
, AccessControlActions control
, string[] pathList
)
112 throw new NotImplementedException ();
115 internal FileIOPermission (FileIOPermissionAccess access
, string[] pathList
, bool checkForDuplicates
, bool needFullPath
)
119 public FileIOPermissionAccess AllFiles
{
120 get { return m_AllFilesAccess; }
122 // if we are already set to unrestricted, don't change this property
123 if (!m_Unrestricted
){
124 m_AllFilesAccess
= value;
129 public FileIOPermissionAccess AllLocalFiles
{
130 get { return m_AllLocalFilesAccess; }
132 // if we are already set to unrestricted, don't change this property
133 if (!m_Unrestricted
){
134 m_AllLocalFilesAccess
= value;
139 public void AddPathList (FileIOPermissionAccess access
, string path
)
141 if ((FileIOPermissionAccess
.AllAccess
& access
) != access
)
142 ThrowInvalidFlag (access
, true);
143 ThrowIfInvalidPath (path
);
144 AddPathInternal (access
, path
);
147 public void AddPathList (FileIOPermissionAccess access
, string[] pathList
)
149 if ((FileIOPermissionAccess
.AllAccess
& access
) != access
)
150 ThrowInvalidFlag (access
, true);
151 ThrowIfInvalidPath (pathList
);
152 foreach (string path
in pathList
) {
153 AddPathInternal (access
, path
);
157 // internal to avoid duplicate checks
158 internal void AddPathInternal (FileIOPermissionAccess access
, string path
)
160 // call InsecureGetFullPath (and not GetFullPath) to avoid recursion
161 path
= Path
.InsecureGetFullPath (path
);
163 if ((access
& FileIOPermissionAccess
.Read
) == FileIOPermissionAccess
.Read
)
165 if ((access
& FileIOPermissionAccess
.Write
) == FileIOPermissionAccess
.Write
)
166 writeList
.Add (path
);
167 if ((access
& FileIOPermissionAccess
.Append
) == FileIOPermissionAccess
.Append
)
168 appendList
.Add (path
);
169 if ((access
& FileIOPermissionAccess
.PathDiscovery
) == FileIOPermissionAccess
.PathDiscovery
)
173 public override IPermission
Copy ()
176 return new FileIOPermission (PermissionState
.Unrestricted
);
178 FileIOPermission copy
= new FileIOPermission (PermissionState
.None
);
179 copy
.readList
= (ArrayList
) readList
.Clone ();
180 copy
.writeList
= (ArrayList
) writeList
.Clone ();
181 copy
.appendList
= (ArrayList
) appendList
.Clone ();
182 copy
.pathList
= (ArrayList
) pathList
.Clone ();
183 copy
.m_AllFilesAccess
= m_AllFilesAccess
;
184 copy
.m_AllLocalFilesAccess
= m_AllLocalFilesAccess
;
188 public override void FromXml (SecurityElement esd
)
190 // General validation in CodeAccessPermission
191 CheckSecurityElement (esd
, "esd", version
, version
);
192 // Note: we do not (yet) care about the return value
193 // as we only accept version 1 (min/max values)
195 if (IsUnrestricted (esd
)) {
196 m_Unrestricted
= true;
199 m_Unrestricted
= false;
200 string fileList
= esd
.Attribute ("Read");
202 if (fileList
!= null){
203 files
= fileList
.Split (';');
204 AddPathList (FileIOPermissionAccess
.Read
, files
);
206 fileList
= esd
.Attribute ("Write");
207 if (fileList
!= null){
208 files
= fileList
.Split (';');
209 AddPathList (FileIOPermissionAccess
.Write
, files
);
211 fileList
= esd
.Attribute ("Append");
212 if (fileList
!= null){
213 files
= fileList
.Split (';');
214 AddPathList (FileIOPermissionAccess
.Append
, files
);
216 fileList
= esd
.Attribute ("PathDiscovery");
217 if (fileList
!= null){
218 files
= fileList
.Split (';');
219 AddPathList (FileIOPermissionAccess
.PathDiscovery
, files
);
224 public string[] GetPathList (FileIOPermissionAccess access
)
226 if ((FileIOPermissionAccess
.AllAccess
& access
) != access
)
227 ThrowInvalidFlag (access
, true);
229 ArrayList result
= new ArrayList ();
231 case FileIOPermissionAccess
.NoAccess
:
233 case FileIOPermissionAccess
.Read
:
234 result
.AddRange (readList
);
236 case FileIOPermissionAccess
.Write
:
237 result
.AddRange (writeList
);
239 case FileIOPermissionAccess
.Append
:
240 result
.AddRange (appendList
);
242 case FileIOPermissionAccess
.PathDiscovery
:
243 result
.AddRange (pathList
);
246 ThrowInvalidFlag (access
, false);
249 return (result
.Count
> 0) ? (string[]) result
.ToArray (typeof (string)) : null;
252 public override IPermission
Intersect (IPermission target
)
254 FileIOPermission fiop
= Cast (target
);
258 if (IsUnrestricted ())
260 if (fiop
.IsUnrestricted ())
263 FileIOPermission result
= new FileIOPermission (PermissionState
.None
);
264 result
.AllFiles
= m_AllFilesAccess
& fiop
.AllFiles
;
265 result
.AllLocalFiles
= m_AllLocalFilesAccess
& fiop
.AllLocalFiles
;
267 IntersectKeys (readList
, fiop
.readList
, result
.readList
);
268 IntersectKeys (writeList
, fiop
.writeList
, result
.writeList
);
269 IntersectKeys (appendList
, fiop
.appendList
, result
.appendList
);
270 IntersectKeys (pathList
, fiop
.pathList
, result
.pathList
);
272 return (result
.IsEmpty () ? null : result
);
275 public override bool IsSubsetOf (IPermission target
)
277 FileIOPermission fiop
= Cast (target
);
283 if (IsUnrestricted ())
284 return fiop
.IsUnrestricted ();
285 else if (fiop
.IsUnrestricted ())
288 if ((m_AllFilesAccess
& fiop
.AllFiles
) != m_AllFilesAccess
)
290 if ((m_AllLocalFilesAccess
& fiop
.AllLocalFiles
) != m_AllLocalFilesAccess
)
293 if (!KeyIsSubsetOf (appendList
, fiop
.appendList
))
295 if (!KeyIsSubsetOf (readList
, fiop
.readList
))
297 if (!KeyIsSubsetOf (writeList
, fiop
.writeList
))
299 if (!KeyIsSubsetOf (pathList
, fiop
.pathList
))
305 public bool IsUnrestricted ()
307 return m_Unrestricted
;
310 public void SetPathList (FileIOPermissionAccess access
, string path
)
312 if ((FileIOPermissionAccess
.AllAccess
& access
) != access
)
313 ThrowInvalidFlag (access
, true);
314 ThrowIfInvalidPath (path
);
315 // note: throw before clearing the actual list
317 AddPathInternal (access
, path
);
320 public void SetPathList (FileIOPermissionAccess access
, string[] pathList
)
322 if ((FileIOPermissionAccess
.AllAccess
& access
) != access
)
323 ThrowInvalidFlag (access
, true);
324 ThrowIfInvalidPath (pathList
);
325 // note: throw before clearing the actual list
327 foreach (string path
in pathList
)
328 AddPathInternal (access
, path
);
331 public override SecurityElement
ToXml ()
333 SecurityElement se
= Element (1);
334 if (m_Unrestricted
) {
335 se
.AddAttribute ("Unrestricted", "true");
338 string[] paths
= GetPathList (FileIOPermissionAccess
.Append
);
339 if (null != paths
&& paths
.Length
> 0) {
340 se
.AddAttribute ("Append", String
.Join (";", paths
));
342 paths
= GetPathList (FileIOPermissionAccess
.Read
);
343 if (null != paths
&& paths
.Length
> 0) {
344 se
.AddAttribute ("Read", String
.Join (";", paths
));
346 paths
= GetPathList (FileIOPermissionAccess
.Write
);
347 if (null != paths
&& paths
.Length
> 0) {
348 se
.AddAttribute ("Write", String
.Join (";", paths
));
350 paths
= GetPathList (FileIOPermissionAccess
.PathDiscovery
);
351 if (null != paths
&& paths
.Length
> 0) {
352 se
.AddAttribute ("PathDiscovery", String
.Join (";", paths
));
358 public override IPermission
Union (IPermission other
)
360 FileIOPermission fiop
= Cast (other
);
364 if (IsUnrestricted () || fiop
.IsUnrestricted ())
365 return new FileIOPermission (PermissionState
.Unrestricted
);
367 if (IsEmpty () && fiop
.IsEmpty ())
370 FileIOPermission result
= (FileIOPermission
) Copy ();
371 result
.AllFiles
|= fiop
.AllFiles
;
372 result
.AllLocalFiles
|= fiop
.AllLocalFiles
;
374 string[] paths
= fiop
.GetPathList (FileIOPermissionAccess
.Read
);
376 UnionKeys (result
.readList
, paths
);
378 paths
= fiop
.GetPathList (FileIOPermissionAccess
.Write
);
380 UnionKeys (result
.writeList
, paths
);
382 paths
= fiop
.GetPathList (FileIOPermissionAccess
.Append
);
384 UnionKeys (result
.appendList
, paths
);
386 paths
= fiop
.GetPathList (FileIOPermissionAccess
.PathDiscovery
);
388 UnionKeys (result
.pathList
, paths
);
395 public override bool Equals (object obj
)
402 public override int GetHashCode ()
404 return base.GetHashCode ();
407 // IBuiltInPermission
408 int IBuiltInPermission
.GetTokenIndex ()
410 return (int) BuiltInToken
.FileIO
;
415 private bool IsEmpty ()
417 return ((!m_Unrestricted
) && (appendList
.Count
== 0) && (readList
.Count
== 0)
418 && (writeList
.Count
== 0) && (pathList
.Count
== 0));
421 private static FileIOPermission
Cast (IPermission target
)
426 FileIOPermission fiop
= (target
as FileIOPermission
);
428 ThrowInvalidPermission (target
, typeof (FileIOPermission
));
434 internal static void ThrowInvalidFlag (FileIOPermissionAccess access
, bool context
)
438 msg
= Locale
.GetText ("Unknown flag '{0}'.");
440 msg
= Locale
.GetText ("Invalid flag '{0}' in this context.");
441 throw new ArgumentException (String
.Format (msg
, access
), "access");
444 internal static void ThrowIfInvalidPath (string path
)
446 string dir
= Path
.GetDirectoryName (path
);
447 if ((dir
!= null) && (dir
.LastIndexOfAny (BadPathNameCharacters
) >= 0)) {
448 string msg
= String
.Format (Locale
.GetText ("Invalid path characters in path: '{0}'"), path
);
449 throw new ArgumentException (msg
, "path");
452 string fname
= Path
.GetFileName (path
);
453 if ((fname
!= null) && (fname
.LastIndexOfAny (BadFileNameCharacters
) >= 0)) {
454 string msg
= String
.Format (Locale
.GetText ("Invalid filename characters in path: '{0}'"), path
);
455 throw new ArgumentException (msg
, "path");
457 // LAMESPEC: docs don't say it must be a rooted path, but the MS implementation enforces it, so we will too.
458 if (!Path
.IsPathRooted (path
)) {
459 string msg
= Locale
.GetText ("Absolute path information is required.");
460 throw new ArgumentException (msg
, "path");
464 internal static void ThrowIfInvalidPath (string[] paths
)
466 foreach (string path
in paths
)
467 ThrowIfInvalidPath (path
);
470 // we known that access is valid at this point
471 internal void Clear (FileIOPermissionAccess access
)
473 if ((access
& FileIOPermissionAccess
.Read
) == FileIOPermissionAccess
.Read
)
475 if ((access
& FileIOPermissionAccess
.Write
) == FileIOPermissionAccess
.Write
)
477 if ((access
& FileIOPermissionAccess
.Append
) == FileIOPermissionAccess
.Append
)
479 if ((access
& FileIOPermissionAccess
.PathDiscovery
) == FileIOPermissionAccess
.PathDiscovery
)
483 // note: all path in IList are already "full paths"
484 internal static bool KeyIsSubsetOf (IList local
, IList target
)
487 foreach (string l
in local
) {
488 foreach (string t
in target
) {
489 if (Path
.IsPathSubsetOf (t
, l
)) {
500 internal static void UnionKeys (IList list
, string[] paths
)
502 foreach (string path
in paths
) {
503 int len
= list
.Count
;
509 for (i
=0; i
< len
; i
++) {
510 string s
= (string) list
[i
];
511 if (Path
.IsPathSubsetOf (path
, s
)) {
512 // replace (with reduced version)
516 else if (Path
.IsPathSubsetOf (s
, path
)) {
528 internal static void IntersectKeys (IList local
, IList target
, IList result
)
530 foreach (string l
in local
) {
531 foreach (string t
in target
) {
532 if (t
.Length
> l
.Length
) {
533 if (Path
.IsPathSubsetOf (l
,t
))
537 if (Path
.IsPathSubsetOf (t
, l
))