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