1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.io;
  27 
  28 import java.security.*;
  29 import java.util.Enumeration;
  30 import java.util.List;
  31 import java.util.ArrayList;
  32 import java.util.Vector;
  33 import java.util.Collections;
  34 import java.util.StringJoiner;
  35 import sun.security.util.SecurityConstants;
  36 
  37 /**
  38  * This class represents access to a file or directory.  A FilePermission consists
  39  * of a pathname and a set of actions valid for that pathname.
  40  * <P>
  41  * Pathname is the pathname of the file or directory granted the specified
  42  * actions. A pathname that ends in "/*" (where "/" is
  43  * the file separator character, <code>File.separatorChar</code>) indicates
  44  * all the files and directories contained in that directory. A pathname
  45  * that ends with "/-" indicates (recursively) all files
  46  * and subdirectories contained in that directory. A pathname consisting of
  47  * the special token "&lt;&lt;ALL FILES&gt;&gt;" matches <b>any</b> file.
  48  * <P>
  49  * Note: A pathname consisting of a single "*" indicates all the files
  50  * in the current directory, while a pathname consisting of a single "-"
  51  * indicates all the files in the current directory and
  52  * (recursively) all files and subdirectories contained in the current
  53  * directory.
  54  * <P>
  55  * The actions to be granted are passed to the constructor in a string containing
  56  * a list of one or more comma-separated keywords. The possible keywords are
  57  * "read", "write", "execute", "delete", and "readlink". Their meaning is
  58  * defined as follows:
  59  *
  60  * <DL>
  61  *    <DT> read <DD> read permission
  62  *    <DT> write <DD> write permission
  63  *    <DT> execute
  64  *    <DD> execute permission. Allows <code>Runtime.exec</code> to
  65  *         be called. Corresponds to <code>SecurityManager.checkExec</code>.
  66  *    <DT> delete
  67  *    <DD> delete permission. Allows <code>File.delete</code> to
  68  *         be called. Corresponds to <code>SecurityManager.checkDelete</code>.
  69  *    <DT> readlink
  70  *    <DD> read link permission. Allows the target of a
  71  *         <a href="../nio/file/package-summary.html#links">symbolic link</a>
  72  *         to be read by invoking the {@link java.nio.file.Files#readSymbolicLink
  73  *         readSymbolicLink } method.
  74  * </DL>
  75  * <P>
  76  * The actions string is converted to lowercase before processing.
  77  * <P>
  78  * Be careful when granting FilePermissions. Think about the implications
  79  * of granting read and especially write access to various files and
  80  * directories. The "&lt;&lt;ALL FILES&gt;&gt;" permission with write action is
  81  * especially dangerous. This grants permission to write to the entire
  82  * file system. One thing this effectively allows is replacement of the
  83  * system binary, including the JVM runtime environment.
  84  *
  85  * <p>Please note: Code can always read a file from the same
  86  * directory it's in (or a subdirectory of that directory); it does not
  87  * need explicit permission to do so.
  88  *
  89  * @see java.security.Permission
  90  * @see java.security.Permissions
  91  * @see java.security.PermissionCollection
  92  *
  93  *
  94  * @author Marianne Mueller
  95  * @author Roland Schemers
  96  * @since 1.2
  97  *
  98  * @serial exclude
  99  */
 100 
 101 public final class FilePermission extends Permission implements Serializable {
 102 
 103     /**
 104      * Execute action.
 105      */
 106     private final static int EXECUTE = 0x1;
 107     /**
 108      * Write action.
 109      */
 110     private final static int WRITE   = 0x2;
 111     /**
 112      * Read action.
 113      */
 114     private final static int READ    = 0x4;
 115     /**
 116      * Delete action.
 117      */
 118     private final static int DELETE  = 0x8;
 119     /**
 120      * Read link action.
 121      */
 122     private final static int READLINK    = 0x10;
 123 
 124     /**
 125      * All actions (read,write,execute,delete,readlink)
 126      */
 127     private final static int ALL     = READ|WRITE|EXECUTE|DELETE|READLINK;
 128     /**
 129      * No actions.
 130      */
 131     private final static int NONE    = 0x0;
 132 
 133     // the actions mask
 134     private transient int mask;
 135 
 136     // does path indicate a directory? (wildcard or recursive)
 137     private transient boolean directory;
 138 
 139     // is it a recursive directory specification?
 140     private transient boolean recursive;
 141 
 142     /**
 143      * the actions string.
 144      *
 145      * @serial
 146      */
 147     private String actions; // Left null as long as possible, then
 148                             // created and re-used in the getAction function.
 149 
 150     // canonicalized dir path. In the case of
 151     // directories, it is the name "/blah/*" or "/blah/-" without
 152     // the last character (the "*" or "-").
 153 
 154     private transient String cpath;
 155 
 156     // static Strings used by init(int mask)
 157     private static final char RECURSIVE_CHAR = '-';
 158     private static final char WILD_CHAR = '*';
 159 
 160 /*
 161     public String toString()
 162     {
 163         StringBuffer sb = new StringBuffer();
 164         sb.append("***\n");
 165         sb.append("cpath = "+cpath+"\n");
 166         sb.append("mask = "+mask+"\n");
 167         sb.append("actions = "+getActions()+"\n");
 168         sb.append("directory = "+directory+"\n");
 169         sb.append("recursive = "+recursive+"\n");
 170         sb.append("***\n");
 171         return sb.toString();
 172     }
 173 */
 174 
 175     private static final long serialVersionUID = 7930732926638008763L;
 176 
 177     /**
 178      * initialize a FilePermission object. Common to all constructors.
 179      * Also called during de-serialization.
 180      *
 181      * @param mask the actions mask to use.
 182      *
 183      */
 184     private void init(int mask) {
 185         if ((mask & ALL) != mask)
 186                 throw new IllegalArgumentException("invalid actions mask");
 187 
 188         if (mask == NONE)
 189                 throw new IllegalArgumentException("invalid actions mask");
 190 
 191         if ((cpath = getName()) == null)
 192                 throw new NullPointerException("name can't be null");
 193 
 194         this.mask = mask;
 195 
 196         if (cpath.equals("<<ALL FILES>>")) {
 197             directory = true;
 198             recursive = true;
 199             cpath = "";
 200             return;
 201         }
 202 
 203         // store only the canonical cpath if possible
 204         cpath = AccessController.doPrivileged(new PrivilegedAction<>() {
 205             public String run() {
 206                 try {
 207                     String path = cpath;
 208                     if (cpath.endsWith("*")) {
 209                         // call getCanonicalPath with a path with wildcard character
 210                         // replaced to avoid calling it with paths that are
 211                         // intended to match all entries in a directory
 212                         path = path.substring(0, path.length()-1) + "-";
 213                         path = new File(path).getCanonicalPath();
 214                         return path.substring(0, path.length()-1) + "*";
 215                     } else {
 216                         return new File(path).getCanonicalPath();
 217                     }
 218                 } catch (IOException ioe) {
 219                     return cpath;
 220                 }
 221             }
 222         });
 223 
 224         int len = cpath.length();
 225         char last = ((len > 0) ? cpath.charAt(len - 1) : 0);
 226 
 227         if (last == RECURSIVE_CHAR &&
 228             cpath.charAt(len - 2) == File.separatorChar) {
 229             directory = true;
 230             recursive = true;
 231             cpath = cpath.substring(0, --len);
 232         } else if (last == WILD_CHAR &&
 233             cpath.charAt(len - 2) == File.separatorChar) {
 234             directory = true;
 235             //recursive = false;
 236             cpath = cpath.substring(0, --len);
 237         } else {
 238             // overkill since they are initialized to false, but
 239             // commented out here to remind us...
 240             //directory = false;
 241             //recursive = false;
 242         }
 243 
 244         // XXX: at this point the path should be absolute. die if it isn't?
 245     }
 246 
 247     /**
 248      * Creates a new FilePermission object with the specified actions.
 249      * <i>path</i> is the pathname of a file or directory, and <i>actions</i>
 250      * contains a comma-separated list of the desired actions granted on the
 251      * file or directory. Possible actions are
 252      * "read", "write", "execute", "delete", and "readlink".
 253      *
 254      * <p>A pathname that ends in "/*" (where "/" is
 255      * the file separator character, <code>File.separatorChar</code>)
 256      * indicates all the files and directories contained in that directory.
 257      * A pathname that ends with "/-" indicates (recursively) all files and
 258      * subdirectories contained in that directory. The special pathname
 259      * "&lt;&lt;ALL FILES&gt;&gt;" matches any file.
 260      *
 261      * <p>A pathname consisting of a single "*" indicates all the files
 262      * in the current directory, while a pathname consisting of a single "-"
 263      * indicates all the files in the current directory and
 264      * (recursively) all files and subdirectories contained in the current
 265      * directory.
 266      *
 267      * <p>A pathname containing an empty string represents an empty path.
 268      *
 269      * @param path the pathname of the file/directory.
 270      * @param actions the action string.
 271      *
 272      * @throws IllegalArgumentException
 273      *          If actions is <code>null</code>, empty or contains an action
 274      *          other than the specified possible actions.
 275      */
 276     public FilePermission(String path, String actions) {
 277         super(path);
 278         init(getMask(actions));
 279     }
 280 
 281     /**
 282      * Creates a new FilePermission object using an action mask.
 283      * More efficient than the FilePermission(String, String) constructor.
 284      * Can be used from within
 285      * code that needs to create a FilePermission object to pass into the
 286      * <code>implies</code> method.
 287      *
 288      * @param path the pathname of the file/directory.
 289      * @param mask the action mask to use.
 290      */
 291 
 292     // package private for use by the FilePermissionCollection add method
 293     FilePermission(String path, int mask) {
 294         super(path);
 295         init(mask);
 296     }
 297 
 298     /**
 299      * Checks if this FilePermission object "implies" the specified permission.
 300      * <P>
 301      * More specifically, this method returns true if:
 302      * <ul>
 303      * <li> <i>p</i> is an instanceof FilePermission,
 304      * <li> <i>p</i>'s actions are a proper subset of this
 305      * object's actions, and
 306      * <li> <i>p</i>'s pathname is implied by this object's
 307      *      pathname. For example, "/tmp/*" implies "/tmp/foo", since
 308      *      "/tmp/*" encompasses all files in the "/tmp" directory,
 309      *      including the one named "foo".
 310      * </ul>
 311      *
 312      * @param p the permission to check against.
 313      *
 314      * @return <code>true</code> if the specified permission is not
 315      *                  <code>null</code> and is implied by this object,
 316      *                  <code>false</code> otherwise.
 317      */
 318     public boolean implies(Permission p) {
 319         if (!(p instanceof FilePermission))
 320             return false;
 321 
 322         FilePermission that = (FilePermission) p;
 323 
 324         // we get the effective mask. i.e., the "and" of this and that.
 325         // They must be equal to that.mask for implies to return true.
 326 
 327         return ((this.mask & that.mask) == that.mask) && impliesIgnoreMask(that);
 328     }
 329 
 330     /**
 331      * Checks if the Permission's actions are a proper subset of the
 332      * this object's actions. Returns the effective mask iff the
 333      * this FilePermission's path also implies that FilePermission's path.
 334      *
 335      * @param that the FilePermission to check against.
 336      * @return the effective mask
 337      */
 338     boolean impliesIgnoreMask(FilePermission that) {
 339         if (this.directory) {
 340             if (this.recursive) {
 341                 // make sure that.path is longer then path so
 342                 // something like /foo/- does not imply /foo
 343                 if (that.directory) {
 344                     return (that.cpath.length() >= this.cpath.length()) &&
 345                             that.cpath.startsWith(this.cpath);
 346                 }  else {
 347                     return ((that.cpath.length() > this.cpath.length()) &&
 348                         that.cpath.startsWith(this.cpath));
 349                 }
 350             } else {
 351                 if (that.directory) {
 352                     // if the permission passed in is a directory
 353                     // specification, make sure that a non-recursive
 354                     // permission (i.e., this object) can't imply a recursive
 355                     // permission.
 356                     if (that.recursive)
 357                         return false;
 358                     else
 359                         return (this.cpath.equals(that.cpath));
 360                 } else {
 361                     int last = that.cpath.lastIndexOf(File.separatorChar);
 362                     if (last == -1)
 363                         return false;
 364                     else {
 365                         // this.cpath.equals(that.cpath.substring(0, last+1));
 366                         // Use regionMatches to avoid creating new string
 367                         return (this.cpath.length() == (last + 1)) &&
 368                             this.cpath.regionMatches(0, that.cpath, 0, last+1);
 369                     }
 370                 }
 371             }
 372         } else if (that.directory) {
 373             // if this is NOT recursive/wildcarded,
 374             // do not let it imply a recursive/wildcarded permission
 375             return false;
 376         } else {
 377             return (this.cpath.equals(that.cpath));
 378         }
 379     }
 380 
 381     /**
 382      * Checks two FilePermission objects for equality. Checks that <i>obj</i> is
 383      * a FilePermission, and has the same pathname and actions as this object.
 384      *
 385      * @param obj the object we are testing for equality with this object.
 386      * @return <code>true</code> if obj is a FilePermission, and has the same
 387      *          pathname and actions as this FilePermission object,
 388      *          <code>false</code> otherwise.
 389      */
 390     public boolean equals(Object obj) {
 391         if (obj == this)
 392             return true;
 393 
 394         if (! (obj instanceof FilePermission))
 395             return false;
 396 
 397         FilePermission that = (FilePermission) obj;
 398 
 399         return (this.mask == that.mask) &&
 400             this.cpath.equals(that.cpath) &&
 401             (this.directory == that.directory) &&
 402             (this.recursive == that.recursive);
 403     }
 404 
 405     /**
 406      * Returns the hash code value for this object.
 407      *
 408      * @return a hash code value for this object.
 409      */
 410     public int hashCode() {
 411         return 0;
 412     }
 413 
 414     /**
 415      * Converts an actions String to an actions mask.
 416      *
 417      * @param actions the action string.
 418      * @return the actions mask.
 419      */
 420     private static int getMask(String actions) {
 421         int mask = NONE;
 422 
 423         // Null action valid?
 424         if (actions == null) {
 425             return mask;
 426         }
 427 
 428         // Use object identity comparison against known-interned strings for
 429         // performance benefit (these values are used heavily within the JDK).
 430         if (actions == SecurityConstants.FILE_READ_ACTION) {
 431             return READ;
 432         } else if (actions == SecurityConstants.FILE_WRITE_ACTION) {
 433             return WRITE;
 434         } else if (actions == SecurityConstants.FILE_EXECUTE_ACTION) {
 435             return EXECUTE;
 436         } else if (actions == SecurityConstants.FILE_DELETE_ACTION) {
 437             return DELETE;
 438         } else if (actions == SecurityConstants.FILE_READLINK_ACTION) {
 439             return READLINK;
 440         }
 441 
 442         char[] a = actions.toCharArray();
 443 
 444         int i = a.length - 1;
 445         if (i < 0)
 446             return mask;
 447 
 448         while (i != -1) {
 449             char c;
 450 
 451             // skip whitespace
 452             while ((i!=-1) && ((c = a[i]) == ' ' ||
 453                                c == '\r' ||
 454                                c == '\n' ||
 455                                c == '\f' ||
 456                                c == '\t'))
 457                 i--;
 458 
 459             // check for the known strings
 460             int matchlen;
 461 
 462             if (i >= 3 && (a[i-3] == 'r' || a[i-3] == 'R') &&
 463                           (a[i-2] == 'e' || a[i-2] == 'E') &&
 464                           (a[i-1] == 'a' || a[i-1] == 'A') &&
 465                           (a[i] == 'd' || a[i] == 'D'))
 466             {
 467                 matchlen = 4;
 468                 mask |= READ;
 469 
 470             } else if (i >= 4 && (a[i-4] == 'w' || a[i-4] == 'W') &&
 471                                  (a[i-3] == 'r' || a[i-3] == 'R') &&
 472                                  (a[i-2] == 'i' || a[i-2] == 'I') &&
 473                                  (a[i-1] == 't' || a[i-1] == 'T') &&
 474                                  (a[i] == 'e' || a[i] == 'E'))
 475             {
 476                 matchlen = 5;
 477                 mask |= WRITE;
 478 
 479             } else if (i >= 6 && (a[i-6] == 'e' || a[i-6] == 'E') &&
 480                                  (a[i-5] == 'x' || a[i-5] == 'X') &&
 481                                  (a[i-4] == 'e' || a[i-4] == 'E') &&
 482                                  (a[i-3] == 'c' || a[i-3] == 'C') &&
 483                                  (a[i-2] == 'u' || a[i-2] == 'U') &&
 484                                  (a[i-1] == 't' || a[i-1] == 'T') &&
 485                                  (a[i] == 'e' || a[i] == 'E'))
 486             {
 487                 matchlen = 7;
 488                 mask |= EXECUTE;
 489 
 490             } else if (i >= 5 && (a[i-5] == 'd' || a[i-5] == 'D') &&
 491                                  (a[i-4] == 'e' || a[i-4] == 'E') &&
 492                                  (a[i-3] == 'l' || a[i-3] == 'L') &&
 493                                  (a[i-2] == 'e' || a[i-2] == 'E') &&
 494                                  (a[i-1] == 't' || a[i-1] == 'T') &&
 495                                  (a[i] == 'e' || a[i] == 'E'))
 496             {
 497                 matchlen = 6;
 498                 mask |= DELETE;
 499 
 500             } else if (i >= 7 && (a[i-7] == 'r' || a[i-7] == 'R') &&
 501                                  (a[i-6] == 'e' || a[i-6] == 'E') &&
 502                                  (a[i-5] == 'a' || a[i-5] == 'A') &&
 503                                  (a[i-4] == 'd' || a[i-4] == 'D') &&
 504                                  (a[i-3] == 'l' || a[i-3] == 'L') &&
 505                                  (a[i-2] == 'i' || a[i-2] == 'I') &&
 506                                  (a[i-1] == 'n' || a[i-1] == 'N') &&
 507                                  (a[i] == 'k' || a[i] == 'K'))
 508             {
 509                 matchlen = 8;
 510                 mask |= READLINK;
 511 
 512             } else {
 513                 // parse error
 514                 throw new IllegalArgumentException(
 515                         "invalid permission: " + actions);
 516             }
 517 
 518             // make sure we didn't just match the tail of a word
 519             // like "ackbarfaccept".  Also, skip to the comma.
 520             boolean seencomma = false;
 521             while (i >= matchlen && !seencomma) {
 522                 switch(a[i-matchlen]) {
 523                 case ',':
 524                     seencomma = true;
 525                     break;
 526                 case ' ': case '\r': case '\n':
 527                 case '\f': case '\t':
 528                     break;
 529                 default:
 530                     throw new IllegalArgumentException(
 531                             "invalid permission: " + actions);
 532                 }
 533                 i--;
 534             }
 535 
 536             // point i at the location of the comma minus one (or -1).
 537             i -= matchlen;
 538         }
 539 
 540         return mask;
 541     }
 542 
 543     /**
 544      * Return the current action mask. Used by the FilePermissionCollection.
 545      *
 546      * @return the actions mask.
 547      */
 548     int getMask() {
 549         return mask;
 550     }
 551 
 552     /**
 553      * Return the canonical string representation of the actions.
 554      * Always returns present actions in the following order:
 555      * read, write, execute, delete, readlink.
 556      *
 557      * @return the canonical string representation of the actions.
 558      */
 559     private static String getActions(int mask) {
 560         StringJoiner sj = new StringJoiner(",");
 561 
 562         if ((mask & READ) == READ) {
 563             sj.add("read");
 564         }
 565         if ((mask & WRITE) == WRITE) {
 566             sj.add("write");
 567         }
 568         if ((mask & EXECUTE) == EXECUTE) {
 569             sj.add("execute");
 570         }
 571         if ((mask & DELETE) == DELETE) {
 572             sj.add("delete");
 573         }
 574         if ((mask & READLINK) == READLINK) {
 575             sj.add("readlink");
 576         }
 577 
 578         return sj.toString();
 579     }
 580 
 581     /**
 582      * Returns the "canonical string representation" of the actions.
 583      * That is, this method always returns present actions in the following order:
 584      * read, write, execute, delete, readlink. For example, if this FilePermission
 585      * object allows both write and read actions, a call to <code>getActions</code>
 586      * will return the string "read,write".
 587      *
 588      * @return the canonical string representation of the actions.
 589      */
 590     public String getActions() {
 591         if (actions == null)
 592             actions = getActions(this.mask);
 593 
 594         return actions;
 595     }
 596 
 597     /**
 598      * Returns a new PermissionCollection object for storing FilePermission
 599      * objects.
 600      * <p>
 601      * FilePermission objects must be stored in a manner that allows them
 602      * to be inserted into the collection in any order, but that also enables the
 603      * PermissionCollection <code>implies</code>
 604      * method to be implemented in an efficient (and consistent) manner.
 605      *
 606      * <p>For example, if you have two FilePermissions:
 607      * <OL>
 608      * <LI>  <code>"/tmp/-", "read"</code>
 609      * <LI>  <code>"/tmp/scratch/foo", "write"</code>
 610      * </OL>
 611      *
 612      * <p>and you are calling the <code>implies</code> method with the FilePermission:
 613      *
 614      * <pre>
 615      *   "/tmp/scratch/foo", "read,write",
 616      * </pre>
 617      *
 618      * then the <code>implies</code> function must
 619      * take into account both the "/tmp/-" and "/tmp/scratch/foo"
 620      * permissions, so the effective permission is "read,write",
 621      * and <code>implies</code> returns true. The "implies" semantics for
 622      * FilePermissions are handled properly by the PermissionCollection object
 623      * returned by this <code>newPermissionCollection</code> method.
 624      *
 625      * @return a new PermissionCollection object suitable for storing
 626      * FilePermissions.
 627      */
 628     public PermissionCollection newPermissionCollection() {
 629         return new FilePermissionCollection();
 630     }
 631 
 632     /**
 633      * WriteObject is called to save the state of the FilePermission
 634      * to a stream. The actions are serialized, and the superclass
 635      * takes care of the name.
 636      */
 637     private void writeObject(ObjectOutputStream s)
 638         throws IOException
 639     {
 640         // Write out the actions. The superclass takes care of the name
 641         // call getActions to make sure actions field is initialized
 642         if (actions == null)
 643             getActions();
 644         s.defaultWriteObject();
 645     }
 646 
 647     /**
 648      * readObject is called to restore the state of the FilePermission from
 649      * a stream.
 650      */
 651     private void readObject(ObjectInputStream s)
 652          throws IOException, ClassNotFoundException
 653     {
 654         // Read in the actions, then restore everything else by calling init.
 655         s.defaultReadObject();
 656         init(getMask(actions));
 657     }
 658 }
 659 
 660 /**
 661  * A FilePermissionCollection stores a set of FilePermission permissions.
 662  * FilePermission objects
 663  * must be stored in a manner that allows them to be inserted in any
 664  * order, but enable the implies function to evaluate the implies
 665  * method.
 666  * For example, if you have two FilePermissions:
 667  * <OL>
 668  * <LI> "/tmp/-", "read"
 669  * <LI> "/tmp/scratch/foo", "write"
 670  * </OL>
 671  * And you are calling the implies function with the FilePermission:
 672  * "/tmp/scratch/foo", "read,write", then the implies function must
 673  * take into account both the /tmp/- and /tmp/scratch/foo
 674  * permissions, so the effective permission is "read,write".
 675  *
 676  * @see java.security.Permission
 677  * @see java.security.Permissions
 678  * @see java.security.PermissionCollection
 679  *
 680  *
 681  * @author Marianne Mueller
 682  * @author Roland Schemers
 683  *
 684  * @serial include
 685  *
 686  */
 687 
 688 final class FilePermissionCollection extends PermissionCollection
 689     implements Serializable
 690 {
 691     // Not serialized; see serialization section at end of class
 692     private transient List<Permission> perms;
 693 
 694     /**
 695      * Create an empty FilePermissionCollection object.
 696      */
 697     public FilePermissionCollection() {
 698         perms = new ArrayList<>();
 699     }
 700 
 701     /**
 702      * Adds a permission to the FilePermissionCollection. The key for the hash is
 703      * permission.path.
 704      *
 705      * @param permission the Permission object to add.
 706      *
 707      * @exception IllegalArgumentException - if the permission is not a
 708      *                                       FilePermission
 709      *
 710      * @exception SecurityException - if this FilePermissionCollection object
 711      *                                has been marked readonly
 712      */
 713     public void add(Permission permission) {
 714         if (! (permission instanceof FilePermission))
 715             throw new IllegalArgumentException("invalid permission: "+
 716                                                permission);
 717         if (isReadOnly())
 718             throw new SecurityException(
 719                 "attempt to add a Permission to a readonly PermissionCollection");
 720 
 721         synchronized (this) {
 722             perms.add(permission);
 723         }
 724     }
 725 
 726     /**
 727      * Check and see if this set of permissions implies the permissions
 728      * expressed in "permission".
 729      *
 730      * @param permission the Permission object to compare
 731      *
 732      * @return true if "permission" is a proper subset of a permission in
 733      * the set, false if not.
 734      */
 735     public boolean implies(Permission permission) {
 736         if (! (permission instanceof FilePermission))
 737             return false;
 738 
 739         FilePermission fp = (FilePermission) permission;
 740 
 741         int desired = fp.getMask();
 742         int effective = 0;
 743         int needed = desired;
 744 
 745         synchronized (this) {
 746             int len = perms.size();
 747             for (int i = 0; i < len; i++) {
 748                 FilePermission x = (FilePermission) perms.get(i);
 749                 if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(fp)) {
 750                     effective |=  x.getMask();
 751                     if ((effective & desired) == desired)
 752                         return true;
 753                     needed = (desired ^ effective);
 754                 }
 755             }
 756         }
 757         return false;
 758     }
 759 
 760     /**
 761      * Returns an enumeration of all the FilePermission objects in the
 762      * container.
 763      *
 764      * @return an enumeration of all the FilePermission objects.
 765      */
 766     public Enumeration<Permission> elements() {
 767         // Convert Iterator into Enumeration
 768         synchronized (this) {
 769             return Collections.enumeration(perms);
 770         }
 771     }
 772 
 773     private static final long serialVersionUID = 2202956749081564585L;
 774 
 775     // Need to maintain serialization interoperability with earlier releases,
 776     // which had the serializable field:
 777     //    private Vector permissions;
 778 
 779     /**
 780      * @serialField permissions java.util.Vector
 781      *     A list of FilePermission objects.
 782      */
 783     private static final ObjectStreamField[] serialPersistentFields = {
 784         new ObjectStreamField("permissions", Vector.class),
 785     };
 786 
 787     /**
 788      * @serialData "permissions" field (a Vector containing the FilePermissions).
 789      */
 790     /*
 791      * Writes the contents of the perms field out as a Vector for
 792      * serialization compatibility with earlier releases.
 793      */
 794     private void writeObject(ObjectOutputStream out) throws IOException {
 795         // Don't call out.defaultWriteObject()
 796 
 797         // Write out Vector
 798         Vector<Permission> permissions = new Vector<>(perms.size());
 799         synchronized (this) {
 800             permissions.addAll(perms);
 801         }
 802 
 803         ObjectOutputStream.PutField pfields = out.putFields();
 804         pfields.put("permissions", permissions);
 805         out.writeFields();
 806     }
 807 
 808     /*
 809      * Reads in a Vector of FilePermissions and saves them in the perms field.
 810      */
 811     private void readObject(ObjectInputStream in)
 812         throws IOException, ClassNotFoundException
 813     {
 814         // Don't call defaultReadObject()
 815 
 816         // Read in serialized fields
 817         ObjectInputStream.GetField gfields = in.readFields();
 818 
 819         // Get the one we want
 820         @SuppressWarnings("unchecked")
 821         Vector<Permission> permissions = (Vector<Permission>)gfields.get("permissions", null);
 822         perms = new ArrayList<>(permissions.size());
 823         perms.addAll(permissions);
 824     }
 825 }