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.StringJoiner;
31 import java.util.Vector;
32 import java.util.concurrent.ConcurrentHashMap;
33 import sun.security.util.SecurityConstants;
34
35 /**
36 * This class represents access to a file or directory. A FilePermission consists
37 * of a pathname and a set of actions valid for that pathname.
38 * <P>
39 * Pathname is the pathname of the file or directory granted the specified
40 * actions. A pathname that ends in "/*" (where "/" is
41 * the file separator character, <code>File.separatorChar</code>) indicates
42 * all the files and directories contained in that directory. A pathname
43 * that ends with "/-" indicates (recursively) all files
44 * and subdirectories contained in that directory. A pathname consisting of
45 * the special token "<<ALL FILES>>" matches <b>any</b> file.
46 * <P>
47 * Note: A pathname consisting of a single "*" indicates all the files
48 * in the current directory, while a pathname consisting of a single "-"
49 * indicates all the files in the current directory and
50 * (recursively) all files and subdirectories contained in the current
51 * directory.
52 * <P>
53 * The actions to be granted are passed to the constructor in a string containing
54 * a list of one or more comma-separated keywords. The possible keywords are
55 * "read", "write", "execute", "delete", and "readlink". Their meaning is
56 * defined as follows:
57 *
58 * <DL>
59 * <DT> read <DD> read permission
60 * <DT> write <DD> write permission
61 * <DT> execute
62 * <DD> execute permission. Allows <code>Runtime.exec</code> to
63 * be called. Corresponds to <code>SecurityManager.checkExec</code>.
64 * <DT> delete
65 * <DD> delete permission. Allows <code>File.delete</code> to
66 * be called. Corresponds to <code>SecurityManager.checkDelete</code>.
67 * <DT> readlink
68 * <DD> read link permission. Allows the target of a
69 * <a href="../nio/file/package-summary.html#links">symbolic link</a>
70 * to be read by invoking the {@link java.nio.file.Files#readSymbolicLink
71 * readSymbolicLink } method.
72 * </DL>
73 * <P>
74 * The actions string is converted to lowercase before processing.
75 * <P>
76 * Be careful when granting FilePermissions. Think about the implications
77 * of granting read and especially write access to various files and
78 * directories. The "<<ALL FILES>>" permission with write action is
79 * especially dangerous. This grants permission to write to the entire
80 * file system. One thing this effectively allows is replacement of the
81 * system binary, including the JVM runtime environment.
82 *
83 * <p>Please note: Code can always read a file from the same
84 * directory it's in (or a subdirectory of that directory); it does not
85 * need explicit permission to do so.
86 *
87 * @see java.security.Permission
88 * @see java.security.Permissions
89 * @see java.security.PermissionCollection
90 *
91 *
92 * @author Marianne Mueller
93 * @author Roland Schemers
94 * @since 1.2
95 *
96 * @serial exclude
97 */
98
99 public final class FilePermission extends Permission implements Serializable {
100
101 /**
102 * Execute action.
103 */
104 private static final int EXECUTE = 0x1;
105 /**
106 * Write action.
128 */
129 private static final int NONE = 0x0;
130
131 // the actions mask
132 private transient int mask;
133
134 // does path indicate a directory? (wildcard or recursive)
135 private transient boolean directory;
136
137 // is it a recursive directory specification?
138 private transient boolean recursive;
139
140 /**
141 * the actions string.
142 *
143 * @serial
144 */
145 private String actions; // Left null as long as possible, then
146 // created and re-used in the getAction function.
147
148 // canonicalized dir path. In the case of
149 // directories, it is the name "/blah/*" or "/blah/-" without
150 // the last character (the "*" or "-").
151
152 private transient String cpath;
153
154 // static Strings used by init(int mask)
155 private static final char RECURSIVE_CHAR = '-';
156 private static final char WILD_CHAR = '*';
157
158 /*
159 public String toString()
160 {
161 StringBuffer sb = new StringBuffer();
162 sb.append("***\n");
163 sb.append("cpath = "+cpath+"\n");
164 sb.append("mask = "+mask+"\n");
165 sb.append("actions = "+getActions()+"\n");
166 sb.append("directory = "+directory+"\n");
167 sb.append("recursive = "+recursive+"\n");
168 sb.append("***\n");
169 return sb.toString();
170 }
171 */
172
173 private static final long serialVersionUID = 7930732926638008763L;
174
175 /**
176 * initialize a FilePermission object. Common to all constructors.
177 * Also called during de-serialization.
178 *
179 * @param mask the actions mask to use.
180 *
181 */
182 private void init(int mask) {
183 if ((mask & ALL) != mask)
184 throw new IllegalArgumentException("invalid actions mask");
185
186 if (mask == NONE)
187 throw new IllegalArgumentException("invalid actions mask");
188
189 if ((cpath = getName()) == null)
190 throw new NullPointerException("name can't be null");
191
192 this.mask = mask;
193
194 if (cpath.equals("<<ALL FILES>>")) {
195 directory = true;
196 recursive = true;
197 cpath = "";
198 return;
199 }
200
201 // store only the canonical cpath if possible
202 cpath = AccessController.doPrivileged(new PrivilegedAction<>() {
203 public String run() {
204 try {
205 String path = cpath;
206 if (cpath.endsWith("*")) {
207 // call getCanonicalPath with a path with wildcard character
208 // replaced to avoid calling it with paths that are
209 // intended to match all entries in a directory
210 path = path.substring(0, path.length()-1) + "-";
211 path = new File(path).getCanonicalPath();
212 return path.substring(0, path.length()-1) + "*";
213 } else {
214 return new File(path).getCanonicalPath();
215 }
216 } catch (IOException ioe) {
217 return cpath;
218 }
219 }
220 });
221
222 int len = cpath.length();
223 char last = ((len > 0) ? cpath.charAt(len - 1) : 0);
224
225 if (last == RECURSIVE_CHAR &&
226 cpath.charAt(len - 2) == File.separatorChar) {
227 directory = true;
228 recursive = true;
229 cpath = cpath.substring(0, --len);
230 } else if (last == WILD_CHAR &&
231 cpath.charAt(len - 2) == File.separatorChar) {
232 directory = true;
233 //recursive = false;
234 cpath = cpath.substring(0, --len);
235 } else {
236 // overkill since they are initialized to false, but
237 // commented out here to remind us...
238 //directory = false;
239 //recursive = false;
240 }
241
242 // XXX: at this point the path should be absolute. die if it isn't?
243 }
244
245 /**
246 * Creates a new FilePermission object with the specified actions.
247 * <i>path</i> is the pathname of a file or directory, and <i>actions</i>
248 * contains a comma-separated list of the desired actions granted on the
249 * file or directory. Possible actions are
250 * "read", "write", "execute", "delete", and "readlink".
251 *
252 * <p>A pathname that ends in "/*" (where "/" is
253 * the file separator character, <code>File.separatorChar</code>)
254 * indicates all the files and directories contained in that directory.
255 * A pathname that ends with "/-" indicates (recursively) all files and
256 * subdirectories contained in that directory. The special pathname
257 * "<<ALL FILES>>" matches any file.
258 *
259 * <p>A pathname consisting of a single "*" indicates all the files
260 * in the current directory, while a pathname consisting of a single "-"
261 * indicates all the files in the current directory and
262 * (recursively) all files and subdirectories contained in the current
263 * directory.
289 // package private for use by the FilePermissionCollection add method
290 FilePermission(String path, int mask) {
291 super(path);
292 init(mask);
293 }
294
295 /**
296 * Checks if this FilePermission object "implies" the specified permission.
297 * <P>
298 * More specifically, this method returns true if:
299 * <ul>
300 * <li> <i>p</i> is an instanceof FilePermission,
301 * <li> <i>p</i>'s actions are a proper subset of this
302 * object's actions, and
303 * <li> <i>p</i>'s pathname is implied by this object's
304 * pathname. For example, "/tmp/*" implies "/tmp/foo", since
305 * "/tmp/*" encompasses all files in the "/tmp" directory,
306 * including the one named "foo".
307 * </ul>
308 *
309 * @param p the permission to check against.
310 *
311 * @return <code>true</code> if the specified permission is not
312 * <code>null</code> and is implied by this object,
313 * <code>false</code> otherwise.
314 */
315 @Override
316 public boolean implies(Permission p) {
317 if (!(p instanceof FilePermission))
318 return false;
319
320 FilePermission that = (FilePermission) p;
321
322 // we get the effective mask. i.e., the "and" of this and that.
323 // They must be equal to that.mask for implies to return true.
324
325 return ((this.mask & that.mask) == that.mask) && impliesIgnoreMask(that);
326 }
327
328 /**
329 * Checks if the Permission's actions are a proper subset of the
330 * this object's actions. Returns the effective mask iff the
331 * this FilePermission's path also implies that FilePermission's path.
332 *
333 * @param that the FilePermission to check against.
334 * @return the effective mask
335 */
336 boolean impliesIgnoreMask(FilePermission that) {
337 if (this.directory) {
338 if (this.recursive) {
339 // make sure that.path is longer then path so
340 // something like /foo/- does not imply /foo
341 if (that.directory) {
342 return (that.cpath.length() >= this.cpath.length()) &&
343 that.cpath.startsWith(this.cpath);
344 } else {
345 return ((that.cpath.length() > this.cpath.length()) &&
346 that.cpath.startsWith(this.cpath));
347 }
348 } else {
349 if (that.directory) {
350 // if the permission passed in is a directory
351 // specification, make sure that a non-recursive
352 // permission (i.e., this object) can't imply a recursive
353 // permission.
354 if (that.recursive)
355 return false;
356 else
357 return (this.cpath.equals(that.cpath));
358 } else {
359 int last = that.cpath.lastIndexOf(File.separatorChar);
360 if (last == -1)
361 return false;
362 else {
363 // this.cpath.equals(that.cpath.substring(0, last+1));
364 // Use regionMatches to avoid creating new string
365 return (this.cpath.length() == (last + 1)) &&
366 this.cpath.regionMatches(0, that.cpath, 0, last+1);
367 }
368 }
369 }
370 } else if (that.directory) {
371 // if this is NOT recursive/wildcarded,
372 // do not let it imply a recursive/wildcarded permission
373 return false;
374 } else {
375 return (this.cpath.equals(that.cpath));
376 }
377 }
378
379 /**
380 * Checks two FilePermission objects for equality. Checks that <i>obj</i> is
381 * a FilePermission, and has the same pathname and actions as this object.
382 *
383 * @param obj the object we are testing for equality with this object.
384 * @return <code>true</code> if obj is a FilePermission, and has the same
385 * pathname and actions as this FilePermission object,
386 * <code>false</code> otherwise.
387 */
388 @Override
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 @Override
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;
|
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.net.URI;
29 import java.nio.file.*;
30 import java.security.*;
31 import java.util.Enumeration;
32 import java.util.Objects;
33 import java.util.StringJoiner;
34 import java.util.Vector;
35 import java.util.concurrent.ConcurrentHashMap;
36
37 import jdk.internal.misc.JavaIOFilePermissionAccess;
38 import jdk.internal.misc.SharedSecrets;
39 import sun.nio.fs.DefaultFileSystemProvider;
40 import sun.security.action.GetPropertyAction;
41 import sun.security.util.FilePermCompat;
42 import sun.security.util.SecurityConstants;
43
44 /**
45 * This class represents access to a file or directory. A FilePermission consists
46 * of a pathname and a set of actions valid for that pathname.
47 * <P>
48 * Pathname is the pathname of the file or directory granted the specified
49 * actions. A pathname that ends in "/*" (where "/" is
50 * the file separator character, <code>File.separatorChar</code>) indicates
51 * all the files and directories contained in that directory. A pathname
52 * that ends with "/-" indicates (recursively) all files
53 * and subdirectories contained in that directory. Such a pathname is called
54 * a wildcard pathname. Otherwise, it's a simple pathname.
55 * <P>
56 * A pathname consisting of the special token "<<ALL FILES>>"
57 * matches <b>any</b> file.
58 * <P>
59 * Note: A pathname consisting of a single "*" indicates all the files
60 * in the current directory, while a pathname consisting of a single "-"
61 * indicates all the files in the current directory and
62 * (recursively) all files and subdirectories contained in the current
63 * directory.
64 * <P>
65 * The actions to be granted are passed to the constructor in a string containing
66 * a list of one or more comma-separated keywords. The possible keywords are
67 * "read", "write", "execute", "delete", and "readlink". Their meaning is
68 * defined as follows:
69 *
70 * <DL>
71 * <DT> read <DD> read permission
72 * <DT> write <DD> write permission
73 * <DT> execute
74 * <DD> execute permission. Allows <code>Runtime.exec</code> to
75 * be called. Corresponds to <code>SecurityManager.checkExec</code>.
76 * <DT> delete
77 * <DD> delete permission. Allows <code>File.delete</code> to
78 * be called. Corresponds to <code>SecurityManager.checkDelete</code>.
79 * <DT> readlink
80 * <DD> read link permission. Allows the target of a
81 * <a href="../nio/file/package-summary.html#links">symbolic link</a>
82 * to be read by invoking the {@link java.nio.file.Files#readSymbolicLink
83 * readSymbolicLink } method.
84 * </DL>
85 * <P>
86 * The actions string is converted to lowercase before processing.
87 * <P>
88 * Be careful when granting FilePermissions. Think about the implications
89 * of granting read and especially write access to various files and
90 * directories. The "<<ALL FILES>>" permission with write action is
91 * especially dangerous. This grants permission to write to the entire
92 * file system. One thing this effectively allows is replacement of the
93 * system binary, including the JVM runtime environment.
94 * <P>
95 * Please note: Code can always read a file from the same
96 * directory it's in (or a subdirectory of that directory); it does not
97 * need explicit permission to do so.
98 *
99 * @implNote In this implementation, the relation between two FilePermission
100 * objects is calculated without accessing the underlying file system.
101 * This means the pathname is not canonicalized, i.e. a relative path is not
102 * converted to an absolute path, a Windows DOS-style 8.3 name is not expanded
103 * to a long name, and a symbolic link is not resolved to its target, etc.
104 * In order for policy files to work as expected, the pathname of the
105 * FilePermission granted should match how an application accesses a file.
106 * <P>
107 * Except for the special name {@code <<ALL FILES>>}, when a FilePermission
108 * object is created, its pathname information is divided into two values.
109 * The first is the path itself converted to a {@link Path} object and
110 * normalized. The second is a wildcard flag indicating whether
111 * the pathname contains a trailing "-", "*", or none.
112 *
113 * @see java.security.Permission
114 * @see java.security.Permissions
115 * @see java.security.PermissionCollection
116 *
117 *
118 * @author Marianne Mueller
119 * @author Roland Schemers
120 * @since 1.2
121 *
122 * @serial exclude
123 */
124
125 public final class FilePermission extends Permission implements Serializable {
126
127 /**
128 * Execute action.
129 */
130 private static final int EXECUTE = 0x1;
131 /**
132 * Write action.
154 */
155 private static final int NONE = 0x0;
156
157 // the actions mask
158 private transient int mask;
159
160 // does path indicate a directory? (wildcard or recursive)
161 private transient boolean directory;
162
163 // is it a recursive directory specification?
164 private transient boolean recursive;
165
166 /**
167 * the actions string.
168 *
169 * @serial
170 */
171 private String actions; // Left null as long as possible, then
172 // created and re-used in the getAction function.
173
174 // canonicalized dir path. used by the "old" behavior (nb == false).
175 // In the case of directories, it is the name "/blah/*" or "/blah/-"
176 // without the last character (the "*" or "-").
177
178 private transient String cpath;
179
180 // Following fields used by the "new" behavior (nb == true), in which
181 // input path is not canonicalized. For compatibility (so that granting
182 // FilePermission on "x" allows reading "`pwd`/x", an alternative path
183 // can be added so that both can be used in an implies() check. Please note
184 // the alternative path only deals with absolute/relative path, and does
185 // not deal with symlink/target.
186
187 private transient Path npath; // normalized dir path.
188 private transient Path npath2; // alternative normalized dir path.
189 private transient boolean allFiles; // whether this is <<ALL FILES>>
190
191 // static Strings used by init(int mask)
192 private static final char RECURSIVE_CHAR = '-';
193 private static final char WILD_CHAR = '*';
194
195 // public String toString() {
196 // StringBuffer sb = new StringBuffer();
197 // sb.append("*** FilePermission on " + getName() + " ***");
198 // for (Field f : FilePermission.class.getDeclaredFields()) {
199 // if (!Modifier.isStatic(f.getModifiers())) {
200 // try {
201 // sb.append(f.getName() + " = " + f.get(this));
202 // } catch (Exception e) {
203 // sb.append(f.getName() + " = " + e.toString());
204 // }
205 // sb.append('\n');
206 // }
207 // }
208 // sb.append("***\n");
209 // return sb.toString();
210 // }
211
212 private static final long serialVersionUID = 7930732926638008763L;
213
214 /**
215 * Always use the internal default file system, in case it was modified
216 * with java.nio.file.spi.DefaultFileSystemProvider.
217 */
218 private static final java.nio.file.FileSystem builtInFS =
219 DefaultFileSystemProvider.create()
220 .getFileSystem(URI.create("file:///"));
221
222 /**
223 * Creates FilePermission objects with special internals.
224 * See {@link FilePermCompat#newPermPlusAltPath(Permission)} and
225 * {@link FilePermCompat#newPermUsingAltPath(Permission)}.
226 */
227
228 private static final Path here = builtInFS.getPath(
229 GetPropertyAction.privilegedGetProperty("user.dir"));
230
231 /**
232 * A private constructor like a clone, only npath2 is not touched.
233 * @param input
234 */
235 private FilePermission(FilePermission input) {
236 super(input.getName());
237 this.npath = input.npath;
238 this.actions = input.actions;
239 this.allFiles = input.allFiles;
240 this.recursive = input.recursive;
241 this.directory = input.directory;
242 this.cpath = input.cpath;
243 this.mask = input.mask;
244 }
245
246 /**
247 * Returns the alternative path as a Path object, i.e. absolute path
248 * for a relative one, or vice versa.
249 *
250 * @param in a real path w/o "-" or "*" at the end, and not <<ALL FILES>>.
251 * @return the alternative path, or null if cannot find one.
252 */
253 private static Path altPath(Path in) {
254 try {
255 if (!in.isAbsolute()) {
256 return here.resolve(in).normalize();
257 } else {
258 return here.relativize(in).normalize();
259 }
260 } catch (IllegalArgumentException e) {
261 return null;
262 }
263 }
264
265 static {
266 SharedSecrets.setJavaIOFilePermissionAccess(
267 new JavaIOFilePermissionAccess() {
268 public FilePermission newPermPlusAltPath(FilePermission input) {
269 if (input.npath2 == null && !input.allFiles) {
270 Path npath2 = altPath(input.npath);
271 if (npath2 != null) {
272 FilePermission np = new FilePermission(input);
273 np.npath2 = npath2;
274 return np;
275 }
276 }
277 return input;
278 }
279 public FilePermission newPermUsingAltPath(FilePermission input) {
280 if (!input.allFiles) {
281 Path npath2 = altPath(input.npath);
282 if (npath2 != null) {
283 FilePermission np = new FilePermission(input);
284 np.npath = npath2;
285 return np;
286 }
287 }
288 return null;
289 }
290 }
291 );
292 }
293
294 /**
295 * initialize a FilePermission object. Common to all constructors.
296 * Also called during de-serialization.
297 *
298 * @param mask the actions mask to use.
299 *
300 */
301 private void init(int mask) {
302 if ((mask & ALL) != mask)
303 throw new IllegalArgumentException("invalid actions mask");
304
305 if (mask == NONE)
306 throw new IllegalArgumentException("invalid actions mask");
307
308 if (FilePermCompat.nb) {
309 String name = getName();
310
311 if (name == null)
312 throw new NullPointerException("name can't be null");
313
314 this.mask = mask;
315
316 if (name.equals("<<ALL FILES>>")) {
317 allFiles = true;
318 npath = builtInFS.getPath("");
319 // other fields remain default
320 return;
321 }
322
323 boolean rememberStar = false;
324 if (name.endsWith("*")) {
325 rememberStar = true;
326 recursive = false;
327 name = name.substring(0, name.length()-1) + "-";
328 }
329
330 try {
331 // new File() can "normalize" some name, for example, "/C:/X" on
332 // Windows. Some JDK codes generate such illegal names.
333 npath = builtInFS.getPath(new File(name).getPath())
334 .normalize();
335 } catch (InvalidPathException ipe) {
336 // Still invalid. For compatibility reason, accept it
337 // but make this permission useless.
338 npath = builtInFS.getPath("-u-s-e-l-e-s-s-");
339 this.mask = NONE;
340 }
341
342 // lastName should always be non-null now
343 Path lastName = npath.getFileName();
344 if (lastName != null && lastName.toString().equals("-")) {
345 directory = true;
346 recursive = !rememberStar;
347 npath = npath.getParent();
348 }
349 if (npath == null) {
350 npath = builtInFS.getPath("");
351 }
352 } else {
353 if ((cpath = getName()) == null)
354 throw new NullPointerException("name can't be null");
355
356 this.mask = mask;
357
358 if (cpath.equals("<<ALL FILES>>")) {
359 directory = true;
360 recursive = true;
361 cpath = "";
362 return;
363 }
364
365 // store only the canonical cpath if possible
366 cpath = AccessController.doPrivileged(new PrivilegedAction<>() {
367 public String run() {
368 try {
369 String path = cpath;
370 if (cpath.endsWith("*")) {
371 // call getCanonicalPath with a path with wildcard character
372 // replaced to avoid calling it with paths that are
373 // intended to match all entries in a directory
374 path = path.substring(0, path.length() - 1) + "-";
375 path = new File(path).getCanonicalPath();
376 return path.substring(0, path.length() - 1) + "*";
377 } else {
378 return new File(path).getCanonicalPath();
379 }
380 } catch (IOException ioe) {
381 return cpath;
382 }
383 }
384 });
385
386 int len = cpath.length();
387 char last = ((len > 0) ? cpath.charAt(len - 1) : 0);
388
389 if (last == RECURSIVE_CHAR &&
390 cpath.charAt(len - 2) == File.separatorChar) {
391 directory = true;
392 recursive = true;
393 cpath = cpath.substring(0, --len);
394 } else if (last == WILD_CHAR &&
395 cpath.charAt(len - 2) == File.separatorChar) {
396 directory = true;
397 //recursive = false;
398 cpath = cpath.substring(0, --len);
399 } else {
400 // overkill since they are initialized to false, but
401 // commented out here to remind us...
402 //directory = false;
403 //recursive = false;
404 }
405
406 // XXX: at this point the path should be absolute. die if it isn't?
407 }
408 }
409
410 /**
411 * Creates a new FilePermission object with the specified actions.
412 * <i>path</i> is the pathname of a file or directory, and <i>actions</i>
413 * contains a comma-separated list of the desired actions granted on the
414 * file or directory. Possible actions are
415 * "read", "write", "execute", "delete", and "readlink".
416 *
417 * <p>A pathname that ends in "/*" (where "/" is
418 * the file separator character, <code>File.separatorChar</code>)
419 * indicates all the files and directories contained in that directory.
420 * A pathname that ends with "/-" indicates (recursively) all files and
421 * subdirectories contained in that directory. The special pathname
422 * "<<ALL FILES>>" matches any file.
423 *
424 * <p>A pathname consisting of a single "*" indicates all the files
425 * in the current directory, while a pathname consisting of a single "-"
426 * indicates all the files in the current directory and
427 * (recursively) all files and subdirectories contained in the current
428 * directory.
454 // package private for use by the FilePermissionCollection add method
455 FilePermission(String path, int mask) {
456 super(path);
457 init(mask);
458 }
459
460 /**
461 * Checks if this FilePermission object "implies" the specified permission.
462 * <P>
463 * More specifically, this method returns true if:
464 * <ul>
465 * <li> <i>p</i> is an instanceof FilePermission,
466 * <li> <i>p</i>'s actions are a proper subset of this
467 * object's actions, and
468 * <li> <i>p</i>'s pathname is implied by this object's
469 * pathname. For example, "/tmp/*" implies "/tmp/foo", since
470 * "/tmp/*" encompasses all files in the "/tmp" directory,
471 * including the one named "foo".
472 * </ul>
473 *
474 * @implNote A simple pathname implies another simple pathname
475 * if and only if their normalized Path objects are equal. A simple
476 * pathname never implies a wildcard pathname. A wildcard pathname
477 * implies another wildcard pathname if and only if all simple pathnames
478 * implied by the latter are implied by the former. A wildcard pathname
479 * implies a simple pathname if and only if
480 * <ul>
481 * <li>if the wildcard flag is "*", the simple pathname's path
482 * must be right in the wildcard pathname's path, i.e.
483 * normalized_simple_path.relativize(normalized_wildcard_path)
484 * is exactly "..".
485 * <li>if the wildcard flag is "-", the simple pathname's path
486 * must be recursively inside the wildcard pathname's path, i.e.
487 * normalized_simple_path.relativize(normalized_wildcard_path)
488 * must be a series of one or more "..".
489 * </ul>
490 * Note that this means "/" does not imply "foo".
491 * <P>
492 * {@code <<ALL FILES>>} implies every other pathname. No pathname,
493 * except for {@code <<ALL FILES>>} itself, implies {@code <<ALL FILES>>}.
494 *
495 * @param p the permission to check against.
496 *
497 * @return <code>true</code> if the specified permission is not
498 * <code>null</code> and is implied by this object,
499 * <code>false</code> otherwise.
500 */
501 @Override
502 public boolean implies(Permission p) {
503 if (!(p instanceof FilePermission))
504 return false;
505
506 FilePermission that = (FilePermission) p;
507
508 // we get the effective mask. i.e., the "and" of this and that.
509 // They must be equal to that.mask for implies to return true.
510
511 return ((this.mask & that.mask) == that.mask) && impliesIgnoreMask(that);
512 }
513
514 /**
515 * Checks if the Permission's actions are a proper subset of the
516 * this object's actions. Returns the effective mask iff the
517 * this FilePermission's path also implies that FilePermission's path.
518 *
519 * @param that the FilePermission to check against.
520 * @return the effective mask
521 */
522 boolean impliesIgnoreMask(FilePermission that) {
523 if (FilePermCompat.nb) {
524 if (allFiles) {
525 return true;
526 }
527 if (that.allFiles) {
528 return false;
529 }
530 // Left at least same level of wildness as right
531 if ((this.recursive && that.recursive) != that.recursive
532 || (this.directory && that.directory) != that.directory) {
533 return false;
534 }
535 // Same npath is good as long as both or neither are directories
536 if (this.npath.equals(that.npath)
537 && this.directory == that.directory) {
538 return true;
539 }
540 int diff = containsPath(this.npath, that.npath);
541 // Right inside left is good if recursive
542 if (diff >= 1 && recursive) {
543 return true;
544 }
545 // Right right inside left if it is element in set
546 if (diff == 1 && directory && !that.directory) {
547 return true;
548 }
549
550 // Hack: if a npath2 field exists, apply the same checks
551 // on it as a fallback.
552 if (this.npath2 != null) {
553 if (this.npath2.equals(that.npath)
554 && this.directory == that.directory) {
555 return true;
556 }
557 diff = containsPath(this.npath2, that.npath);
558 if (diff >= 1 && recursive) {
559 return true;
560 }
561 if (diff == 1 && directory && !that.directory) {
562 return true;
563 }
564 }
565
566 return false;
567 } else {
568 if (this.directory) {
569 if (this.recursive) {
570 // make sure that.path is longer then path so
571 // something like /foo/- does not imply /foo
572 if (that.directory) {
573 return (that.cpath.length() >= this.cpath.length()) &&
574 that.cpath.startsWith(this.cpath);
575 } else {
576 return ((that.cpath.length() > this.cpath.length()) &&
577 that.cpath.startsWith(this.cpath));
578 }
579 } else {
580 if (that.directory) {
581 // if the permission passed in is a directory
582 // specification, make sure that a non-recursive
583 // permission (i.e., this object) can't imply a recursive
584 // permission.
585 if (that.recursive)
586 return false;
587 else
588 return (this.cpath.equals(that.cpath));
589 } else {
590 int last = that.cpath.lastIndexOf(File.separatorChar);
591 if (last == -1)
592 return false;
593 else {
594 // this.cpath.equals(that.cpath.substring(0, last+1));
595 // Use regionMatches to avoid creating new string
596 return (this.cpath.length() == (last + 1)) &&
597 this.cpath.regionMatches(0, that.cpath, 0, last + 1);
598 }
599 }
600 }
601 } else if (that.directory) {
602 // if this is NOT recursive/wildcarded,
603 // do not let it imply a recursive/wildcarded permission
604 return false;
605 } else {
606 return (this.cpath.equals(that.cpath));
607 }
608 }
609 }
610
611 /**
612 * Returns the depth between an outer path p1 and an inner path p2. -1
613 * is returned if
614 *
615 * - p1 does not contains p2.
616 * - this is not decidable. For example, p1="../x", p2="y".
617 * - the depth is not decidable. For example, p1="/", p2="x".
618 *
619 * This method can return 2 if the depth is greater than 2.
620 *
621 * @param p1 the expected outer path, normalized
622 * @param p2 the expected inner path, normalized
623 * @return the depth in between
624 */
625 private static int containsPath(Path p1, Path p2) {
626 Path p;
627 try {
628 p = p2.relativize(p1).normalize();
629 if (p.getName(0).toString().isEmpty()) {
630 return 0;
631 } else {
632 for (Path item: p) {
633 String s = item.toString();
634 // Workaround for Windows bug JDK-8140449
635 if (!s.equals("..") && !s.equals("..\\")) {
636 return -1;
637 }
638 }
639 return p.getNameCount();
640 }
641 } catch (IllegalArgumentException iae) {
642 return -1;
643 }
644 }
645
646 /**
647 * Checks two FilePermission objects for equality. Checks that <i>obj</i> is
648 * a FilePermission, and has the same pathname and actions as this object.
649 *
650 * @implNote More specifically, two pathnames are the same if and only if
651 * the pathnames have the same wildcard flag and the normalized Path forms
652 * are equal. Or they are both {@code <<ALL FILES>>}.
653 *
654 * @param obj the object we are testing for equality with this object.
655 * @return <code>true</code> if obj is a FilePermission, and has the same
656 * pathname and actions as this FilePermission object,
657 * <code>false</code> otherwise.
658 */
659 @Override
660 public boolean equals(Object obj) {
661 if (obj == this)
662 return true;
663
664 if (! (obj instanceof FilePermission))
665 return false;
666
667 FilePermission that = (FilePermission) obj;
668
669 if (FilePermCompat.nb) {
670 return (this.mask == that.mask) &&
671 (this.allFiles == that.allFiles) &&
672 this.npath.equals(that.npath) &&
673 (this.directory == that.directory) &&
674 (this.recursive == that.recursive);
675 } else {
676 return (this.mask == that.mask) &&
677 this.cpath.equals(that.cpath) &&
678 (this.directory == that.directory) &&
679 (this.recursive == that.recursive);
680 }
681 }
682
683 /**
684 * Returns the hash code value for this object.
685 *
686 * @return a hash code value for this object.
687 */
688 @Override
689 public int hashCode() {
690 if (FilePermCompat.nb) {
691 return Objects.hash(mask, allFiles, directory, recursive, npath);
692 } else {
693 return 0;
694 }
695 }
696
697 /**
698 * Converts an actions String to an actions mask.
699 *
700 * @param actions the action string.
701 * @return the actions mask.
702 */
703 private static int getMask(String actions) {
704 int mask = NONE;
705
706 // Null action valid?
707 if (actions == null) {
708 return mask;
709 }
710
711 // Use object identity comparison against known-interned strings for
712 // performance benefit (these values are used heavily within the JDK).
713 if (actions == SecurityConstants.FILE_READ_ACTION) {
714 return READ;
|