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