New test.
[mono.git] / mcs / class / corlib / System.Security.Permissions / FileIOPermission.cs
1 // \r
2 // System.Security.Permissions.FileIOPermission.cs \r
3 //\r
4 // Authors:
5 //      Nick Drochak, ndrochak@gol.com\r
6 //      Sebastien Pouliot  <sebastien@ximian.com>
7 //
8 // Copyright (C) 2001 Nick Drochak, All Rights Reserved\r
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 \r
31 using System.Collections;\r
32 using System.IO;\r
33 using System.Text;\r
34
35 #if NET_2_0
36 using System.Runtime.InteropServices;
37 using System.Security.AccessControl;
38 #endif
39 \r
40 namespace System.Security.Permissions {\r
41 \r
42 #if NET_2_0
43         [ComVisible (true)]
44 #endif
45         [Serializable]\r
46         public sealed class FileIOPermission\r
47                 : CodeAccessPermission, IBuiltInPermission, IUnrestrictedPermission {\r
48
49                 private const int version = 1;
50
51 #if NET_2_0
52                 private static char[] BadPathNameCharacters;
53                 private static char[] BadFileNameCharacters;
54
55                 static FileIOPermission ()
56                 {
57                         // we keep a local (static) copies to avoid calls/allocations
58                         BadPathNameCharacters = Path.GetInvalidPathChars ();
59                         BadFileNameCharacters = Path.GetInvalidFileNameChars ();
60                 }
61 #else
62                 private static char[] m_badCharacters;
63
64                 static FileIOPermission ()
65                 {
66                         // note: deprecated in 2.0 as InvalidPathChars is an array (i.e. items can be
67                         // modified). Anyway we keep our own copy, which should be called by the 
68                         // security manager before anyone has the chance to change it.
69                         m_badCharacters = (char[]) Path.InvalidPathChars.Clone ();
70                 }
71 #endif\r
72
73                 private bool m_Unrestricted = false;\r
74                 private FileIOPermissionAccess m_AllFilesAccess = FileIOPermissionAccess.NoAccess;\r
75                 private FileIOPermissionAccess m_AllLocalFilesAccess = FileIOPermissionAccess.NoAccess;\r
76                 private ArrayList readList;
77                 private ArrayList writeList;
78                 private ArrayList appendList;
79                 private ArrayList pathList;
80
81                 public FileIOPermission (PermissionState state)
82                 {\r
83                         if (CheckPermissionState (state, true) == PermissionState.Unrestricted) {
84                                 m_Unrestricted = true;\r
85                                 m_AllFilesAccess = FileIOPermissionAccess.AllAccess;\r
86                                 m_AllLocalFilesAccess = FileIOPermissionAccess.AllAccess;\r
87                         }
88                         CreateLists ();\r
89                 }\r
90 \r
91                 public FileIOPermission (FileIOPermissionAccess access, string path)
92                 {
93                         if (path == null)
94                                 throw new ArgumentNullException ("path");
95
96                         CreateLists ();
97                         // access and path will be validated in AddPathList\r
98                         AddPathList (access, path);\r
99                 }\r
100 \r
101                 public FileIOPermission (FileIOPermissionAccess access, string[] pathList)
102                 {\r
103                         if (pathList == null)
104                                 throw new ArgumentNullException ("pathList");
105
106                         CreateLists ();\r
107                         // access and path will be validated in AddPathList\r
108                         AddPathList (access, pathList);\r
109                 }
110
111                 internal void CreateLists ()
112                 {
113                         readList = new ArrayList ();
114                         writeList = new ArrayList ();
115                         appendList = new ArrayList ();
116                         pathList = new ArrayList ();
117                 }
118
119 #if NET_2_0
120                 [MonoTODO ("(2.0) Access Control isn't implemented")]
121                 public FileIOPermission (FileIOPermissionAccess access, AccessControlActions control, string path)
122                 {
123                         throw new NotImplementedException ();
124                 }\r
125 \r
126                 [MonoTODO ("(2.0) Access Control isn't implemented")]
127                 public FileIOPermission (FileIOPermissionAccess access, AccessControlActions control, string[] pathList)
128                 {\r
129                         throw new NotImplementedException ();
130                 }
131 #endif\r
132 \r
133                 public FileIOPermissionAccess AllFiles {\r
134                         get { return m_AllFilesAccess; } \r
135                         set {\r
136                                 // if we are already set to unrestricted, don't change this property\r
137                                 if (!m_Unrestricted){\r
138                                         m_AllFilesAccess = value;\r
139                                 }\r
140                         }\r
141                 }\r
142 \r
143                 public FileIOPermissionAccess AllLocalFiles {\r
144                         get { return m_AllLocalFilesAccess; } \r
145                         set {\r
146                                 // if we are already set to unrestricted, don't change this property\r
147                                 if (!m_Unrestricted){\r
148                                         m_AllLocalFilesAccess = value;\r
149                                 }\r
150                         }\r
151                 }\r
152 \r
153                 public void AddPathList (FileIOPermissionAccess access, string path)
154                 {
155                         if ((FileIOPermissionAccess.AllAccess & access) != access)
156                                 ThrowInvalidFlag (access, true);
157                         ThrowIfInvalidPath (path);\r
158                         AddPathInternal (access, path);\r
159                 }\r
160 \r
161                 public void AddPathList (FileIOPermissionAccess access, string[] pathList)
162                 {\r
163                         if ((FileIOPermissionAccess.AllAccess & access) != access)
164                                 ThrowInvalidFlag (access, true);
165                         ThrowIfInvalidPath (pathList);\r
166                         foreach (string path in pathList) {
167                                 AddPathInternal (access, path);\r
168                         }\r
169                 }
170
171                 // internal to avoid duplicate checks
172                 internal void AddPathInternal (FileIOPermissionAccess access, string path)
173                 {
174                         // call InsecureGetFullPath (and not GetFullPath) to avoid recursion
175                         path = Path.InsecureGetFullPath (path);
176
177                         if ((access & FileIOPermissionAccess.Read) == FileIOPermissionAccess.Read)
178                                 readList.Add (path);
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)
184                                 pathList.Add (path);
185                 }
186
187                 public override IPermission Copy ()
188                 {\r
189                         if (m_Unrestricted)\r
190                                 return new FileIOPermission (PermissionState.Unrestricted);\r
191
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;
199                         return copy;\r
200                 }\r
201 \r
202                 public override void FromXml (SecurityElement esd)
203                 {\r
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)
208
209                         if (IsUnrestricted (esd)) {\r
210                                 m_Unrestricted = true;\r
211                         }\r
212                         else{\r
213                                 m_Unrestricted = false;\r
214                                 string fileList = esd.Attribute ("Read");\r
215                                 string[] files;\r
216                                 if (fileList != null){\r
217                                         files = fileList.Split (';');\r
218                                         AddPathList (FileIOPermissionAccess.Read, files);\r
219                                 }\r
220                                 fileList = esd.Attribute ("Write");\r
221                                 if (fileList != null){\r
222                                         files = fileList.Split (';');\r
223                                         AddPathList (FileIOPermissionAccess.Write, files);\r
224                                 }\r
225                                 fileList = esd.Attribute ("Append");\r
226                                 if (fileList != null){\r
227                                         files = fileList.Split (';');\r
228                                         AddPathList (FileIOPermissionAccess.Append, files);\r
229                                 }\r
230                                 fileList = esd.Attribute ("PathDiscovery");\r
231                                 if (fileList != null){\r
232                                         files = fileList.Split (';');\r
233                                         AddPathList (FileIOPermissionAccess.PathDiscovery, files);\r
234                                 }\r
235                         }\r
236                 }\r
237 \r
238                 public string[] GetPathList (FileIOPermissionAccess access)
239                 {\r
240                         if ((FileIOPermissionAccess.AllAccess & access) != access)
241                                 ThrowInvalidFlag (access, true);
242
243                         ArrayList result = new ArrayList ();
244                         switch (access) {
245                                 case FileIOPermissionAccess.NoAccess:
246                                         break;
247                                 case FileIOPermissionAccess.Read:
248                                         result.AddRange (readList);
249                                         break;
250                                 case FileIOPermissionAccess.Write:
251                                         result.AddRange (writeList);
252                                         break;
253                                 case FileIOPermissionAccess.Append:
254                                         result.AddRange (appendList);
255                                         break;
256                                 case FileIOPermissionAccess.PathDiscovery:
257                                         result.AddRange (pathList);
258                                         break;
259                                 default:
260                                         ThrowInvalidFlag (access, false);
261                                         break;
262                         }
263                         return (result.Count > 0) ? (string[]) result.ToArray (typeof (string)) : null;\r
264                 }\r
265 \r
266                 public override IPermission Intersect (IPermission target)
267                 {
268                         FileIOPermission fiop = Cast (target);
269                         if (fiop == null)
270                                 return null;
271
272                         if (IsUnrestricted ())
273                                 return fiop.Copy ();
274                         if (fiop.IsUnrestricted ())
275                                 return Copy ();
276
277                         FileIOPermission result = new FileIOPermission (PermissionState.None);
278                         result.AllFiles = m_AllFilesAccess & fiop.AllFiles;
279                         result.AllLocalFiles = m_AllLocalFilesAccess & fiop.AllLocalFiles;
280
281                         IntersectKeys (readList, fiop.readList, result.readList);
282                         IntersectKeys (writeList, fiop.writeList, result.writeList);
283                         IntersectKeys (appendList, fiop.appendList, result.appendList);
284                         IntersectKeys (pathList, fiop.pathList, result.pathList);
285
286                         return (result.IsEmpty () ? null : result);
287                 }\r
288 \r
289                 public override bool IsSubsetOf (IPermission target)
290                 {
291                         FileIOPermission fiop = Cast (target);
292                         if (fiop == null) 
293                                 return false;
294                         if (fiop.IsEmpty ())
295                                 return IsEmpty ();
296
297                         if (IsUnrestricted ())
298                                 return fiop.IsUnrestricted ();
299                         else if (fiop.IsUnrestricted ())
300                                 return true;
301
302                         if ((m_AllFilesAccess & fiop.AllFiles) != m_AllFilesAccess)
303                                 return false;
304                         if ((m_AllLocalFilesAccess & fiop.AllLocalFiles) != m_AllLocalFilesAccess)
305                                 return false;
306
307                         if (!KeyIsSubsetOf (appendList, fiop.appendList))
308                                 return false;
309                         if (!KeyIsSubsetOf (readList, fiop.readList))
310                                 return false;
311                         if (!KeyIsSubsetOf (writeList, fiop.writeList))
312                                 return false;
313                         if (!KeyIsSubsetOf (pathList, fiop.pathList))
314                                 return false;
315
316                         return true;
317                 }\r
318
319                 public bool IsUnrestricted ()
320                 {\r
321                         return m_Unrestricted;\r
322                 }\r
323 \r
324                 public void SetPathList (FileIOPermissionAccess access, string path)
325                 {
326                         if ((FileIOPermissionAccess.AllAccess & access) != access)
327                                 ThrowInvalidFlag (access, true);
328                         ThrowIfInvalidPath (path);\r
329                         // note: throw before clearing the actual list
330                         Clear (access);\r
331                         AddPathInternal (access, path);\r
332                 }\r
333                 \r
334                 public void SetPathList (FileIOPermissionAccess access, string[] pathList)
335                 {\r
336                         if ((FileIOPermissionAccess.AllAccess & access) != access)
337                                 ThrowInvalidFlag (access, true);
338                         ThrowIfInvalidPath (pathList);\r
339                         // note: throw before clearing the actual list
340                         Clear (access);
341                         foreach (string path in pathList)\r
342                                 AddPathInternal (access, path);\r
343                 }
344
345                 public override SecurityElement ToXml ()
346                 {\r
347                         SecurityElement se = Element (1);
348                         if (m_Unrestricted) {\r
349                                 se.AddAttribute ("Unrestricted", "true");\r
350                         }\r
351                         else {\r
352                                 string[] paths = GetPathList (FileIOPermissionAccess.Append);\r
353                                 if (null != paths && paths.Length > 0) {\r
354                                         se.AddAttribute ("Append", String.Join (";", paths));\r
355                                 }\r
356                                 paths = GetPathList (FileIOPermissionAccess.Read);\r
357                                 if (null != paths && paths.Length > 0) {\r
358                                         se.AddAttribute ("Read", String.Join (";", paths));\r
359                                 }\r
360                                 paths = GetPathList (FileIOPermissionAccess.Write);\r
361                                 if (null != paths && paths.Length > 0) {\r
362                                         se.AddAttribute ("Write", String.Join  (";", paths));\r
363                                 }\r
364                                 paths = GetPathList (FileIOPermissionAccess.PathDiscovery);\r
365                                 if (null != paths && paths.Length > 0) {\r
366                                         se.AddAttribute ("PathDiscovery", String.Join  (";", paths));\r
367                                 }\r
368                         }\r
369                         return se;\r
370                 }\r
371 \r
372                 public override IPermission Union (IPermission other)
373                 {\r
374                         FileIOPermission fiop = Cast (other);
375                         if (fiop == null)
376                                 return Copy ();
377
378                         if (IsUnrestricted () || fiop.IsUnrestricted ())
379                                 return new FileIOPermission (PermissionState.Unrestricted);
380
381                         if (IsEmpty () && fiop.IsEmpty ())
382                                 return null;
383
384                         FileIOPermission result = (FileIOPermission) Copy ();
385                         result.AllFiles |= fiop.AllFiles;
386                         result.AllLocalFiles |= fiop.AllLocalFiles;
387
388                         string[] paths = fiop.GetPathList (FileIOPermissionAccess.Read);
389                         if (paths != null) 
390                                 UnionKeys (result.readList, paths);
391
392                         paths = fiop.GetPathList (FileIOPermissionAccess.Write);
393                         if (paths != null)
394                                 UnionKeys (result.writeList, paths);
395
396                         paths = fiop.GetPathList (FileIOPermissionAccess.Append);
397                         if (paths != null) 
398                                 UnionKeys (result.appendList, paths);
399
400                         paths = fiop.GetPathList (FileIOPermissionAccess.PathDiscovery);
401                         if (paths != null) 
402                                 UnionKeys (result.pathList, paths);
403                         
404                         return result;
405                 }\r
406 \r
407 #if NET_2_0
408                 [MonoTODO ("(2.0)")]
409                 [ComVisible (false)]
410                 public override bool Equals (object obj)
411                 {
412                         return false;
413                 }
414
415                 [MonoTODO ("(2.0)")]
416                 [ComVisible (false)]
417                 public override int GetHashCode ()
418                 {
419                         return base.GetHashCode ();
420                 }
421 #endif
422
423                 // IBuiltInPermission\r
424                 int IBuiltInPermission.GetTokenIndex ()\r
425                 {\r
426                         return (int) BuiltInToken.FileIO;
427                 }
428
429                 // helpers
430
431                 private bool IsEmpty ()
432                 {
433                         return ((!m_Unrestricted) && (appendList.Count == 0) && (readList.Count == 0)
434                                 && (writeList.Count == 0) && (pathList.Count == 0));
435                 }
436
437                 private static FileIOPermission Cast (IPermission target)
438                 {
439                         if (target == null)
440                                 return null;
441
442                         FileIOPermission fiop = (target as FileIOPermission);
443                         if (fiop == null) {
444                                 ThrowInvalidPermission (target, typeof (FileIOPermission));
445                         }
446
447                         return fiop;
448                 }
449
450                 internal static void ThrowInvalidFlag (FileIOPermissionAccess access, bool context) 
451                 {
452                         string msg = null;
453                         if (context)
454                                 msg = Locale.GetText ("Unknown flag '{0}'.");
455                         else
456                                 msg = Locale.GetText ("Invalid flag '{0}' in this context.");
457                         throw new ArgumentException (String.Format (msg, access), "access");
458                 }
459
460                 internal static void ThrowIfInvalidPath (string path)
461                 {
462 #if NET_2_0
463                         string dir = Path.GetDirectoryName (path);
464                         if ((dir != null) && (dir.LastIndexOfAny (BadPathNameCharacters) >= 0)) {
465                                 string msg = String.Format (Locale.GetText ("Invalid path characters in path: '{0}'"), path);\r
466                                 throw new ArgumentException (msg, "path");\r
467                         }
468 \r
469                         string fname = Path.GetFileName (path);
470                         if ((fname != null) && (fname.LastIndexOfAny (BadFileNameCharacters) >= 0)) {
471                                 string msg = String.Format (Locale.GetText ("Invalid filename characters in path: '{0}'"), path);\r
472                                 throw new ArgumentException (msg, "path");\r
473                         }
474 #else
475                         if (path.LastIndexOfAny (m_badCharacters) >= 0) {\r
476                                 string msg = String.Format (Locale.GetText ("Invalid characters in path: '{0}'"), path);\r
477                                 throw new ArgumentException (msg, "path");\r
478                         }
479 #endif\r
480                         // LAMESPEC: docs don't say it must be a rooted path, but the MS implementation enforces it, so we will too.\r
481                         if (!Path.IsPathRooted (path)) {
482                                 string msg = Locale.GetText ("Absolute path information is required.");
483                                 throw new ArgumentException (msg, "path");\r
484                         }
485                 }
486 \r
487                 internal static void ThrowIfInvalidPath (string[] paths)
488                 {
489                         foreach (string path in paths)
490                                 ThrowIfInvalidPath (path);
491                 }
492
493                 // we known that access is valid at this point
494                 internal void Clear (FileIOPermissionAccess access)
495                 {
496                         if ((access & FileIOPermissionAccess.Read) == FileIOPermissionAccess.Read)
497                                 readList.Clear ();
498                         if ((access & FileIOPermissionAccess.Write) == FileIOPermissionAccess.Write)
499                                 writeList.Clear ();
500                         if ((access & FileIOPermissionAccess.Append) == FileIOPermissionAccess.Append)
501                                 appendList.Clear ();
502                         if ((access & FileIOPermissionAccess.PathDiscovery) == FileIOPermissionAccess.PathDiscovery)
503                                 pathList.Clear ();
504                 }\r
505
506                 // note: all path in IList are already "full paths"
507                 internal static bool KeyIsSubsetOf (IList local, IList target)
508                 {
509                         bool result = false;
510                         foreach (string l in local) {
511                                 foreach (string t in target) {
512                                         if (Path.IsPathSubsetOf (t, l)) {
513                                                 result = true;
514                                                 break;
515                                         }
516                                 }
517                                 if (!result)
518                                         return false;
519                         }
520                         return true;
521                 }
522
523                 internal static void UnionKeys (IList list, string[] paths)
524                 {
525                         foreach (string path in paths) {
526                                 int len = list.Count;
527                                 if (len == 0) {
528                                         list.Add (path);
529                                 }
530                                 else {
531                                         int i;
532                                         for (i=0; i < len; i++) {
533                                                 string s = (string) list [i];
534                                                 if (Path.IsPathSubsetOf (path, s)) {
535                                                         // replace (with reduced version)
536                                                         list [i] = path;
537                                                         break;
538                                                 }
539                                                 else if (Path.IsPathSubsetOf (s, path)) {
540                                                         // no need to add
541                                                         break;
542                                                 }
543                                         }
544                                         if (i == len) {
545                                                 list.Add (path);
546                                         }
547                                 }
548                         }
549                 }
550
551                 internal static void IntersectKeys (IList local, IList target, IList result)
552                 {
553                         foreach (string l in local) {
554                                 foreach (string t in target) {
555                                         if (t.Length > l.Length) {
556                                                 if (Path.IsPathSubsetOf (l ,t))
557                                                         result.Add (t);
558                                         }
559                                         else {
560                                                 if (Path.IsPathSubsetOf (t, l))
561                                                         result.Add (l);
562                                         }
563                                 }
564                         }
565                 }
566         }\r
567 }\r