svn path=/branches/mono-1-1-9/mcs/; revision=51216
[mono.git] / mcs / class / corlib / System.Security.Permissions / FileIOPermission.cs
index 8113954f2030a3c8d81d195623f6c4526bde8cf4..791b8eb03d47d6cee340d3ab1a93ab4ff9b22a6e 100644 (file)
@@ -1,16 +1,12 @@
-//------------------------------------------------------------------------------\r
 // \r
 // System.Security.Permissions.FileIOPermission.cs \r
 //\r
-// Copyright (C) 2001 Nick Drochak, All Rights Reserved\r
-// \r
-// Author:         Nick Drochak, ndrochak@gol.com\r
-// Created:        2002-01-09 \r
-//\r
-//------------------------------------------------------------------------------\r
-
+// Authors:
+//     Nick Drochak, ndrochak@gol.com\r
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2001 Nick Drochak, All Rights Reserved\r
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 using System.Collections;\r
 using System.IO;\r
 using System.Text;\r
+
+#if NET_2_0
+using System.Runtime.InteropServices;
+using System.Security.AccessControl;
+#endif
 \r
 namespace System.Security.Permissions {\r
 \r
+#if NET_2_0
+       [ComVisible (true)]
+#endif
        [Serializable]\r
        public sealed class FileIOPermission\r
                 : CodeAccessPermission, IBuiltInPermission, IUnrestrictedPermission {\r
-\r
+
+               private const int version = 1;
                private static char[] m_badCharacters = {'\"','<', '>', '|', '*', '?'};\r
+
                private bool m_Unrestricted = false;\r
-               private Hashtable m_PathList = new Hashtable();\r
                private FileIOPermissionAccess m_AllFilesAccess = FileIOPermissionAccess.NoAccess;\r
                private FileIOPermissionAccess m_AllLocalFilesAccess = FileIOPermissionAccess.NoAccess;\r
-\r
-               public FileIOPermission(PermissionState state) {\r
-                       if (!Enum.IsDefined(typeof(PermissionState), state)){\r
-                               throw new ArgumentException("Invalid permission state.", "state");\r
-                       }\r
-                       m_Unrestricted = (PermissionState.Unrestricted == state);\r
-                       if (m_Unrestricted) {\r
+               private ArrayList readList;
+               private ArrayList writeList;
+               private ArrayList appendList;
+               private ArrayList pathList;
+
+               public FileIOPermission (PermissionState state)
+               {\r
+                       if (CheckPermissionState (state, true) == PermissionState.Unrestricted) {
+                               m_Unrestricted = true;\r
                                m_AllFilesAccess = FileIOPermissionAccess.AllAccess;\r
                                m_AllLocalFilesAccess = FileIOPermissionAccess.AllAccess;\r
-                       }\r
+                       }
+                       CreateLists ();\r
                }\r
 \r
-               public FileIOPermission(FileIOPermissionAccess access, string path)
+               public FileIOPermission (FileIOPermissionAccess access, string path)
                {
                        if (path == null)
                                throw new ArgumentNullException ("path");
-\r
-                       if ((FileIOPermissionAccess.AllAccess & access) != access){\r
-                               throw new ArgumentException("Illegal enum value: "+access.ToString()+".");\r
-                       }\r
-\r
-                       if (path.LastIndexOfAny(m_badCharacters) >= 0){\r
-                               throw new ArgumentException("Illegal characters found in input.  Security checks can not contain wild card characters.", "path");\r
-                       }\r
-                       \r
-                       AddPathList(access, path);\r
+
+                       CreateLists ();
+                       // access and path will be validated in AddPathList\r
+                       AddPathList (access, path);\r
                }\r
 \r
-               public FileIOPermission(FileIOPermissionAccess access, string[] pathList)
+               public FileIOPermission (FileIOPermissionAccess access, string[] pathList)
                {\r
                        if (pathList == null)
                                throw new ArgumentNullException ("pathList");
 
-                       if ((FileIOPermissionAccess.AllAccess & access) != access){\r
-                               throw new ArgumentException("Illegal enum value: "+access.ToString()+".");\r
-                       }\r
-\r
-                       AddPathList(access, pathList);\r
+                       CreateLists ();\r
+                       // access and path will be validated in AddPathList\r
+                       AddPathList (access, pathList);\r
+               }
+
+               internal void CreateLists ()
+               {
+                       readList = new ArrayList ();
+                       writeList = new ArrayList ();
+                       appendList = new ArrayList ();
+                       pathList = new ArrayList ();
+               }
+
+#if NET_2_0
+               [MonoTODO ("(2.0) Access Control isn't implemented")]
+               public FileIOPermission (FileIOPermissionAccess access, AccessControlActions control, string path)
+               {
+                       throw new NotImplementedException ();
                }\r
 \r
+               [MonoTODO ("(2.0) Access Control isn't implemented")]
+               public FileIOPermission (FileIOPermissionAccess access, AccessControlActions control, string[] pathList)
+               {\r
+                       throw new NotImplementedException ();
+               }
+#endif\r
+\r
                public FileIOPermissionAccess AllFiles {\r
-                       get {\r
-                               return m_AllFilesAccess;\r
-                       } \r
+                       get { return m_AllFilesAccess; } \r
                        set {\r
                                // if we are already set to unrestricted, don't change this property\r
                                if (!m_Unrestricted){\r
@@ -100,9 +120,7 @@ namespace System.Security.Permissions {
                }\r
 \r
                public FileIOPermissionAccess AllLocalFiles {\r
-                       get {\r
-                               return m_AllLocalFilesAccess;\r
-                       } \r
+                       get { return m_AllLocalFilesAccess; } \r
                        set {\r
                                // if we are already set to unrestricted, don't change this property\r
                                if (!m_Unrestricted){\r
@@ -111,337 +129,404 @@ namespace System.Security.Permissions {
                        }\r
                }\r
 \r
-               public void AddPathList(FileIOPermissionAccess access, string path){\r
-                       if ((FileIOPermissionAccess.AllAccess & access) != access){\r
-                               throw new ArgumentException("Illegal enum value: {0}.",access.ToString());\r
-                       }\r
-\r
-                       if (path.LastIndexOfAny(m_badCharacters) >= 0){\r
-                               throw new ArgumentException("Invalid characters in path: '{0}'", path);\r
-                       }\r
-\r
-                       // LAMESPEC: docs don't say it must be a rooted path, but the MS implementation enforces it, so we will too.\r
-                       if(!Path.IsPathRooted(path)) {\r
-                               throw new ArgumentException("Absolute path information is required.");\r
-                       }\r
-\r
-                       // don't add the same path twice, instead overwrite access entry for that path\r
-                       if (m_PathList.ContainsKey(path)) {\r
-                               FileIOPermissionAccess currentPermission = (FileIOPermissionAccess)m_PathList[path];\r
-                               currentPermission |= access;\r
-                               m_PathList[path] = currentPermission;\r
-                       }\r
-                       else {\r
-                               m_PathList.Add(path, access);\r
-                       }\r
-               }\r
-\r
-               public void AddPathList(FileIOPermissionAccess access, string[] pathList        ){\r
-                       foreach(string path in pathList){\r
-                               AddPathList(access, path);\r
-                       }\r
-                       \r
-               }\r
-\r
-               // private constructor used by Copy() method\r
-               private FileIOPermission(Hashtable pathList, FileIOPermissionAccess allFiles, \r
-                                               FileIOPermissionAccess allLocalFiles, bool unrestricted){\r
-                       m_PathList = pathList;\r
-                       m_AllFilesAccess = allFiles;\r
-                       m_AllLocalFilesAccess = allLocalFiles;\r
-                       m_Unrestricted = unrestricted;\r
+               public void AddPathList (FileIOPermissionAccess access, string path)
+               {
+                        if ((FileIOPermissionAccess.AllAccess & access) != access)
+                               ThrowInvalidFlag (access, true);
+                       ThrowIfInvalidPath (path);\r
+                       AddPathInternal (access, path);\r
                }\r
 \r
-               public override IPermission Copy(){\r
-                       if (m_Unrestricted) {\r
-                               return new FileIOPermission(PermissionState.Unrestricted);\r
-                       }\r
-                       else{\r
-                               FileIOPermission retVal = new FileIOPermission(m_PathList, m_AllFilesAccess, m_AllLocalFilesAccess, m_Unrestricted);\r
-                               return retVal;\r
+               public void AddPathList (FileIOPermissionAccess access, string[] pathList)
+               {\r
+                        if ((FileIOPermissionAccess.AllAccess & access) != access)
+                               ThrowInvalidFlag (access, true);
+                       ThrowIfInvalidPath (pathList);\r
+                       foreach (string path in pathList) {
+                               AddPathInternal (access, path);\r
                        }\r
+               }
+
+               // internal to avoid duplicate checks
+               internal void AddPathInternal (FileIOPermissionAccess access, string path)
+               {
+                       // call InsecureGetFullPath (and not GetFullPath) to avoid recursion
+                       path = Path.InsecureGetFullPath (path);
+
+                       if ((access & FileIOPermissionAccess.Read) == FileIOPermissionAccess.Read)
+                               readList.Add (path);
+                       if ((access & FileIOPermissionAccess.Write) == FileIOPermissionAccess.Write)
+                               writeList.Add (path);
+                       if ((access & FileIOPermissionAccess.Append) == FileIOPermissionAccess.Append)
+                               appendList.Add (path);
+                       if ((access & FileIOPermissionAccess.PathDiscovery) == FileIOPermissionAccess.PathDiscovery)
+                               pathList.Add (path);
+               }
+
+               public override IPermission Copy ()
+               {\r
+                       if (m_Unrestricted)\r
+                               return new FileIOPermission (PermissionState.Unrestricted);\r
+
+                       FileIOPermission copy = new FileIOPermission (PermissionState.None);
+                       copy.readList = (ArrayList) readList.Clone ();
+                       copy.writeList = (ArrayList) writeList.Clone ();
+                       copy.appendList = (ArrayList) appendList.Clone ();
+                       copy.pathList = (ArrayList) pathList.Clone ();
+                       copy.m_AllFilesAccess = m_AllFilesAccess;
+                       copy.m_AllLocalFilesAccess = m_AllLocalFilesAccess;
+                       return copy;\r
                }\r
 \r
-               /*  XML Schema for FileIOPermission\r
-                               <IPermission class=\94FileIOPermission\94 \r
-                                                                version=\941\94\r
-                                                                (\r
-                                                                Read=\94[list of files or folders]\94 | \r
-                                                                Write=\94[list of files or folders]\94 |\r
-                                                                Append=\94[list of files or folders]\94   \r
-                                                                ) v Unrestricted=\94true\94 \r
-                                                                />\r
-               */\r
-               public override void FromXml(SecurityElement esd){\r
-                       if (null == esd) {\r
-                               throw new ArgumentNullException();\r
-                       }\r
-                       if (esd.Tag != "IPermission" || (string)esd.Attributes["class"] != "FileIOPermission"\r
-                                       || (string)esd.Attributes["version"] != "1"){\r
-                               throw new ArgumentException("Not a valid permission element");\r
-                       }\r
-                       m_PathList.Clear();\r
-                       if ("true" == (string)esd.Attributes["Unrestricted"]){\r
+               public override void FromXml (SecurityElement esd)
+               {\r
+                       // General validation in CodeAccessPermission
+                       CheckSecurityElement (esd, "esd", version, version);
+                       // Note: we do not (yet) care about the return value 
+                       // as we only accept version 1 (min/max values)
+
+                       if (IsUnrestricted (esd)) {\r
                                m_Unrestricted = true;\r
                        }\r
                        else{\r
                                m_Unrestricted = false;\r
-                               string fileList;\r
-                               fileList = (string)esd.Attributes["Read"];\r
+                               string fileList = esd.Attribute ("Read");\r
                                string[] files;\r
                                if (fileList != null){\r
-                                       files = fileList.Split(';');\r
-                                       AddPathList(FileIOPermissionAccess.Read, files);\r
+                                       files = fileList.Split (';');\r
+                                       AddPathList (FileIOPermissionAccess.Read, files);\r
                                }\r
-                               fileList = (string)esd.Attributes["Write"];\r
+                               fileList = esd.Attribute ("Write");\r
                                if (fileList != null){\r
-                                       files = fileList.Split(';');\r
-                                       AddPathList(FileIOPermissionAccess.Write, files);\r
+                                       files = fileList.Split (';');\r
+                                       AddPathList (FileIOPermissionAccess.Write, files);\r
                                }\r
-                               fileList = (string)esd.Attributes["Append"];\r
+                               fileList = esd.Attribute ("Append");\r
                                if (fileList != null){\r
-                                       files = fileList.Split(';');\r
-                                       AddPathList(FileIOPermissionAccess.Append, files);\r
+                                       files = fileList.Split (';');\r
+                                       AddPathList (FileIOPermissionAccess.Append, files);\r
                                }\r
-                       }\r
-               }\r
-\r
-               public string[] GetPathList(FileIOPermissionAccess access){\r
-                       //LAMESPEC: docs says it returns (semicolon separated) list, but return\r
-                       //type is array.  I think docs are wrong and it just returns an array\r
-                       if ((FileIOPermissionAccess.AllAccess & access) != access){\r
-                               throw new ArgumentException("Illegal enum value: "+access.ToString()+".");\r
-                       }\r
-\r
-                       ArrayList matchingPaths = new ArrayList();\r
-                       System.Collections.IDictionaryEnumerator pathListIterator = m_PathList.GetEnumerator();\r
-                       while (pathListIterator.MoveNext()) {\r
-                               if (((FileIOPermissionAccess)pathListIterator.Value & access) != 0) {\r
-                                       matchingPaths.Add((string)pathListIterator.Key);\r
+                               fileList = esd.Attribute ("PathDiscovery");\r
+                               if (fileList != null){\r
+                                       files = fileList.Split (';');\r
+                                       AddPathList (FileIOPermissionAccess.PathDiscovery, files);\r
                                }\r
                        }\r
-                       if (matchingPaths.Count == 0) {\r
-                               return null;\r
-                       }\r
-                       else {\r
-                               return (string[])matchingPaths.ToArray(typeof(string));\r
-                       }\r
                }\r
 \r
-               public override IPermission Intersect(IPermission target){ \r
-                       if (null == target){\r
-                               return null;\r
-                       }\r
-                       else {\r
-                               if (target.GetType() != typeof(FileIOPermission)){\r
-                                       throw new ArgumentException();\r
-                               }\r
-                       }\r
-                       FileIOPermission FIOPTarget = (FileIOPermission)target;\r
-                       if (FIOPTarget.IsUnrestricted() && m_Unrestricted){\r
-                               return new FileIOPermission(PermissionState.Unrestricted);\r
-                       }\r
-                       else if (FIOPTarget.IsUnrestricted()){\r
-                               return Copy();\r
-                       }\r
-                       else if (m_Unrestricted){\r
-                               return FIOPTarget.Copy();\r
-                       }\r
-                       else{\r
-                               FileIOPermission retVal = new FileIOPermission(PermissionState.None);\r
-                               retVal.AllFiles = m_AllFilesAccess & FIOPTarget.AllFiles;\r
-                               retVal.AllLocalFiles = m_AllLocalFilesAccess & FIOPTarget.AllLocalFiles;\r
-\r
-                               string[] paths;\r
-                               paths = FIOPTarget.GetPathList(FileIOPermissionAccess.Append);\r
-                               if (null != paths) {\r
-                                       foreach (string path in paths){\r
-                                               if (m_PathList.ContainsKey(path) \r
-                                                       && ((FileIOPermissionAccess)m_PathList[path] & FileIOPermissionAccess.Append) != 0){\r
-                                                       retVal.AddPathList(FileIOPermissionAccess.Append, path);\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               paths = FIOPTarget.GetPathList(FileIOPermissionAccess.Read);\r
-                               if (null != paths) {\r
-                                       foreach (string path in paths){\r
-                                               if (m_PathList.ContainsKey(path) \r
-                                                       && ((FileIOPermissionAccess)m_PathList[path] & FileIOPermissionAccess.Read) != 0){\r
-                                                       retVal.AddPathList(FileIOPermissionAccess.Read, path);\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               paths = FIOPTarget.GetPathList(FileIOPermissionAccess.Write);\r
-                               if (null != paths) {\r
-                                       foreach (string path in paths){\r
-                                               if (m_PathList.ContainsKey(path) \r
-                                                       && ((FileIOPermissionAccess)m_PathList[path] & FileIOPermissionAccess.Write) != 0){\r
-                                                       retVal.AddPathList(FileIOPermissionAccess.Write, path);\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               return retVal;\r
-                       }\r
+               public string[] GetPathList (FileIOPermissionAccess access)
+               {\r
+                        if ((FileIOPermissionAccess.AllAccess & access) != access)
+                               ThrowInvalidFlag (access, true);
+
+                       ArrayList result = new ArrayList ();
+                       switch (access) {
+                               case FileIOPermissionAccess.NoAccess:
+                                       break;
+                               case FileIOPermissionAccess.Read:
+                                       result.AddRange (readList);
+                                       break;
+                               case FileIOPermissionAccess.Write:
+                                       result.AddRange (writeList);
+                                       break;
+                               case FileIOPermissionAccess.Append:
+                                       result.AddRange (appendList);
+                                       break;
+                               case FileIOPermissionAccess.PathDiscovery:
+                                       result.AddRange (pathList);
+                                       break;
+                               default:
+                                       ThrowInvalidFlag (access, false);
+                                       break;
+                       }
+                       return (result.Count > 0) ? (string[]) result.ToArray (typeof (string)) : null;\r
                }\r
 \r
-\r
-               public override bool IsSubsetOf(IPermission target){\r
-                       // X.IsSubsetOf(Y) is true if permission Y includes everything allowed by X.\r
-                       if (target != null && target.GetType() != typeof(FileIOPermission)){\r
-                               throw new ArgumentException();\r
-                       }\r
-                       FileIOPermission FIOPTarget = (FileIOPermission)target;\r
-                       if (FIOPTarget.IsUnrestricted()){\r
-                               return true;\r
-                       }\r
-                       else if (m_Unrestricted){\r
-                               return false;\r
-                       }\r
-                       else if ((m_AllFilesAccess & FIOPTarget.AllFiles) != m_AllFilesAccess) {\r
-                               return false;\r
-                       }\r
-                       else if ((m_AllLocalFilesAccess & FIOPTarget.AllLocalFiles) != m_AllLocalFilesAccess) {\r
-                               return false;\r
-                       }\r
-                       else{\r
-                               string[] pathsNeeded;\r
-                               string[] pathsInTarget;\r
-\r
-                               pathsNeeded = GetPathList(FileIOPermissionAccess.Append);\r
-                               if (null != pathsNeeded) {\r
-                                       pathsInTarget = FIOPTarget.GetPathList(FileIOPermissionAccess.Append);\r
-                                       foreach (string path in pathsNeeded){\r
-                                               if (Array.IndexOf(pathsInTarget, path) <0) {\r
-                                                       return false;\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               pathsNeeded = GetPathList(FileIOPermissionAccess.Read);\r
-                               if (null != pathsNeeded) {\r
-                                       pathsInTarget = FIOPTarget.GetPathList(FileIOPermissionAccess.Read);\r
-                                       foreach (string path in pathsNeeded){\r
-                                               if (Array.IndexOf(pathsInTarget, path) <0) {\r
-                                                       return false;\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               pathsNeeded = GetPathList(FileIOPermissionAccess.Write);\r
-                               if (null != pathsNeeded) {\r
-                                       pathsInTarget = FIOPTarget.GetPathList(FileIOPermissionAccess.Write);\r
-                                       foreach (string path in pathsNeeded){\r
-                                               if (Array.IndexOf(pathsInTarget, path) <0) {\r
-                                                       return false;\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               return true;\r
-                       }\r
+               public override IPermission Intersect (IPermission target)
+               {
+                       FileIOPermission fiop = Cast (target);
+                       if (fiop == null)
+                               return null;
+
+                       if (IsUnrestricted ())
+                               return fiop.Copy ();
+                       if (fiop.IsUnrestricted ())
+                               return Copy ();
+
+                       FileIOPermission result = new FileIOPermission (PermissionState.None);
+                       result.AllFiles = m_AllFilesAccess & fiop.AllFiles;
+                       result.AllLocalFiles = m_AllLocalFilesAccess & fiop.AllLocalFiles;
+
+                       IntersectKeys (readList, fiop.readList, result.readList);
+                       IntersectKeys (writeList, fiop.writeList, result.writeList);
+                       IntersectKeys (appendList, fiop.appendList, result.appendList);
+                       IntersectKeys (pathList, fiop.pathList, result.pathList);
+
+                       return (result.IsEmpty () ? null : result);
                }\r
 \r
-               public bool IsUnrestricted(){\r
+               public override bool IsSubsetOf (IPermission target)
+               {
+                       FileIOPermission fiop = Cast (target);
+                       if (fiop == null) 
+                               return false;
+                       if (fiop.IsEmpty ())
+                               return IsEmpty ();
+
+                       if (IsUnrestricted ())
+                               return fiop.IsUnrestricted ();
+                       else if (fiop.IsUnrestricted ())
+                               return true;
+
+                       if ((m_AllFilesAccess & fiop.AllFiles) != m_AllFilesAccess)
+                               return false;
+                       if ((m_AllLocalFilesAccess & fiop.AllLocalFiles) != m_AllLocalFilesAccess)
+                               return false;
+
+                       if (!KeyIsSubsetOf (appendList, fiop.appendList))
+                               return false;
+                       if (!KeyIsSubsetOf (readList, fiop.readList))
+                               return false;
+                       if (!KeyIsSubsetOf (writeList, fiop.writeList))
+                               return false;
+                       if (!KeyIsSubsetOf (pathList, fiop.pathList))
+                               return false;
+
+                       return true;
+               }\r
+
+               public bool IsUnrestricted ()
+               {\r
                        return m_Unrestricted;\r
                }\r
 \r
-               public void SetPathList(FileIOPermissionAccess access, string path){\r
-                       if ((FileIOPermissionAccess.AllAccess & access) != access){\r
-                               throw new ArgumentException("Illegal enum value: "+access.ToString()+".");\r
-                       }\r
-                       if (path.LastIndexOfAny(m_badCharacters) >= 0){\r
-                               throw new ArgumentException("Invalid characters in path: '{0}'", path);\r
-                       }\r
-\r
-                       m_PathList.Clear();\r
-                       AddPathList(access, path);\r
+               public void SetPathList (FileIOPermissionAccess access, string path)
+               {
+                        if ((FileIOPermissionAccess.AllAccess & access) != access)
+                               ThrowInvalidFlag (access, true);
+                       ThrowIfInvalidPath (path);\r
+                       // note: throw before clearing the actual list
+                       Clear (access);\r
+                       AddPathInternal (access, path);\r
                }\r
                \r
-               public void SetPathList(FileIOPermissionAccess access, string[] pathList){\r
-                       if ((FileIOPermissionAccess.AllAccess & access) != access){\r
-                               throw new ArgumentException("Illegal enum value: "+access.ToString()+".");\r
-                       }\r
-                       foreach(string path in pathList){\r
-                               if (path.LastIndexOfAny(m_badCharacters) >= 0){\r
-                                       throw new ArgumentException("Invalid characters in path entry: '{0}'", path);\r
-                               }\r
-                       }\r
-\r
-                       m_PathList.Clear();\r
-                       AddPathList(access, pathList);\r
-               }\r
-\r
-               public override SecurityElement ToXml(){\r
-                       //Encode the the current permission to XML using the \r
-                       //security element class.\r
-                       SecurityElement element = new SecurityElement("IPermission");\r
-                       Type type = this.GetType();\r
-                       StringBuilder AsmName = new StringBuilder(type.Assembly.ToString());\r
-                       AsmName.Replace('\"', '\'');\r
-                       element.AddAttribute("class", type.FullName + ", " + AsmName);\r
-                       element.AddAttribute("version", "1");\r
-                       if(m_Unrestricted){\r
-                               element.AddAttribute("Unrestricted", "true");\r
+               public void SetPathList (FileIOPermissionAccess access, string[] pathList)
+               {\r
+                        if ((FileIOPermissionAccess.AllAccess & access) != access)
+                               ThrowInvalidFlag (access, true);
+                       ThrowIfInvalidPath (pathList);\r
+                       // note: throw before clearing the actual list
+                       Clear (access);
+                       foreach (string path in pathList)\r
+                               AddPathInternal (access, path);\r
+               }
+
+               public override SecurityElement ToXml ()
+               {\r
+                       SecurityElement se = Element (1);
+                       if (m_Unrestricted) {\r
+                               se.AddAttribute ("Unrestricted", "true");\r
                        }\r
                        else {\r
-                               string[] paths;\r
-                               paths = GetPathList(FileIOPermissionAccess.Append);\r
-                               if (null != paths && paths.Length >0){\r
-                                       element.AddAttribute("Append", String.Join(";",paths));\r
+                               string[] paths = GetPathList (FileIOPermissionAccess.Append);\r
+                               if (null != paths && paths.Length > 0) {\r
+                                       se.AddAttribute ("Append", String.Join (";", paths));\r
+                               }\r
+                               paths = GetPathList (FileIOPermissionAccess.Read);\r
+                               if (null != paths && paths.Length > 0) {\r
+                                       se.AddAttribute ("Read", String.Join (";", paths));\r
                                }\r
-                               paths = GetPathList(FileIOPermissionAccess.Read);\r
-                               if (null != paths && paths.Length >0){\r
-                                       element.AddAttribute("Read", String.Join(";",paths));\r
+                               paths = GetPathList (FileIOPermissionAccess.Write);\r
+                               if (null != paths && paths.Length > 0) {\r
+                                       se.AddAttribute ("Write", String.Join  (";", paths));\r
                                }\r
-                               paths = GetPathList(FileIOPermissionAccess.Write);\r
-                               if (null != paths && paths.Length >0){\r
-                                       element.AddAttribute("Write", String.Join(";",paths));\r
+                               paths = GetPathList (FileIOPermissionAccess.PathDiscovery);\r
+                               if (null != paths && paths.Length > 0) {\r
+                                       se.AddAttribute ("PathDiscovery", String.Join  (";", paths));\r
                                }\r
                        }\r
-                       return element;\r
+                       return se;\r
                }\r
 \r
-               public override IPermission Union(IPermission other){\r
-                       if (null == other){\r
-                               return null;\r
-                       }\r
-                       else {\r
-                               if (other.GetType() != typeof(FileIOPermission)){\r
-                                       throw new ArgumentException();\r
-                               }\r
-                       }\r
-                       FileIOPermission FIOPTarget = (FileIOPermission)other;\r
-                       if (FIOPTarget.IsUnrestricted() || m_Unrestricted){\r
-                               return new FileIOPermission(PermissionState.Unrestricted);\r
-                       }\r
-                       else{\r
-                               FileIOPermission retVal = (FileIOPermission)Copy();\r
-                               retVal.AllFiles |= FIOPTarget.AllFiles;\r
-                               retVal.AllLocalFiles |= FIOPTarget.AllLocalFiles;\r
-                               string[] paths;\r
-                               paths = FIOPTarget.GetPathList(FileIOPermissionAccess.Append);\r
-                               if (null != paths){\r
-                                               retVal.AddPathList(FileIOPermissionAccess.Append, paths);\r
-                               }\r
-                               paths = FIOPTarget.GetPathList(FileIOPermissionAccess.Read);\r
-                               if (null != paths){\r
-                                       retVal.AddPathList(FileIOPermissionAccess.Read, paths);\r
-                               }\r
-                               paths = FIOPTarget.GetPathList(FileIOPermissionAccess.Write);\r
-                               if (null != paths){\r
-                                       retVal.AddPathList(FileIOPermissionAccess.Write, paths);\r
-                               }\r
-                               return retVal;\r
-                       }\r
+               public override IPermission Union (IPermission other)
+               {\r
+                       FileIOPermission fiop = Cast (other);
+                       if (fiop == null)
+                               return Copy ();
+
+                       if (IsUnrestricted () || fiop.IsUnrestricted ())
+                               return new FileIOPermission (PermissionState.Unrestricted);
+
+                       if (IsEmpty () && fiop.IsEmpty ())
+                               return null;
+
+                       FileIOPermission result = (FileIOPermission) Copy ();
+                       result.AllFiles |= fiop.AllFiles;
+                       result.AllLocalFiles |= fiop.AllLocalFiles;
+
+                       string[] paths = fiop.GetPathList (FileIOPermissionAccess.Read);
+                       if (paths != null) 
+                               UnionKeys (result.readList, paths);
+
+                       paths = fiop.GetPathList (FileIOPermissionAccess.Write);
+                       if (paths != null)
+                               UnionKeys (result.writeList, paths);
+
+                       paths = fiop.GetPathList (FileIOPermissionAccess.Append);
+                       if (paths != null) 
+                               UnionKeys (result.appendList, paths);
+
+                       paths = fiop.GetPathList (FileIOPermissionAccess.PathDiscovery);
+                       if (paths != null) 
+                               UnionKeys (result.pathList, paths);
+                       
+                       return result;
                }\r
 \r
+#if NET_2_0
+               [MonoTODO ("(2.0)")]
+               [ComVisible (false)]
+               public override bool Equals (object obj)
+               {
+                       return false;
+               }
+
+               [MonoTODO ("(2.0)")]
+               [ComVisible (false)]
+               public override int GetHashCode ()
+               {
+                       return base.GetHashCode ();
+               }
+#endif
+
                // IBuiltInPermission\r
                int IBuiltInPermission.GetTokenIndex ()\r
                {\r
-                       return 2;\r
+                       return (int) BuiltInToken.FileIO;
+               }
+
+               // helpers
+
+               private bool IsEmpty ()
+               {
+                       return ((!m_Unrestricted) && (appendList.Count == 0) && (readList.Count == 0)
+                               && (writeList.Count == 0) && (pathList.Count == 0));
+               }
+
+               private FileIOPermission Cast (IPermission target)
+               {
+                       if (target == null)
+                               return null;
+
+                       FileIOPermission fiop = (target as FileIOPermission);
+                       if (fiop == null) {
+                               ThrowInvalidPermission (target, typeof (FileIOPermission));
+                       }
+
+                       return fiop;
+               }
+
+               internal void ThrowInvalidFlag (FileIOPermissionAccess access, bool context) 
+               {
+                       string msg = null;
+                       if (context)
+                               msg = Locale.GetText ("Unknown flag '{0}'.");
+                       else
+                               msg = Locale.GetText ("Invalid flag '{0}' in this context.");
+                       throw new ArgumentException (String.Format (msg, access), "access");
+               }
+
+               internal void ThrowIfInvalidPath (string path)
+               {
+                       if (path.LastIndexOfAny (m_badCharacters) >= 0) {\r
+                               string msg = String.Format (Locale.GetText ("Invalid characters in path: '{0}'"), path);\r
+                               throw new ArgumentException (msg, "path");\r
+                       }\r
+                       // LAMESPEC: docs don't say it must be a rooted path, but the MS implementation enforces it, so we will too.\r
+                       if (!Path.IsPathRooted (path)) {
+                               string msg = Locale.GetText ("Absolute path information is required.");
+                               throw new ArgumentException (msg, "path");\r
+                       }
+               }
+\r
+               internal void ThrowIfInvalidPath (string[] paths)
+               {
+                       foreach (string path in paths)
+                               ThrowIfInvalidPath (path);
+               }
+
+               // we known that access is valid at this point
+               internal void Clear (FileIOPermissionAccess access)
+               {
+                       if ((access & FileIOPermissionAccess.Read) == FileIOPermissionAccess.Read)
+                               readList.Clear ();
+                       if ((access & FileIOPermissionAccess.Write) == FileIOPermissionAccess.Write)
+                               writeList.Clear ();
+                       if ((access & FileIOPermissionAccess.Append) == FileIOPermissionAccess.Append)
+                               appendList.Clear ();
+                       if ((access & FileIOPermissionAccess.PathDiscovery) == FileIOPermissionAccess.PathDiscovery)
+                               pathList.Clear ();
                }\r
+
+               // note: all path in IList are already "full paths"
+               internal bool KeyIsSubsetOf (IList local, IList target)
+               {
+                       bool result = false;
+                       foreach (string l in local) {
+                               foreach (string t in target) {
+                                       if (l.StartsWith (t)) {
+                                               result = true;
+                                               break;
+                                       }
+                               }
+                               if (!result)
+                                       return false;
+                       }
+                       return true;
+               }
+
+               internal void UnionKeys (IList list, string[] paths)
+               {
+                       foreach (string path in paths) {
+                               int len = list.Count;
+                               if (len == 0) {
+                                       list.Add (path);
+                               }
+                               else {
+                                       for (int i=0; i < len; i++) {
+                                               string s = (string) list [i];
+                                               if (s.StartsWith (path)) {
+                                                       // replace (with reduced version)
+                                                       list [i] = path;
+                                                       break;
+                                               }
+                                               else if (path.StartsWith (s)) {
+                                                       // no need to add
+                                                       break;
+                                               }
+                                               else {
+                                                       list.Add (path);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               internal void IntersectKeys (IList local, IList target, IList result)
+               {
+                       foreach (string l in local) {
+                               foreach (string t in target) {
+                                       if (t.Length > l.Length) {
+                                               if (t.StartsWith (l))
+                                                       result.Add (t);
+                                       }
+                                       else {
+                                               if (l.StartsWith (t))
+                                                       result.Add (l);
+                                       }
+                               }
+                       }
+               }
        }\r
 }\r