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 "<<ALL FILES>>" 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 "<<ALL FILES>>" 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 * "<<ALL FILES>>" 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 }