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